Skip to content

Commit 8fe8415

Browse files
committed
Remove unnecessary import before destroy
Import was just a detour to create an empty resource state (type cty.Value) with just the ID set.
1 parent 294834a commit 8fe8415

File tree

6 files changed

+65
-43
lines changed

6 files changed

+65
-43
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/hashicorp/go-hclog v0.12.0
1212
github.com/hashicorp/go-plugin v1.0.1
1313
github.com/hashicorp/go-version v1.2.0
14-
github.com/hashicorp/terraform v0.12.21
14+
github.com/hashicorp/terraform v0.12.24
1515
github.com/mitchellh/cli v1.0.0
1616
github.com/onsi/gomega v1.9.0
1717
github.com/stretchr/testify v1.4.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,8 @@ github.com/hashicorp/memberlist v0.1.0 h1:qSsCiC0WYD39lbSitKNt40e30uorm2Ss/d4JGU
280280
github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=
281281
github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb h1:ZbgmOQt8DOg796figP87/EFCVx2v2h9yRvwHF/zceX4=
282282
github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
283-
github.com/hashicorp/terraform v0.12.21 h1:0qamALIu4/g2+4k4rC5DpxMdLz4A4K67pGQA2X4prgs=
284-
github.com/hashicorp/terraform v0.12.21/go.mod h1:eJcloDEx5ywM4a1tetIuVrlqklM0bUVRYJBYAh4CYzA=
283+
github.com/hashicorp/terraform v0.12.24 h1:lTTswsCcmTOhTwuUl2NdjtJBCNdGqZmRGQi0cjFHYOM=
284+
github.com/hashicorp/terraform v0.12.24/go.mod h1:eJcloDEx5ywM4a1tetIuVrlqklM0bUVRYJBYAh4CYzA=
285285
github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7 h1:Pc5TCv9mbxFN6UVX0LH6CpQrdTM5YjbVI2w15237Pjk=
286286
github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7/go.mod h1:p+ivJws3dpqbp1iP84+npOyAmTTOLMgCzrXd3GSdn/A=
287287
github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596 h1:hjyO2JsNZUKT1ym+FAdlBEkGPevazYsmVgIMw7dVELg=

pkg/provider/provider.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Package provider implements a client to call import, read, and destroy on any Terraform provider Plugin via GRPC.
1+
// Package provider implements a client to call destroy on any Terraform Provider Plugin via GRPC.
22
package provider
33

44
import (
@@ -31,6 +31,7 @@ const requestError = "RequestError"
3131
// provider is the interface that every Terraform Provider Plugin implements.
3232
type provider interface {
3333
Configure(providers.ConfigureRequest) providers.ConfigureResponse
34+
GetSchema() providers.GetSchemaResponse
3435
ReadResource(providers.ReadResourceRequest) providers.ReadResourceResponse
3536
ApplyResourceChange(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse
3637
ImportResourceState(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse
@@ -108,18 +109,23 @@ func (p TerraformProvider) Configure(config cty.Value) error {
108109
return respConf.Diagnostics.Err()
109110
}
110111

112+
// GetSchema returns the schema for all resource types and the provider itself.
113+
func (p TerraformProvider) GetSchema() providers.GetSchemaResponse {
114+
return p.provider.GetSchema()
115+
}
116+
111117
// ImportResource imports a Terraform resource by type and ID.
112118
// Terraform Type and ID is the minimal information needed to uniquely identify a resource.
113119
// For example, call:
114120
// ImportResource("aws_instance", "i-1234567890abcdef0")
115121
// The result is a resource which has only its ID set (all other attributes are empty).
116-
func (p TerraformProvider) ImportResource(terraformType string, resID string) ([]providers.ImportedResource, error) {
122+
func (p TerraformProvider) ImportResource(terraformType string, id string) ([]providers.ImportedResource, error) {
117123
var response providers.ImportResourceStateResponse
118124

119125
err := resource.Retry(30*time.Second, func() *resource.RetryError {
120126
response = p.ImportResourceState(providers.ImportResourceStateRequest{
121127
TypeName: terraformType,
122-
ID: resID,
128+
ID: id,
123129
})
124130

125131
if response.Diagnostics.HasErrors() {
@@ -144,16 +150,15 @@ func (p TerraformProvider) ImportResource(terraformType string, resID string) ([
144150
return response.ImportedResources, nil
145151
}
146152

147-
// ReadResource refreshes and sets all attributes of an imported resource.
148-
// This function is used to populate all attributes of a resource after import.
149-
func (p TerraformProvider) ReadResource(r providers.ImportedResource) (cty.Value, error) {
153+
// ReadResource refreshes all attributes of a resources.
154+
// For example, this function can be used to populate all attributes of a resource after import.
155+
func (p TerraformProvider) ReadResource(terraformType string, rState cty.Value) (cty.Value, error) {
150156
var response providers.ReadResourceResponse
151157

152158
err := resource.Retry(30*time.Second, func() *resource.RetryError {
153159
response = p.provider.ReadResource(providers.ReadResourceRequest{
154-
TypeName: r.TypeName,
155-
PriorState: r.State,
156-
Private: r.Private,
160+
TypeName: terraformType,
161+
PriorState: rState,
157162
})
158163

159164
if response.Diagnostics.HasErrors() {
@@ -179,7 +184,7 @@ func (p TerraformProvider) ReadResource(r providers.ImportedResource) (cty.Value
179184
}
180185

181186
// DestroyResource destroys a resource.
182-
// This function requires the current state of a resource as input (fetched via ReadResource).
187+
// This function requires the current state of a resource as input.
183188
func (p TerraformProvider) DestroyResource(terraformType string, currentState cty.Value) error {
184189
var response providers.ApplyResourceChangeResponse
185190

pkg/provider/provider_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
"github.com/gruntwork-io/terratest/modules/aws"
1616
"github.com/gruntwork-io/terratest/modules/terraform"
17-
"github.com/hashicorp/terraform/providers"
1817
"github.com/stretchr/testify/assert"
1918
"github.com/stretchr/testify/require"
2019
"github.com/zclconf/go-cty/cty"
@@ -205,9 +204,8 @@ func TestTerraformProvider_ReadResource(t *testing.T) {
205204
p, err := provider.Init("aws", 15)
206205
require.NoError(t, err)
207206

208-
currentResourceState, err := p.ReadResource(providers.ImportedResource{
209-
TypeName: "aws_vpc",
210-
State: cty.ObjectVal(map[string]cty.Value{
207+
currentResourceState, err := p.ReadResource("aws_vpc",
208+
cty.ObjectVal(map[string]cty.Value{
211209
"arn": cty.NullVal(cty.String),
212210
"assign_generated_ipv6_cidr_block": cty.False,
213211
"cidr_block": cty.NullVal(cty.String),
@@ -226,8 +224,8 @@ func TestTerraformProvider_ReadResource(t *testing.T) {
226224
"main_route_table_id": cty.NullVal(cty.String),
227225
"owner_id": cty.NullVal(cty.String),
228226
"tags": cty.NullVal(cty.Map(cty.String)),
229-
}),
230-
})
227+
}))
228+
231229
require.NoError(t, err)
232230

233231
assert.Equal(t, currentResourceState.GetAttr("tags"),

pkg/resource/resource.go

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ package resource
44
import (
55
"fmt"
66

7+
"github.com/hashicorp/terraform/configs/configschema"
8+
9+
"github.com/zclconf/go-cty/cty"
10+
711
"github.com/apex/log"
812
"github.com/jckuester/terradozer/internal"
913
"github.com/jckuester/terradozer/pkg/provider"
@@ -51,40 +55,56 @@ func (r Resource) ID() string {
5155

5256
// Destroy destroys a Terraform resource.
5357
func (r Resource) Destroy(dryRun bool) error {
54-
importedResources, err := r.provider.ImportResource(r.terraformType, r.id)
58+
resourceSchema, ok := r.provider.GetSchema().ResourceTypes[r.Type()]
59+
if !ok {
60+
return fmt.Errorf("failed to get schema for resource")
61+
}
62+
63+
currentResourceState, err := r.provider.ReadResource(r.Type(), emptyValueWitId(r.ID(), resourceSchema.Block))
5564
if err != nil {
56-
return fmt.Errorf("failed to import resource: %s", err)
65+
return fmt.Errorf("failed to read current state of resource: %s", err)
5766
}
5867

59-
for _, rImported := range importedResources {
60-
currentResourceState, err := r.provider.ReadResource(rImported)
61-
if err != nil {
62-
return fmt.Errorf("failed to read current state of resource: %s", err)
63-
}
68+
resourceNotFound := currentResourceState.IsNull()
69+
if resourceNotFound {
70+
return fmt.Errorf("resource found in state doesn't exist anymore")
71+
}
6472

65-
resourceNotFound := currentResourceState.IsNull()
66-
if resourceNotFound {
67-
return fmt.Errorf("resource found in state doesn't exist anymore")
68-
}
73+
if dryRun {
74+
log.WithField("id", r.id).Warn(internal.Pad(r.terraformType))
6975

70-
if dryRun {
71-
log.WithField("id", r.id).Warn(internal.Pad(r.terraformType))
76+
return nil
77+
}
7278

73-
return nil
74-
}
79+
err = r.provider.DestroyResource(r.terraformType, currentResourceState)
80+
if err != nil {
81+
log.WithError(err).WithFields(log.Fields{
82+
"id": r.id, "type": r.terraformType}).Debug(internal.Pad("failed to delete resource"))
7583

76-
err = r.provider.DestroyResource(r.terraformType, currentResourceState)
77-
if err != nil {
78-
log.WithError(err).WithFields(log.Fields{
79-
"id": r.id, "type": r.terraformType}).Debug(internal.Pad("failed to delete resource"))
84+
return NewRetryDestroyError(err, r)
85+
}
8086

81-
return NewRetryDestroyError(err, r)
82-
}
87+
log.WithField("id", r.id).Error(internal.Pad(r.terraformType))
88+
89+
return nil
90+
}
8391

84-
log.WithField("id", r.id).Error(internal.Pad(r.terraformType))
92+
// emptyValueWitId returns a non-null object for the configuration block
93+
// where all of the attribute values are set to empty values except the ID attribute.
94+
//
95+
// see also github.com/hashicorp/terraform/configs/configschema/empty_value.go
96+
func emptyValueWitId(id string, block *configschema.Block) cty.Value {
97+
vals := make(map[string]cty.Value)
98+
for name, attrS := range block.Attributes {
99+
vals[name] = attrS.EmptyValue()
100+
}
101+
for name, blockS := range block.BlockTypes {
102+
vals[name] = blockS.EmptyValue()
85103
}
86104

87-
return nil
105+
vals["id"] = cty.StringVal(id)
106+
107+
return cty.ObjectVal(vals)
88108
}
89109

90110
// DestroyResources destroys a given list of resources, which may depend on each other.

test/acc_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ func TestAcc_AllResourcesAlreadyDeleted(t *testing.T) {
157157
assert.NotContains(t, actualLogs, "TOTAL NUMBER OF DELETED RESOURCES: ")
158158

159159
fmt.Println(actualLogs)
160-
161160
}
162161

163162
func TestAcc_Version(t *testing.T) {

0 commit comments

Comments
 (0)