terraform: validation in progress
This commit is contained in:
parent
e86698c50d
commit
c308405b53
|
@ -1,6 +1,7 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/module"
|
"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.
|
// Validate validates the configuration and returns any warnings or errors.
|
||||||
func (c *Context2) Validate() ([]string, []error) {
|
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 := c.evalContext()
|
||||||
evalCtx.ComputeMissing = true
|
evalCtx.ComputeMissing = true
|
||||||
|
|
||||||
|
@ -71,14 +80,7 @@ func (c *Context2) Validate() ([]string, []error) {
|
||||||
return nil, []error{err}
|
return nil, []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valiate the graph
|
|
||||||
if err := graph.Validate(); err != nil {
|
|
||||||
return nil, []error{err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk the graph
|
// Walk the graph
|
||||||
var warns []string
|
|
||||||
var errs []error
|
|
||||||
var lock sync.Mutex
|
var lock sync.Mutex
|
||||||
graph.Walk(func(v dag.Vertex) {
|
graph.Walk(func(v dag.Vertex) {
|
||||||
ev, ok := v.(GraphNodeEvalable)
|
ev, ok := v.(GraphNodeEvalable)
|
||||||
|
@ -86,7 +88,12 @@ func (c *Context2) Validate() ([]string, []error) {
|
||||||
return
|
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 {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ func TestContext2Validate(t *testing.T) {
|
||||||
t.Fatalf("bad: %#v", w)
|
t.Fatalf("bad: %#v", w)
|
||||||
}
|
}
|
||||||
if len(e) > 0 {
|
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) {
|
func TestContextValidate_goodModule(t *testing.T) {
|
||||||
p := testProvider("aws")
|
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) {
|
func TestContextValidate_tainted(t *testing.T) {
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
m := testModule(t, "validate-good")
|
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) {
|
func TestContextValidate_providerConfig_badEmpty(t *testing.T) {
|
||||||
m := testModule(t, "validate-bad-pc-empty")
|
m := testModule(t, "validate-bad-pc-empty")
|
||||||
p := testProvider("aws")
|
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 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 {
|
func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,8 +18,9 @@ func (e *EvalValidateError) Error() string {
|
||||||
// EvalValidateResource is an EvalNode implementation that validates
|
// EvalValidateResource is an EvalNode implementation that validates
|
||||||
// the configuration of a resource.
|
// the configuration of a resource.
|
||||||
type EvalValidateResource struct {
|
type EvalValidateResource struct {
|
||||||
Provider EvalNode
|
Provider EvalNode
|
||||||
Config *config.RawConfig
|
Config *config.RawConfig
|
||||||
|
ProviderType string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
||||||
|
@ -31,8 +30,10 @@ func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
||||||
|
|
||||||
func (n *EvalValidateResource) Eval(
|
func (n *EvalValidateResource) Eval(
|
||||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||||
|
// TODO: test
|
||||||
|
|
||||||
//provider := args[0].(ResourceProvider)
|
//provider := args[0].(ResourceProvider)
|
||||||
return nil, fmt.Errorf("WHAT")
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EvalValidateResource) Type() EvalType {
|
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
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,5 +59,6 @@ func (b *BuiltinGraphBuilder) Steps() []GraphTransformer {
|
||||||
&MissingProviderTransformer{Providers: b.Providers},
|
&MissingProviderTransformer{Providers: b.Providers},
|
||||||
&ProviderTransformer{},
|
&ProviderTransformer{},
|
||||||
&PruneProviderTransformer{},
|
&PruneProviderTransformer{},
|
||||||
|
&RootTransformer{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,9 @@ func (n *GraphNodeConfigResource) Name() string {
|
||||||
func (n *GraphNodeConfigResource) EvalTree() EvalNode {
|
func (n *GraphNodeConfigResource) EvalTree() EvalNode {
|
||||||
return &EvalValidateResource{
|
return &EvalValidateResource{
|
||||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()},
|
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.
|
// GraphNodeEvalable impl.
|
||||||
|
/*
|
||||||
func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
||||||
return nil
|
|
||||||
/*
|
|
||||||
TODO
|
|
||||||
return &EvalSequence{
|
return &EvalSequence{
|
||||||
Nodes: []EvalNode{
|
Nodes: []EvalNode{
|
||||||
&EvalRefresh{},
|
&EvalRefresh{},
|
||||||
|
@ -131,8 +129,8 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
||||||
&EvalCommitState{},
|
&EvalCommitState{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func (n *graphNodeOrphanResource) dependableName() string {
|
func (n *graphNodeOrphanResource) dependableName() string {
|
||||||
return n.ResourceName
|
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