mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
terraform: filter eval nodes on operation
This commit is contained in:
parent
bde5fa36e9
commit
863b9a4f45
@ -71,6 +71,21 @@ func (c *Context2) GraphBuilder() GraphBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh goes through all the resources in the state and refreshes them
|
||||||
|
// to their latest state. This will update the state that this context
|
||||||
|
// works with, along with returning it.
|
||||||
|
//
|
||||||
|
// Even in the case an error is returned, the state will be returned and
|
||||||
|
// will potentially be partially updated.
|
||||||
|
func (c *Context2) Refresh() (*State, []error) {
|
||||||
|
if _, err := c.walk(walkRefresh); err != nil {
|
||||||
|
var errs error
|
||||||
|
return nil, multierror.Append(errs, err).Errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Validate validates the configuration and returns any warnings or errors.
|
// Validate validates the configuration and returns any warnings or errors.
|
||||||
func (c *Context2) Validate() ([]string, []error) {
|
func (c *Context2) Validate() ([]string, []error) {
|
||||||
var errs error
|
var errs error
|
||||||
@ -89,17 +104,25 @@ func (c *Context2) Validate() ([]string, []error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the graph
|
// Walk
|
||||||
graph, err := c.GraphBuilder().Build(RootModulePath)
|
walker, err := c.walk(walkValidate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, multierror.Append(errs, err).Errors
|
return nil, multierror.Append(errs, err).Errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk the graph
|
|
||||||
walker := &ContextGraphWalker{Context: c, Operation: walkValidate}
|
|
||||||
graph.Walk(walker)
|
|
||||||
|
|
||||||
// Return the result
|
// Return the result
|
||||||
rerrs := multierror.Append(errs, walker.ValidationErrors...)
|
rerrs := multierror.Append(errs, walker.ValidationErrors...)
|
||||||
return walker.ValidationWarnings, rerrs.Errors
|
return walker.ValidationWarnings, rerrs.Errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context2) walk(operation walkOperation) (*ContextGraphWalker, error) {
|
||||||
|
// Build the graph
|
||||||
|
graph, err := c.GraphBuilder().Build(RootModulePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk the graph
|
||||||
|
walker := &ContextGraphWalker{Context: c, Operation: operation}
|
||||||
|
return walker, graph.Walk(walker)
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package terraform
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -565,6 +566,58 @@ func TestContext2Validate_varRefFilled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext2Refresh(t *testing.T) {
|
||||||
|
p := testProvider("aws")
|
||||||
|
m := testModule(t, "refresh-basic")
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
State: &State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: rootModulePath,
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"aws_instance.web": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
p.RefreshFn = nil
|
||||||
|
p.RefreshReturn = &InstanceState{
|
||||||
|
ID: "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := ctx.Refresh()
|
||||||
|
mod := s.RootModule()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if !p.RefreshCalled {
|
||||||
|
t.Fatal("refresh should be called")
|
||||||
|
}
|
||||||
|
if p.RefreshState.ID != "foo" {
|
||||||
|
t.Fatalf("bad: %#v", p.RefreshState)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) {
|
||||||
|
t.Fatalf("bad: %#v %#v", mod.Resources["aws_instance.web"], p.RefreshReturn)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range mod.Resources {
|
||||||
|
if r.Type == "" {
|
||||||
|
t.Fatalf("no type: %#v", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func TestContextInput(t *testing.T) {
|
func TestContextInput(t *testing.T) {
|
||||||
input := new(MockUIInput)
|
input := new(MockUIInput)
|
||||||
@ -4158,58 +4211,6 @@ func TestContextPlan_varListErr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextRefresh(t *testing.T) {
|
|
||||||
p := testProvider("aws")
|
|
||||||
m := testModule(t, "refresh-basic")
|
|
||||||
ctx := testContext(t, &ContextOpts{
|
|
||||||
Module: m,
|
|
||||||
Providers: map[string]ResourceProviderFactory{
|
|
||||||
"aws": testProviderFuncFixed(p),
|
|
||||||
},
|
|
||||||
State: &State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.web": &ResourceState{
|
|
||||||
Type: "aws_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
p.RefreshFn = nil
|
|
||||||
p.RefreshReturn = &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := ctx.Refresh()
|
|
||||||
mod := s.RootModule()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
if !p.RefreshCalled {
|
|
||||||
t.Fatal("refresh should be called")
|
|
||||||
}
|
|
||||||
if p.RefreshState.ID != "foo" {
|
|
||||||
t.Fatalf("bad: %#v", p.RefreshState)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(mod.Resources["aws_instance.web"].Primary, p.RefreshReturn) {
|
|
||||||
t.Fatalf("bad: %#v %#v", mod.Resources["aws_instance.web"], p.RefreshReturn)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range mod.Resources {
|
|
||||||
if r.Type == "" {
|
|
||||||
t.Fatalf("no type: %#v", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContextRefresh_delete(t *testing.T) {
|
func TestContextRefresh_delete(t *testing.T) {
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
m := testModule(t, "refresh-basic")
|
m := testModule(t, "refresh-basic")
|
||||||
|
23
terraform/eval_filter_operation.go
Normal file
23
terraform/eval_filter_operation.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package terraform
|
||||||
|
|
||||||
|
// EvalNodeOpFilterable is an interface that EvalNodes can implement
|
||||||
|
// to be filterable by the operation that is being run on Terraform.
|
||||||
|
type EvalNodeOpFilterable interface {
|
||||||
|
IncludeInOp(walkOperation) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvalNodeFilterOp returns a filter function that filters nodes that
|
||||||
|
// include themselves in specific operations.
|
||||||
|
func EvalNodeFilterOp(op walkOperation) EvalNodeFilterFunc {
|
||||||
|
return func(n EvalNode) EvalNode {
|
||||||
|
include := true
|
||||||
|
if of, ok := n.(EvalNodeOpFilterable); ok {
|
||||||
|
include = of.IncludeInOp(op)
|
||||||
|
}
|
||||||
|
if include {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
return EvalNoop{}
|
||||||
|
}
|
||||||
|
}
|
10
terraform/eval_noop.go
Normal file
10
terraform/eval_noop.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package terraform
|
||||||
|
|
||||||
|
// EvalNoop is an EvalNode that does nothing.
|
||||||
|
type EvalNoop struct{}
|
||||||
|
|
||||||
|
func (EvalNoop) Args() ([]EvalNode, []EvalType) { return nil, nil }
|
||||||
|
func (EvalNoop) Eval(EvalContext, []interface{}) (interface{}, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (EvalNoop) Type() EvalType { return EvalTypeNull }
|
@ -117,9 +117,8 @@ func (g *Graph) Dependable(n string) dag.Vertex {
|
|||||||
// Walk walks the graph with the given walker for callbacks. The graph
|
// Walk walks the graph with the given walker for callbacks. The graph
|
||||||
// will be walked with full parallelism, so the walker should expect
|
// will be walked with full parallelism, so the walker should expect
|
||||||
// to be called in concurrently.
|
// to be called in concurrently.
|
||||||
func (g *Graph) Walk(walker GraphWalker) {
|
func (g *Graph) Walk(walker GraphWalker) error {
|
||||||
// TODO: test
|
return g.walk(walker)
|
||||||
g.walk(walker)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graph) init() {
|
func (g *Graph) init() {
|
||||||
|
@ -53,6 +53,12 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
|
||||||
|
// We want to filter the evaluation tree to only include operations
|
||||||
|
// that belong in this operation.
|
||||||
|
return EvalFilter(n, EvalNodeFilterOp(w.Operation))
|
||||||
|
}
|
||||||
|
|
||||||
func (w *ContextGraphWalker) ExitEvalTree(
|
func (w *ContextGraphWalker) ExitEvalTree(
|
||||||
v dag.Vertex, output interface{}, err error) {
|
v dag.Vertex, output interface{}, err error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user