252 lines
6.1 KiB
Go
252 lines
6.1 KiB
Go
package terraform
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"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) {
|
|
// The resource was added to state but actually failed to create and was
|
|
// left tainted. This should be removed during plan and result in a Create
|
|
// action.
|
|
m := testModuleInline(t, map[string]string{
|
|
"main.tf": `
|
|
resource "test_object" "a" {
|
|
}
|
|
`,
|
|
})
|
|
|
|
p := simpleMockProvider()
|
|
p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
|
|
resp.NewState = cty.NullVal(req.PriorState.Type())
|
|
return resp
|
|
}
|
|
|
|
addr := mustResourceInstanceAddr("test_object.a")
|
|
state := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"test_string":"foo"}`),
|
|
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())
|
|
}
|
|
|
|
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())
|
|
}
|
|
}
|