2021-01-21 11:43:50 -06:00
|
|
|
package terraform
|
2021-02-16 14:32:36 -06:00
|
|
|
|
|
|
|
import (
|
2021-03-08 14:33:07 -06:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2021-03-22 13:18:36 -05:00
|
|
|
"sync"
|
2021-02-16 14:32:36 -06:00
|
|
|
"testing"
|
2021-03-22 13:18:36 -05:00
|
|
|
"time"
|
2021-02-16 14:32:36 -06:00
|
|
|
|
2021-05-17 14:00:50 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
2021-05-17 14:17:09 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
2021-05-17 12:40:40 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/providers"
|
2021-04-05 17:22:30 -05:00
|
|
|
"github.com/hashicorp/terraform/plans"
|
2021-02-16 14:32:36 -06:00
|
|
|
"github.com/hashicorp/terraform/states"
|
2021-03-08 14:33:07 -06:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
2021-02-16 14:32:36 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
2021-03-08 14:33:07 -06:00
|
|
|
|
|
|
|
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,
|
2021-04-05 17:22:30 -05:00
|
|
|
PlanMode: plans.DestroyMode,
|
2021-03-08 14:33:07 -06:00
|
|
|
})
|
|
|
|
|
|
|
|
_, 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())
|
|
|
|
}
|
|
|
|
}
|
2021-03-22 13:18:36 -05:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2021-03-26 08:40:33 -05:00
|
|
|
|
|
|
|
// 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())
|
|
|
|
}
|
|
|
|
|
2021-05-04 17:59:58 -05:00
|
|
|
bar := plan.PriorState.ResourceInstance(barAddr)
|
2021-03-26 08:40:33 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2021-05-04 17:59:58 -05:00
|
|
|
foo := plan.PriorState.ResourceInstance(fooAddr)
|
2021-03-26 08:40:33 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2021-05-04 17:59:58 -05:00
|
|
|
bin := plan.PriorState.ResourceInstance(binAddr)
|
2021-03-26 08:40:33 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2021-05-04 17:59:58 -05:00
|
|
|
baz := plan.PriorState.ResourceInstance(bazAddr)
|
2021-03-26 08:40:33 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2021-04-15 06:30:50 -05:00
|
|
|
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
}
|
2021-05-11 16:55:27 -05:00
|
|
|
|
|
|
|
func TestContext2Apply_sensitiveOutputPassthrough(t *testing.T) {
|
|
|
|
// Ensure we're not trying to double-mark values decoded from state
|
|
|
|
m := testModuleInline(t, map[string]string{
|
|
|
|
"main.tf": `
|
|
|
|
module "mod" {
|
|
|
|
source = "./mod"
|
|
|
|
}
|
|
|
|
|
|
|
|
resource "test_object" "a" {
|
|
|
|
test_string = module.mod.out
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
|
|
|
|
"mod/main.tf": `
|
|
|
|
variable "in" {
|
|
|
|
sensitive = true
|
|
|
|
default = "foo"
|
|
|
|
}
|
|
|
|
output "out" {
|
|
|
|
value = var.in
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
})
|
|
|
|
|
|
|
|
p := simpleMockProvider()
|
|
|
|
|
|
|
|
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.ErrWithWarnings())
|
|
|
|
}
|
|
|
|
|
|
|
|
state, diags := ctx.Apply()
|
|
|
|
if diags.HasErrors() {
|
|
|
|
t.Fatal(diags.ErrWithWarnings())
|
|
|
|
}
|
|
|
|
|
|
|
|
obj := state.ResourceInstance(mustResourceInstanceAddr("test_object.a"))
|
|
|
|
if len(obj.Current.AttrSensitivePaths) != 1 {
|
|
|
|
t.Fatalf("Expected 1 sensitive mark for test_object.a, got %#v\n", obj.Current.AttrSensitivePaths)
|
|
|
|
}
|
|
|
|
|
|
|
|
plan, diags := ctx.Plan()
|
|
|
|
if diags.HasErrors() {
|
|
|
|
t.Fatal(diags.ErrWithWarnings())
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure the same marks are compared in the next plan as well
|
|
|
|
for _, c := range plan.Changes.Resources {
|
|
|
|
if c.Action != plans.NoOp {
|
|
|
|
t.Errorf("Unexpcetd %s change for %s", c.Action, c.Addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|