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 {
|
type InstanceKey interface {
|
||||||
instanceKeySigil()
|
instanceKeySigil()
|
||||||
String() string
|
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,
|
// 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))
|
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
|
// StringKey is the InstanceKey representation representing string indices, as
|
||||||
// used when the "for_each" argument is specified with a map or object type.
|
// used when the "for_each" argument is specified with a map or object type.
|
||||||
type StringKey string
|
type StringKey string
|
||||||
|
@ -69,6 +77,10 @@ func (k StringKey) String() string {
|
||||||
return fmt.Sprintf("[%q]", string(k))
|
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
|
// InstanceKeyLess returns true if the first given instance key i should sort
|
||||||
// before the second key j, and false otherwise.
|
// before the second key j, and false otherwise.
|
||||||
func InstanceKeyLess(i, j InstanceKey) bool {
|
func InstanceKeyLess(i, j InstanceKey) bool {
|
||||||
|
|
|
@ -5266,28 +5266,23 @@ func TestContext2Apply_provisionerDestroy(t *testing.T) {
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
||||||
val, ok := c.Config["command"]
|
val, ok := c.Config["command"]
|
||||||
if !ok || val != "destroy" {
|
if !ok || val != "destroy a" {
|
||||||
t.Fatalf("bad value for foo: %v %#v", val, c)
|
t.Fatalf("bad value for foo: %v %#v", val, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
state := MustShimLegacyState(&State{
|
state := states.NewState()
|
||||||
Modules: []*ModuleState{
|
root := state.RootModule()
|
||||||
&ModuleState{
|
root.SetResourceInstanceCurrent(
|
||||||
Path: rootModulePath,
|
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||||
Resources: map[string]*ResourceState{
|
&states.ResourceInstanceObjectSrc{
|
||||||
"aws_instance.foo": &ResourceState{
|
Status: states.ObjectReady,
|
||||||
Type: "aws_instance",
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||||
|
)
|
||||||
|
|
||||||
ctx := testContext2(t, &ContextOpts{
|
ctx := testContext2(t, &ContextOpts{
|
||||||
Config: m,
|
Config: m,
|
||||||
|
@ -5331,21 +5326,16 @@ func TestContext2Apply_provisionerDestroyFail(t *testing.T) {
|
||||||
return fmt.Errorf("provisioner error")
|
return fmt.Errorf("provisioner error")
|
||||||
}
|
}
|
||||||
|
|
||||||
state := MustShimLegacyState(&State{
|
state := states.NewState()
|
||||||
Modules: []*ModuleState{
|
root := state.RootModule()
|
||||||
&ModuleState{
|
root.SetResourceInstanceCurrent(
|
||||||
Path: rootModulePath,
|
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||||
Resources: map[string]*ResourceState{
|
&states.ResourceInstanceObjectSrc{
|
||||||
"aws_instance.foo": &ResourceState{
|
Status: states.ObjectReady,
|
||||||
Type: "aws_instance",
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||||
|
)
|
||||||
|
|
||||||
ctx := testContext2(t, &ContextOpts{
|
ctx := testContext2(t, &ContextOpts{
|
||||||
Config: m,
|
Config: m,
|
||||||
|
@ -5371,7 +5361,7 @@ func TestContext2Apply_provisionerDestroyFail(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkStateString(t, state, `
|
checkStateString(t, state, `
|
||||||
aws_instance.foo:
|
aws_instance.foo["a"]:
|
||||||
ID = bar
|
ID = bar
|
||||||
provider = provider["registry.terraform.io/-/aws"]
|
provider = provider["registry.terraform.io/-/aws"]
|
||||||
`)
|
`)
|
||||||
|
@ -5405,21 +5395,16 @@ func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) {
|
||||||
return fmt.Errorf("provisioner error")
|
return fmt.Errorf("provisioner error")
|
||||||
}
|
}
|
||||||
|
|
||||||
state := MustShimLegacyState(&State{
|
state := states.NewState()
|
||||||
Modules: []*ModuleState{
|
root := state.RootModule()
|
||||||
&ModuleState{
|
root.SetResourceInstanceCurrent(
|
||||||
Path: rootModulePath,
|
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||||
Resources: map[string]*ResourceState{
|
&states.ResourceInstanceObjectSrc{
|
||||||
"aws_instance.foo": &ResourceState{
|
Status: states.ObjectReady,
|
||||||
Type: "aws_instance",
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||||
|
)
|
||||||
|
|
||||||
ctx := testContext2(t, &ContextOpts{
|
ctx := testContext2(t, &ContextOpts{
|
||||||
Config: m,
|
Config: m,
|
||||||
|
@ -5547,7 +5532,7 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
||||||
|
|
||||||
destroyCalled := false
|
destroyCalled := false
|
||||||
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
pr.ApplyFn = func(rs *InstanceState, c *ResourceConfig) error {
|
||||||
expected := "create"
|
expected := "create a b"
|
||||||
if rs.ID == "bar" {
|
if rs.ID == "bar" {
|
||||||
destroyCalled = true
|
destroyCalled = true
|
||||||
return nil
|
return nil
|
||||||
|
@ -5561,22 +5546,16 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
state := MustShimLegacyState(&State{
|
state := states.NewState()
|
||||||
Modules: []*ModuleState{
|
root := state.RootModule()
|
||||||
&ModuleState{
|
root.SetResourceInstanceCurrent(
|
||||||
Path: rootModulePath,
|
mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
|
||||||
Resources: map[string]*ResourceState{
|
&states.ResourceInstanceObjectSrc{
|
||||||
"aws_instance.foo": &ResourceState{
|
Status: states.ObjectTainted,
|
||||||
Type: "aws_instance",
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "bar",
|
|
||||||
Tainted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
|
||||||
|
)
|
||||||
|
|
||||||
ctx := testContext2(t, &ContextOpts{
|
ctx := testContext2(t, &ContextOpts{
|
||||||
Config: m,
|
Config: m,
|
||||||
|
@ -5589,6 +5568,14 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
||||||
Provisioners: map[string]ProvisionerFactory{
|
Provisioners: map[string]ProvisionerFactory{
|
||||||
"shell": testProvisionerFuncFixed(pr),
|
"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() {
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
||||||
|
@ -5601,7 +5588,7 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkStateString(t, state, `
|
checkStateString(t, state, `
|
||||||
aws_instance.foo:
|
aws_instance.foo["a"]:
|
||||||
ID = foo
|
ID = foo
|
||||||
provider = provider["registry.terraform.io/-/aws"]
|
provider = provider["registry.terraform.io/-/aws"]
|
||||||
foo = bar
|
foo = bar
|
||||||
|
|
|
@ -561,8 +561,18 @@ func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*configs.Provisio
|
||||||
provisioner := ctx.Provisioner(prov.Type)
|
provisioner := ctx.Provisioner(prov.Type)
|
||||||
schema := ctx.ProvisionerSchema(prov.Type)
|
schema := ctx.ProvisionerSchema(prov.Type)
|
||||||
|
|
||||||
forEach, forEachDiags := evaluateResourceForEachExpression(n.ResourceConfig.ForEach, ctx)
|
var forEach map[string]cty.Value
|
||||||
diags = diags.Append(forEachDiags)
|
|
||||||
|
// 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)
|
keyData := EvalDataForInstanceKey(instanceAddr.Key, forEach)
|
||||||
|
|
||||||
// Evaluate the main provisioner configuration.
|
// Evaluate the main provisioner configuration.
|
||||||
|
|
|
@ -104,25 +104,26 @@ type InstanceKeyEvalData = instances.RepetitionData
|
||||||
|
|
||||||
// EvalDataForInstanceKey constructs a suitable InstanceKeyEvalData for
|
// EvalDataForInstanceKey constructs a suitable InstanceKeyEvalData for
|
||||||
// evaluating in a context that has the given instance key.
|
// 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 {
|
func EvalDataForInstanceKey(key addrs.InstanceKey, forEachMap map[string]cty.Value) InstanceKeyEvalData {
|
||||||
var countIdx cty.Value
|
var evalData InstanceKeyEvalData
|
||||||
var eachKey cty.Value
|
if key == nil {
|
||||||
var eachVal cty.Value
|
return evalData
|
||||||
|
|
||||||
if intKey, ok := key.(addrs.IntKey); ok {
|
|
||||||
countIdx = cty.NumberIntVal(int64(intKey))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if stringKey, ok := key.(addrs.StringKey); ok {
|
keyValue := key.Value()
|
||||||
eachKey = cty.StringVal(string(stringKey))
|
switch keyValue.Type() {
|
||||||
eachVal = forEachMap[string(stringKey)]
|
case cty.String:
|
||||||
}
|
evalData.EachKey = keyValue
|
||||||
|
evalData.EachValue = forEachMap[keyValue.AsString()]
|
||||||
return InstanceKeyEvalData{
|
case cty.Number:
|
||||||
CountIndex: countIdx,
|
evalData.CountIndex = keyValue
|
||||||
EachKey: eachKey,
|
|
||||||
EachValue: eachVal,
|
|
||||||
}
|
}
|
||||||
|
return evalData
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvalDataForNoInstanceKey is a value of InstanceKeyData that sets no instance
|
// EvalDataForNoInstanceKey is a value of InstanceKeyData that sets no instance
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
resource "aws_instance" "foo" {
|
resource "aws_instance" "foo" {
|
||||||
|
for_each = var.input
|
||||||
foo = "bar"
|
foo = "bar"
|
||||||
|
|
||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
command = "create"
|
command = "create ${each.key} ${each.value}"
|
||||||
}
|
}
|
||||||
|
|
||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
command = "destroy"
|
|
||||||
when = "destroy"
|
when = "destroy"
|
||||||
|
command = "destroy ${each.key}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "input" {
|
||||||
|
type = map(string)
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue