terraform: validation in progress
This commit is contained in:
parent
e86698c50d
commit
c308405b53
|
@ -1,6 +1,7 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
|
@ -62,6 +63,14 @@ func (c *Context2) GraphBuilder() GraphBuilder {
|
|||
|
||||
// Validate validates the configuration and returns any warnings or errors.
|
||||
func (c *Context2) Validate() ([]string, []error) {
|
||||
var warns []string
|
||||
var errs []error
|
||||
|
||||
// Validate the configuration itself
|
||||
if err := c.module.Validate(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
evalCtx := c.evalContext()
|
||||
evalCtx.ComputeMissing = true
|
||||
|
||||
|
@ -71,14 +80,7 @@ func (c *Context2) Validate() ([]string, []error) {
|
|||
return nil, []error{err}
|
||||
}
|
||||
|
||||
// Valiate the graph
|
||||
if err := graph.Validate(); err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
// Walk the graph
|
||||
var warns []string
|
||||
var errs []error
|
||||
var lock sync.Mutex
|
||||
graph.Walk(func(v dag.Vertex) {
|
||||
ev, ok := v.(GraphNodeEvalable)
|
||||
|
@ -86,7 +88,12 @@ func (c *Context2) Validate() ([]string, []error) {
|
|||
return
|
||||
}
|
||||
|
||||
_, err := Eval(ev.EvalTree(), evalCtx)
|
||||
tree := ev.EvalTree()
|
||||
if tree == nil {
|
||||
panic(fmt.Sprintf("%s (%T): nil eval tree", dag.VertexName(v), v))
|
||||
}
|
||||
|
||||
_, err := Eval(tree, evalCtx)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -19,7 +21,7 @@ func TestContext2Validate(t *testing.T) {
|
|||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) > 0 {
|
||||
t.Fatalf("bad: %#v", e)
|
||||
t.Fatalf("bad: %s", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,6 +44,70 @@ func TestContext2Validate_badVar(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_orphans(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "validate-good")
|
||||
state := &State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.web": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
c := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
State: state,
|
||||
})
|
||||
|
||||
p.ValidateResourceFn = func(
|
||||
t string, c *ResourceConfig) ([]string, []error) {
|
||||
return nil, c.CheckSet([]string{"foo"})
|
||||
}
|
||||
|
||||
w, e := c.Validate()
|
||||
if len(w) > 0 {
|
||||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) > 0 {
|
||||
t.Fatalf("bad: %#v", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_providerConfig_bad(t *testing.T) {
|
||||
m := testModule(t, "validate-bad-pc")
|
||||
p := testProvider("aws")
|
||||
c := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ValidateReturnErrors = []error{fmt.Errorf("bad")}
|
||||
|
||||
w, e := c.Validate()
|
||||
if len(w) > 0 {
|
||||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) == 0 {
|
||||
t.Fatalf("bad: %s", e)
|
||||
}
|
||||
if !strings.Contains(fmt.Sprintf("%s", e), "bad") {
|
||||
t.Fatalf("bad: %s", e)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestContextValidate_goodModule(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
|
@ -188,46 +254,6 @@ func TestContextValidate_moduleProviderInherit(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContextValidate_orphans(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "validate-good")
|
||||
state := &State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.web": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Primary: &InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
c := testContext(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
State: state,
|
||||
})
|
||||
|
||||
p.ValidateResourceFn = func(
|
||||
t string, c *ResourceConfig) ([]string, []error) {
|
||||
return nil, c.CheckSet([]string{"foo"})
|
||||
}
|
||||
|
||||
w, e := c.Validate()
|
||||
if len(w) > 0 {
|
||||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) > 0 {
|
||||
t.Fatalf("bad: %#v", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextValidate_tainted(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "validate-good")
|
||||
|
@ -270,27 +296,6 @@ func TestContextValidate_tainted(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContextValidate_providerConfig_bad(t *testing.T) {
|
||||
m := testModule(t, "validate-bad-pc")
|
||||
p := testProvider("aws")
|
||||
c := testContext(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ValidateReturnErrors = []error{fmt.Errorf("bad")}
|
||||
|
||||
w, e := c.Validate()
|
||||
if len(w) > 0 {
|
||||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) == 0 {
|
||||
t.Fatalf("bad: %#v", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextValidate_providerConfig_badEmpty(t *testing.T) {
|
||||
m := testModule(t, "validate-bad-pc-empty")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -30,7 +30,13 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
|
|||
return nil, fmt.Errorf("Provider '%s' not found", n)
|
||||
}
|
||||
|
||||
return f()
|
||||
p, err := f()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.providers[n] = p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
)
|
||||
|
||||
|
@ -20,8 +18,9 @@ func (e *EvalValidateError) Error() string {
|
|||
// EvalValidateResource is an EvalNode implementation that validates
|
||||
// the configuration of a resource.
|
||||
type EvalValidateResource struct {
|
||||
Provider EvalNode
|
||||
Config *config.RawConfig
|
||||
Provider EvalNode
|
||||
Config *config.RawConfig
|
||||
ProviderType string
|
||||
}
|
||||
|
||||
func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -31,8 +30,10 @@ func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
|||
|
||||
func (n *EvalValidateResource) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
// TODO: test
|
||||
|
||||
//provider := args[0].(ResourceProvider)
|
||||
return nil, fmt.Errorf("WHAT")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *EvalValidateResource) Type() EvalType {
|
||||
|
|
|
@ -41,6 +41,11 @@ func (b *BuiltinGraphBuilder) Build(path []string) (*Graph, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate the graph structure
|
||||
if err := g.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
|
@ -54,5 +59,6 @@ func (b *BuiltinGraphBuilder) Steps() []GraphTransformer {
|
|||
&MissingProviderTransformer{Providers: b.Providers},
|
||||
&ProviderTransformer{},
|
||||
&PruneProviderTransformer{},
|
||||
&RootTransformer{},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,9 @@ func (n *GraphNodeConfigResource) Name() string {
|
|||
func (n *GraphNodeConfigResource) EvalTree() EvalNode {
|
||||
return &EvalValidateResource{
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()},
|
||||
Config: n.Resource.RawConfig,
|
||||
|
||||
Config: n.Resource.RawConfig,
|
||||
ProviderType: n.ProvidedBy(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
provider "aws" {}
|
||||
resource "aws_instance" "foo" {}
|
||||
|
||||
provider "do" {}
|
||||
resource "do_droplet" "bar" {}
|
|
@ -119,10 +119,8 @@ func (n *graphNodeOrphanResource) ProvidedBy() string {
|
|||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
/*
|
||||
func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
||||
return nil
|
||||
/*
|
||||
TODO
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalRefresh{},
|
||||
|
@ -131,8 +129,8 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||
&EvalCommitState{},
|
||||
},
|
||||
}
|
||||
*/
|
||||
}
|
||||
*/
|
||||
|
||||
func (n *graphNodeOrphanResource) dependableName() string {
|
||||
return n.ResourceName
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
)
|
||||
|
||||
// RootTransformer is a GraphTransformer that adds a root to the graph.
|
||||
type RootTransformer struct{}
|
||||
|
||||
func (t *RootTransformer) Transform(g *Graph) error {
|
||||
// If we already have a good root, we're done
|
||||
if _, err := g.Root(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add a root
|
||||
var root graphNodeRoot
|
||||
g.Add(root)
|
||||
|
||||
// Connect the root to all the edges that need it
|
||||
for _, v := range g.Vertices() {
|
||||
if v == root {
|
||||
continue
|
||||
}
|
||||
|
||||
if g.UpEdges(v).Len() == 0 {
|
||||
g.Connect(dag.BasicEdge(root, v))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type graphNodeRoot struct{}
|
||||
|
||||
func (n graphNodeRoot) Name() string {
|
||||
return "root"
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRootTransformer(t *testing.T) {
|
||||
mod := testModule(t, "transform-root-basic")
|
||||
|
||||
g := Graph{Path: RootModulePath}
|
||||
{
|
||||
tf := &ConfigTransformer{Module: mod}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
transform := &ProviderTransformer{}
|
||||
if err := transform.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
transform := &RootTransformer{}
|
||||
if err := transform.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(g.String())
|
||||
expected := strings.TrimSpace(testTransformRootBasicStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n\n%s", actual)
|
||||
}
|
||||
|
||||
root, err := g.Root()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if _, ok := root.(graphNodeRoot); !ok {
|
||||
t.Fatalf("bad: %#v", root)
|
||||
}
|
||||
}
|
||||
|
||||
const testTransformRootBasicStr = `
|
||||
aws_instance.foo
|
||||
provider.aws
|
||||
do_droplet.bar
|
||||
provider.do
|
||||
provider.aws
|
||||
provider.do
|
||||
root
|
||||
aws_instance.foo
|
||||
do_droplet.bar
|
||||
`
|
Loading…
Reference in New Issue