mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-11 08:05:33 -06:00
374 lines
7.8 KiB
Go
374 lines
7.8 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
)
|
|
|
|
// EvalCompareDiff is an EvalNode implementation that compares two diffs
|
|
// and errors if the diffs are not equal.
|
|
type EvalCompareDiff struct {
|
|
Info *InstanceInfo
|
|
One, Two **InstanceDiff
|
|
}
|
|
|
|
func (n *EvalCompareDiff) Args() ([]EvalNode, []EvalType) {
|
|
return nil, nil
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalCompareDiff) Eval(
|
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
|
one, two := *n.One, *n.Two
|
|
|
|
// If either are nil, let them be empty
|
|
if one == nil {
|
|
one = new(InstanceDiff)
|
|
one.init()
|
|
}
|
|
if two == nil {
|
|
two = new(InstanceDiff)
|
|
two.init()
|
|
}
|
|
oneId := one.Attributes["id"]
|
|
twoId := two.Attributes["id"]
|
|
delete(one.Attributes, "id")
|
|
delete(two.Attributes, "id")
|
|
defer func() {
|
|
if oneId != nil {
|
|
one.Attributes["id"] = oneId
|
|
}
|
|
if twoId != nil {
|
|
two.Attributes["id"] = twoId
|
|
}
|
|
}()
|
|
|
|
if !one.Same(two) {
|
|
log.Printf("[ERROR] %s: diff's didn't match", n.Info.Id)
|
|
log.Printf("[ERROR] %s: diff one: %#v", n.Info.Id, one)
|
|
log.Printf("[ERROR] %s: diff two: %#v", n.Info.Id, two)
|
|
return nil, fmt.Errorf(
|
|
"%s: diffs didn't match during apply. This is a bug with "+
|
|
"Terraform and should be reported.", n.Info.Id)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *EvalCompareDiff) Type() EvalType {
|
|
return EvalTypeNull
|
|
}
|
|
|
|
// EvalDiff is an EvalNode implementation that does a refresh for
|
|
// a resource.
|
|
type EvalDiff struct {
|
|
Info *InstanceInfo
|
|
Config EvalNode
|
|
Provider EvalNode
|
|
State EvalNode
|
|
Output **InstanceDiff
|
|
OutputState **InstanceState
|
|
}
|
|
|
|
func (n *EvalDiff) Args() ([]EvalNode, []EvalType) {
|
|
return []EvalNode{n.Config, n.Provider, n.State},
|
|
[]EvalType{EvalTypeConfig, EvalTypeResourceProvider,
|
|
EvalTypeInstanceState}
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalDiff) Eval(
|
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
|
// Extract our arguments
|
|
var state *InstanceState
|
|
config := args[0].(*ResourceConfig)
|
|
provider := args[1].(ResourceProvider)
|
|
if args[2] != nil {
|
|
state = args[2].(*InstanceState)
|
|
}
|
|
|
|
// Call pre-diff hook
|
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PreDiff(n.Info, state)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// The state for the diff must never be nil
|
|
diffState := state
|
|
if diffState == nil {
|
|
diffState = new(InstanceState)
|
|
}
|
|
diffState.init()
|
|
|
|
// Diff!
|
|
diff, err := provider.Diff(n.Info, diffState, config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if diff == nil {
|
|
diff = new(InstanceDiff)
|
|
}
|
|
|
|
// Require a destroy if there is no ID and it requires new.
|
|
if diff.RequiresNew() && state != nil && state.ID != "" {
|
|
diff.Destroy = true
|
|
}
|
|
|
|
// If we're creating a new resource, compute its ID
|
|
if diff.RequiresNew() || state == nil || state.ID == "" {
|
|
var oldID string
|
|
if state != nil {
|
|
oldID = state.Attributes["id"]
|
|
}
|
|
|
|
// Add diff to compute new ID
|
|
diff.init()
|
|
diff.Attributes["id"] = &ResourceAttrDiff{
|
|
Old: oldID,
|
|
NewComputed: true,
|
|
RequiresNew: true,
|
|
Type: DiffAttrOutput,
|
|
}
|
|
}
|
|
|
|
// Call post-refresh hook
|
|
err = ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PostDiff(n.Info, diff)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Update our output
|
|
*n.Output = diff
|
|
|
|
// Update the state if we care
|
|
if n.OutputState != nil {
|
|
*n.OutputState = state
|
|
|
|
// Merge our state so that the state is updated with our plan
|
|
if !diff.Empty() && n.OutputState != nil {
|
|
*n.OutputState = state.MergeDiff(diff)
|
|
}
|
|
}
|
|
|
|
return state, nil
|
|
}
|
|
|
|
func (n *EvalDiff) Type() EvalType {
|
|
return EvalTypeInstanceState
|
|
}
|
|
|
|
// EvalDiffDestroy is an EvalNode implementation that returns a plain
|
|
// destroy diff.
|
|
type EvalDiffDestroy struct {
|
|
Info *InstanceInfo
|
|
State EvalNode
|
|
Output **InstanceDiff
|
|
}
|
|
|
|
func (n *EvalDiffDestroy) Args() ([]EvalNode, []EvalType) {
|
|
return []EvalNode{n.State}, []EvalType{EvalTypeInstanceState}
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalDiffDestroy) Eval(
|
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
|
// Extract our arguments
|
|
var state *InstanceState
|
|
if args[0] != nil {
|
|
state = args[0].(*InstanceState)
|
|
}
|
|
|
|
// If there is no state or we don't have an ID, we're already destroyed
|
|
if state == nil || state.ID == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
// Call pre-diff hook
|
|
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PreDiff(n.Info, state)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// The diff
|
|
diff := &InstanceDiff{Destroy: true}
|
|
|
|
// Call post-diff hook
|
|
err = ctx.Hook(func(h Hook) (HookAction, error) {
|
|
return h.PostDiff(n.Info, diff)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Update our output
|
|
*n.Output = diff
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *EvalDiffDestroy) Type() EvalType {
|
|
return EvalTypeNull
|
|
}
|
|
|
|
// EvalDiffDestroyModule is an EvalNode implementation that writes the diff to
|
|
// the full diff.
|
|
type EvalDiffDestroyModule struct {
|
|
Path []string
|
|
}
|
|
|
|
func (n *EvalDiffDestroyModule) Args() ([]EvalNode, []EvalType) {
|
|
return nil, nil
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalDiffDestroyModule) Eval(
|
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
|
diff, lock := ctx.Diff()
|
|
|
|
// Acquire the lock so that we can do this safely concurrently
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
// Write the diff
|
|
modDiff := diff.ModuleByPath(n.Path)
|
|
if modDiff == nil {
|
|
modDiff = diff.AddModule(n.Path)
|
|
}
|
|
modDiff.Destroy = true
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *EvalDiffDestroyModule) Type() EvalType {
|
|
return EvalTypeNull
|
|
}
|
|
|
|
// EvalDiffTainted is an EvalNode implementation that writes the diff to
|
|
// the full diff.
|
|
type EvalDiffTainted struct {
|
|
Name string
|
|
Diff **InstanceDiff
|
|
}
|
|
|
|
func (n *EvalDiffTainted) Args() ([]EvalNode, []EvalType) {
|
|
return nil, nil
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalDiffTainted) Eval(
|
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
|
state, lock := ctx.State()
|
|
|
|
// Get a read lock so we can access this instance
|
|
lock.RLock()
|
|
defer lock.RUnlock()
|
|
|
|
// Look for the module state. If we don't have one, then it doesn't matter.
|
|
mod := state.ModuleByPath(ctx.Path())
|
|
if mod == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
// Look for the resource state. If we don't have one, then it is okay.
|
|
rs := mod.Resources[n.Name]
|
|
if rs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
// If we have tainted, then mark it on the diff
|
|
if len(rs.Tainted) > 0 {
|
|
(*n.Diff).DestroyTainted = true
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *EvalDiffTainted) Type() EvalType {
|
|
return EvalTypeNull
|
|
}
|
|
|
|
// EvalReadDiff is an EvalNode implementation that writes the diff to
|
|
// the full diff.
|
|
type EvalReadDiff struct {
|
|
Name string
|
|
Diff **InstanceDiff
|
|
}
|
|
|
|
func (n *EvalReadDiff) Args() ([]EvalNode, []EvalType) {
|
|
return nil, nil
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalReadDiff) Eval(
|
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
|
diff, lock := ctx.Diff()
|
|
|
|
// Acquire the lock so that we can do this safely concurrently
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
// Write the diff
|
|
modDiff := diff.ModuleByPath(ctx.Path())
|
|
if modDiff == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
*n.Diff = modDiff.Resources[n.Name]
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *EvalReadDiff) Type() EvalType {
|
|
return EvalTypeNull
|
|
}
|
|
|
|
// EvalWriteDiff is an EvalNode implementation that writes the diff to
|
|
// the full diff.
|
|
type EvalWriteDiff struct {
|
|
Name string
|
|
Diff **InstanceDiff
|
|
}
|
|
|
|
func (n *EvalWriteDiff) Args() ([]EvalNode, []EvalType) {
|
|
return nil, nil
|
|
}
|
|
|
|
// TODO: test
|
|
func (n *EvalWriteDiff) Eval(
|
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
|
diff, lock := ctx.Diff()
|
|
|
|
// The diff to write, if its empty it should write nil
|
|
diffVal := *n.Diff
|
|
if diffVal.Empty() {
|
|
diffVal = nil
|
|
}
|
|
|
|
// Acquire the lock so that we can do this safely concurrently
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
|
|
// Write the diff
|
|
modDiff := diff.ModuleByPath(ctx.Path())
|
|
if modDiff == nil {
|
|
modDiff = diff.AddModule(ctx.Path())
|
|
}
|
|
if diffVal != nil {
|
|
modDiff.Resources[n.Name] = diffVal
|
|
} else {
|
|
delete(modDiff.Resources, n.Name)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *EvalWriteDiff) Type() EvalType {
|
|
return EvalTypeNull
|
|
}
|