mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
125 lines
4.2 KiB
Go
125 lines
4.2 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package refactoring
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/configs"
|
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
|
)
|
|
|
|
type RemoveStatement struct {
|
|
From addrs.ConfigRemovable
|
|
DeclRange tfdiags.SourceRange
|
|
}
|
|
|
|
// GetEndpointsToRemove recurses through the modules of the given configuration
|
|
// and returns an array of all "removed" addresses within, in a
|
|
// deterministic but undefined order.
|
|
// We also validate that the removed modules/resources configuration blocks were removed.
|
|
func GetEndpointsToRemove(rootCfg *configs.Config) ([]addrs.ConfigRemovable, tfdiags.Diagnostics) {
|
|
rm := findRemoveStatements(rootCfg, nil)
|
|
diags := validateRemoveStatements(rootCfg, rm)
|
|
removedAddresses := make([]addrs.ConfigRemovable, len(rm))
|
|
for i, rs := range rm {
|
|
removedAddresses[i] = rs.From
|
|
}
|
|
return removedAddresses, diags
|
|
}
|
|
|
|
func findRemoveStatements(cfg *configs.Config, into []*RemoveStatement) []*RemoveStatement {
|
|
modAddr := cfg.Path
|
|
|
|
for _, rc := range cfg.Module.Removed {
|
|
var removedEndpoint *RemoveStatement
|
|
switch FromAddress := rc.From.RelSubject.(type) {
|
|
case addrs.ConfigResource:
|
|
// Get the absolute address of the resource by appending the module config address
|
|
// to the resource's relative address
|
|
absModule := make(addrs.Module, 0, len(modAddr)+len(FromAddress.Module))
|
|
absModule = append(absModule, modAddr...)
|
|
absModule = append(absModule, FromAddress.Module...)
|
|
|
|
var absConfigResource addrs.ConfigRemovable = addrs.ConfigResource{
|
|
Resource: FromAddress.Resource,
|
|
Module: absModule,
|
|
}
|
|
|
|
removedEndpoint = &RemoveStatement{From: absConfigResource, DeclRange: tfdiags.SourceRangeFromHCL(rc.DeclRange)}
|
|
|
|
case addrs.Module:
|
|
// Get the absolute address of the module by appending the module config address
|
|
// to the module itself
|
|
var absModule = make(addrs.Module, 0, len(modAddr)+len(FromAddress))
|
|
absModule = append(absModule, modAddr...)
|
|
absModule = append(absModule, FromAddress...)
|
|
removedEndpoint = &RemoveStatement{From: absModule, DeclRange: tfdiags.SourceRangeFromHCL(rc.DeclRange)}
|
|
|
|
default:
|
|
panic(fmt.Sprintf("unhandled address type %T", FromAddress))
|
|
}
|
|
|
|
into = append(into, removedEndpoint)
|
|
|
|
}
|
|
|
|
for _, childCfg := range cfg.Children {
|
|
into = findRemoveStatements(childCfg, into)
|
|
}
|
|
|
|
return into
|
|
}
|
|
|
|
// validateRemoveStatements validates that the removed modules/resources configuration blocks were removed.
|
|
func validateRemoveStatements(cfg *configs.Config, removeStatements []*RemoveStatement) tfdiags.Diagnostics {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
for _, rs := range removeStatements {
|
|
fromAddr := rs.From
|
|
if fromAddr == nil {
|
|
// Invalid value should've been caught during original
|
|
// configuration decoding, in the configs package.
|
|
panic(fmt.Sprintf("incompatible Remove endpoint in %s", rs.DeclRange.ToHCL()))
|
|
}
|
|
|
|
// validate that a resource/module with this address doesn't exist in the config
|
|
switch fromAddr := fromAddr.(type) {
|
|
case addrs.ConfigResource:
|
|
moduleConfig := cfg.Descendent(fromAddr.Module)
|
|
if moduleConfig != nil && moduleConfig.Module.ResourceByAddr(fromAddr.Resource) != nil {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Removed resource block still exists",
|
|
Detail: fmt.Sprintf(
|
|
"This statement declares a removal of the resource %s, but this resource block still exists in the configuration. Please remove the resource block.",
|
|
fromAddr,
|
|
),
|
|
Subject: rs.DeclRange.ToHCL().Ptr(),
|
|
})
|
|
}
|
|
case addrs.Module:
|
|
if cfg.Descendent(fromAddr) != nil {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Removed module block still exists",
|
|
Detail: fmt.Sprintf(
|
|
"This statement declares a removal of the module %s, but this module block still exists in the configuration. Please remove the module block.",
|
|
fromAddr,
|
|
),
|
|
Subject: rs.DeclRange.ToHCL().Ptr(),
|
|
})
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("incompatible Remove endpoint address type in %s", rs.DeclRange.ToHCL()))
|
|
}
|
|
}
|
|
|
|
return diags
|
|
}
|