terraform: build resource graph, validate

This commit is contained in:
Mitchell Hashimoto 2014-06-03 17:14:19 -07:00
parent 9480783ee4
commit d731d033f1
5 changed files with 41 additions and 14 deletions

View File

@ -9,6 +9,10 @@ import (
"github.com/hashicorp/terraform/depgraph" "github.com/hashicorp/terraform/depgraph"
) )
// ResourceGraphRoot is the name of the resource graph root that should be
// ignored since it doesn't map to an exact resource.
const ResourceGraphRoot = "root"
// Config is the configuration that comes from loading a collection // Config is the configuration that comes from loading a collection
// of Terraform templates. // of Terraform templates.
type Config struct { type Config struct {
@ -116,7 +120,7 @@ func (c *Config) ResourceGraph() *depgraph.Graph {
} }
// Create a root that just depends on everything else finishing. // Create a root that just depends on everything else finishing.
root := &depgraph.Noun{Name: "root"} root := &depgraph.Noun{Name: ResourceGraphRoot}
for _, n := range nounsList { for _, n := range nounsList {
root.Deps = append(root.Deps, &depgraph.Dependency{ root.Deps = append(root.Deps, &depgraph.Dependency{
Name: n.Name, Name: n.Name,

View File

@ -4,17 +4,5 @@ package terraform
// can use to keep track of what real world resources it is actually // can use to keep track of what real world resources it is actually
// managing. // managing.
type State struct { type State struct {
resources map[string]resourceState resources map[string]ResourceState
}
// resourceState is the state of a single resource.
//
// The ID is required and is some opaque string used to recognize
// the realized resource.
//
// Extra is arbitrary extra metadata that the resource provider returns
// that is sent back into the resource provider when it is needed.
type resourceState struct {
ID string
Extra map[string]interface{}
} }

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/depgraph"
) )
// Terraform is the primary structure that is used to interact with // Terraform is the primary structure that is used to interact with
@ -12,6 +13,7 @@ import (
// all resources, a resource tree, a specific resource, etc. // all resources, a resource tree, a specific resource, etc.
type Terraform struct { type Terraform struct {
config *config.Config config *config.Config
graph *depgraph.Graph
mapping map[*config.Resource]ResourceProvider mapping map[*config.Resource]ResourceProvider
variables map[string]string variables map[string]string
} }
@ -98,6 +100,13 @@ func New(c *Config) (*Terraform, error) {
mapping[r] = provider mapping[r] = provider
} }
// Build the resource graph
graph := c.Config.ResourceGraph()
if err := graph.Validate(); err != nil {
errs = append(errs, fmt.Errorf(
"Resource graph has an error: %s", err))
}
// If we accumulated any errors, then return them all // If we accumulated any errors, then return them all
if len(errs) > 0 { if len(errs) > 0 {
return nil, &MultiError{Errors: errs} return nil, &MultiError{Errors: errs}
@ -105,6 +114,7 @@ func New(c *Config) (*Terraform, error) {
return &Terraform{ return &Terraform{
config: c.Config, config: c.Config,
graph: graph,
mapping: mapping, mapping: mapping,
variables: c.Variables, variables: c.Variables,
}, nil }, nil

View File

@ -80,6 +80,24 @@ func TestNew(t *testing.T) {
} }
} }
func TestNew_graphCycle(t *testing.T) {
config := testConfig(t, "new-graph-cycle")
tfConfig := &Config{
Config: config,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFunc("aws", []string{"aws_instance"}),
},
}
tf, err := New(tfConfig)
if err == nil {
t.Fatal("should error")
}
if tf != nil {
t.Fatalf("should not return tf")
}
}
func TestNew_variables(t *testing.T) { func TestNew_variables(t *testing.T) {
config := testConfig(t, "new-variables") config := testConfig(t, "new-variables")
tfConfig := &Config{ tfConfig := &Config{

View File

@ -0,0 +1,7 @@
resource "aws_instance" "foo" {
ami = "${aws_instance.bar.id}"
}
resource "aws_instance" "bar" {
ami = "${aws_instance.foo.id}"
}