Merge pull request #24163 from hashicorp/jbardin/destroy-provisioner-keys
Destroy provisioner each.key
This commit is contained in:
commit
bf65b516c0
|
@ -18,6 +18,10 @@ import (
|
|||
type InstanceKey interface {
|
||||
instanceKeySigil()
|
||||
String() string
|
||||
|
||||
// Value returns the cty.Value of the appropriate type for the InstanceKey
|
||||
// value.
|
||||
Value() cty.Value
|
||||
}
|
||||
|
||||
// ParseInstanceKey returns the instance key corresponding to the given value,
|
||||
|
@ -56,6 +60,10 @@ func (k IntKey) String() string {
|
|||
return fmt.Sprintf("[%d]", int(k))
|
||||
}
|
||||
|
||||
func (k IntKey) Value() cty.Value {
|
||||
return cty.NumberIntVal(int64(k))
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -69,6 +77,10 @@ func (k StringKey) String() string {
|
|||
return fmt.Sprintf("[%q]", string(k))
|
||||
}
|
||||
|
||||
func (k StringKey) Value() cty.Value {
|
||||
return cty.StringVal(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 {
|
||||
|
|
|
@ -5266,28 +5266,23 @@ func TestContext2Apply_provisionerDestroy(t *testing.T) {
|
|||
p.DiffFn = testDiffFn
|
||||
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
||||
val, ok := c.Config["command"]
|
||||
if !ok || val != "destroy" {
|
||||
if !ok || val != "destroy a" {
|
||||
t.Fatalf("bad value for foo: %v %#v", val, c)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
state := states.NewState()
|
||||
root := state.RootModule()
|
||||
root.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
})
|
||||
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||
)
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
|
@ -5331,21 +5326,16 @@ func TestContext2Apply_provisionerDestroyFail(t *testing.T) {
|
|||
return fmt.Errorf("provisioner error")
|
||||
}
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
state := states.NewState()
|
||||
root := state.RootModule()
|
||||
root.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
})
|
||||
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||
)
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
|
@ -5371,7 +5361,7 @@ func TestContext2Apply_provisionerDestroyFail(t *testing.T) {
|
|||
}
|
||||
|
||||
checkStateString(t, state, `
|
||||
aws_instance.foo:
|
||||
aws_instance.foo["a"]:
|
||||
ID = bar
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
`)
|
||||
|
@ -5405,21 +5395,16 @@ func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) {
|
|||
return fmt.Errorf("provisioner error")
|
||||
}
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
state := states.NewState()
|
||||
root := state.RootModule()
|
||||
root.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
})
|
||||
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||
)
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
|
@ -5547,7 +5532,7 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
|||
|
||||
destroyCalled := false
|
||||
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
||||
expected := "create"
|
||||
expected := "create a b"
|
||||
if rs.ID == "bar" {
|
||||
destroyCalled = true
|
||||
return nil
|
||||
|
@ -5561,22 +5546,16 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
Tainted: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
state := states.NewState()
|
||||
root := state.RootModule()
|
||||
root.SetResourceInstanceCurrent(
|
||||
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectTainted,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
})
|
||||
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||
)
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
|
@ -5589,6 +5568,14 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
|||
Provisioners: map[string]ProvisionerFactory{
|
||||
"shell": testProvisionerFuncFixed(pr),
|
||||
},
|
||||
Variables: InputValues{
|
||||
"input": &InputValue{
|
||||
Value: cty.MapVal(map[string]cty.Value{
|
||||
"a": cty.StringVal("b"),
|
||||
}),
|
||||
SourceType: ValueFromInput,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if _, diags := ctx.Plan(); diags.HasErrors() {
|
||||
|
@ -5601,7 +5588,7 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
|||
}
|
||||
|
||||
checkStateString(t, state, `
|
||||
aws_instance.foo:
|
||||
aws_instance.foo["a"]:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/-/aws"]
|
||||
foo = bar
|
||||
|
|
|
@ -561,8 +561,18 @@ func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*configs.Provisio
|
|||
provisioner := ctx.Provisioner(prov.Type)
|
||||
schema := ctx.ProvisionerSchema(prov.Type)
|
||||
|
||||
forEach, forEachDiags := evaluateResourceForEachExpression(n.ResourceConfig.ForEach, ctx)
|
||||
diags = diags.Append(forEachDiags)
|
||||
var forEach map[string]cty.Value
|
||||
|
||||
// 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.
|
||||
if n.When != configs.ProvisionerWhenDestroy {
|
||||
m, forEachDiags := evaluateResourceForEachExpression(n.ResourceConfig.ForEach, ctx)
|
||||
diags = diags.Append(forEachDiags)
|
||||
forEach = m
|
||||
}
|
||||
|
||||
keyData := EvalDataForInstanceKey(instanceAddr.Key, forEach)
|
||||
|
||||
// Evaluate the main provisioner configuration.
|
||||
|
|
|
@ -104,25 +104,26 @@ type InstanceKeyEvalData = instances.RepetitionData
|
|||
|
||||
// EvalDataForInstanceKey constructs a suitable InstanceKeyEvalData for
|
||||
// evaluating in a context that has the given instance key.
|
||||
//
|
||||
// The forEachMap argument can be nil when preparing for evaluation
|
||||
// in a context where each.value is prohibited, such as a destroy-time
|
||||
// provisioner. In that case, the returned EachValue will always be
|
||||
// cty.NilVal.
|
||||
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))
|
||||
var evalData InstanceKeyEvalData
|
||||
if key == nil {
|
||||
return evalData
|
||||
}
|
||||
|
||||
if stringKey, ok := key.(addrs.StringKey); ok {
|
||||
eachKey = cty.StringVal(string(stringKey))
|
||||
eachVal = forEachMap[string(stringKey)]
|
||||
}
|
||||
|
||||
return InstanceKeyEvalData{
|
||||
CountIndex: countIdx,
|
||||
EachKey: eachKey,
|
||||
EachValue: eachVal,
|
||||
keyValue := key.Value()
|
||||
switch keyValue.Type() {
|
||||
case cty.String:
|
||||
evalData.EachKey = keyValue
|
||||
evalData.EachValue = forEachMap[keyValue.AsString()]
|
||||
case cty.Number:
|
||||
evalData.CountIndex = keyValue
|
||||
}
|
||||
return evalData
|
||||
}
|
||||
|
||||
// EvalDataForNoInstanceKey is a value of InstanceKeyData that sets no instance
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
resource "aws_instance" "foo" {
|
||||
for_each = var.input
|
||||
foo = "bar"
|
||||
|
||||
provisioner "shell" {
|
||||
command = "create"
|
||||
command = "create ${each.key} ${each.value}"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
command = "destroy"
|
||||
when = "destroy"
|
||||
command = "destroy ${each.key}"
|
||||
}
|
||||
}
|
||||
|
||||
variable "input" {
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue