mirror of
https://github.com/hashicorp/terraform.git
synced 2026-06-09 00:42:48 -04:00
157 lines
4.3 KiB
Go
157 lines
4.3 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package configs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/gohcl"
|
|
)
|
|
|
|
// Removed describes the contents of a "removed" block in configuration.
|
|
type Removed struct {
|
|
// From is the address of the configuration object being removed.
|
|
From *addrs.RemoveTarget
|
|
|
|
// Destroy indicates that the resource should be destroyed, not just removed
|
|
// from state. Defaults to true.
|
|
Destroy bool
|
|
|
|
// Managed captures a number of metadata fields that are applicable only
|
|
// for managed resources, and not for other resource modes.
|
|
//
|
|
// "removed" blocks support only a subset of the fields in [ManagedResource].
|
|
Managed *ManagedResource
|
|
|
|
DeclRange hcl.Range
|
|
}
|
|
|
|
func decodeRemovedBlock(block *hcl.Block) (*Removed, hcl.Diagnostics) {
|
|
var diags hcl.Diagnostics
|
|
removed := &Removed{
|
|
DeclRange: block.DefRange,
|
|
}
|
|
|
|
content, moreDiags := block.Body.Content(removedBlockSchema)
|
|
diags = append(diags, moreDiags...)
|
|
|
|
var targetKind addrs.RemoveTargetKind
|
|
var resourceMode addrs.ResourceMode // only valid if targetKind is addrs.RemoveTargetResource
|
|
if attr, exists := content.Attributes["from"]; exists {
|
|
from, traversalDiags := hcl.AbsTraversalForExpr(attr.Expr)
|
|
diags = append(diags, traversalDiags...)
|
|
if !traversalDiags.HasErrors() {
|
|
from, fromDiags := addrs.ParseRemoveTarget(from)
|
|
diags = append(diags, fromDiags.ToHCL()...)
|
|
removed.From = from
|
|
if removed.From != nil {
|
|
targetKind = removed.From.ObjectKind()
|
|
if targetKind == addrs.RemoveTargetResource {
|
|
resourceMode = removed.From.RelSubject.(addrs.ConfigResource).Resource.Mode
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
removed.Destroy = true
|
|
if resourceMode == addrs.ManagedResourceMode {
|
|
removed.Managed = &ManagedResource{}
|
|
}
|
|
|
|
var seenConnection *hcl.Block
|
|
for _, block := range content.Blocks {
|
|
switch block.Type {
|
|
case "lifecycle":
|
|
lcContent, lcDiags := block.Body.Content(removedLifecycleBlockSchema)
|
|
diags = append(diags, lcDiags...)
|
|
|
|
if attr, exists := lcContent.Attributes["destroy"]; exists {
|
|
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &removed.Destroy)
|
|
diags = append(diags, valDiags...)
|
|
}
|
|
|
|
case "connection":
|
|
if removed.Managed == nil {
|
|
// target is not a managed resource, then
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid connection block",
|
|
Detail: "Provisioner connection configuration is valid only when a removed block targets a managed resource.",
|
|
Subject: &block.DefRange,
|
|
})
|
|
continue
|
|
}
|
|
|
|
if seenConnection != nil {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Duplicate connection block",
|
|
Detail: fmt.Sprintf("This \"removed\" block already has a connection block at %s.", seenConnection.DefRange),
|
|
Subject: &block.DefRange,
|
|
})
|
|
continue
|
|
}
|
|
seenConnection = block
|
|
|
|
removed.Managed.Connection = &Connection{
|
|
Config: block.Body,
|
|
DeclRange: block.DefRange,
|
|
}
|
|
|
|
case "provisioner":
|
|
if removed.Managed == nil {
|
|
// target is not a managed resource, then
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provisioner block",
|
|
Detail: "Provisioners are valid only when a removed block targets a managed resource.",
|
|
Subject: &block.DefRange,
|
|
})
|
|
continue
|
|
}
|
|
|
|
pv, pvDiags := decodeProvisionerBlock(block)
|
|
diags = append(diags, pvDiags...)
|
|
if pv != nil {
|
|
removed.Managed.Provisioners = append(removed.Managed.Provisioners, pv)
|
|
|
|
if pv.When != ProvisionerWhenDestroy {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid provisioner block",
|
|
Detail: "Only destroy-time provisioners are valid in \"removed\" blocks. To declare a destroy-time provisioner, use:\n when = destroy",
|
|
Subject: &block.DefRange,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return removed, diags
|
|
}
|
|
|
|
var removedBlockSchema = &hcl.BodySchema{
|
|
Attributes: []hcl.AttributeSchema{
|
|
{
|
|
Name: "from",
|
|
Required: true,
|
|
},
|
|
},
|
|
Blocks: []hcl.BlockHeaderSchema{
|
|
{Type: "lifecycle"},
|
|
{Type: "connection"},
|
|
{Type: "provisioner", LabelNames: []string{"type"}},
|
|
},
|
|
}
|
|
|
|
var removedLifecycleBlockSchema = &hcl.BodySchema{
|
|
Attributes: []hcl.AttributeSchema{
|
|
{
|
|
Name: "destroy",
|
|
},
|
|
},
|
|
}
|