terraform: make our own graph meta

This commit is contained in:
Mitchell Hashimoto 2014-06-24 15:25:04 -07:00
parent b6272a4a69
commit 0bf394dfe2
2 changed files with 67 additions and 37 deletions

View File

@ -11,19 +11,40 @@ import (
// graph. This node is just a placemarker and has no associated functionality. // graph. This node is just a placemarker and has no associated functionality.
const GraphRootNode = "root" const GraphRootNode = "root"
// GraphNodeResource is a node type in the graph that represents a resource.
type GraphNodeResource struct {
Type string
Config *config.Resource
Orphan bool
Resource *Resource
ResourceProviderID string
}
// GraphNodeResourceProvider is a node type in the graph that represents
// the configuration for a resource provider.
type GraphNodeResourceProvider struct {
ID string
Provider ResourceProvider
Config *config.ProviderConfig
}
// Graph builds a dependency graph for the given configuration and state. // Graph builds a dependency graph for the given configuration and state.
// //
// Before using this graph, Validate should be called on it. This will perform
// some initialization necessary such as setting up a root node. This function
// doesn't perform the Validate automatically in case the caller wants to
// modify the graph.
//
// This dependency graph shows the correct order that any resources need // This dependency graph shows the correct order that any resources need
// to be operated on. // to be operated on.
// //
// The Meta field of a graph Noun can contain one of the follow types. A // The Meta field of a graph Noun can contain one of the follow types. A
// description is next to each type to explain what it is. // description is next to each type to explain what it is.
// //
// *config.Resource - A resource itself // *GraphNodeResource - A resource. See the documentation of this
// *config.ProviderConfig - The configuration for a provider that // struct for more details.
// should be initialized. // *GraphNodeResourceProvider - A resource provider that needs to be
// *ResourceState - An orphan resource that we only have the state of // configured at this point.
// and no more configuration.
// //
func Graph(c *config.Config, s *State) *depgraph.Graph { func Graph(c *config.Config, s *State) *depgraph.Graph {
g := new(depgraph.Graph) g := new(depgraph.Graph)
@ -56,7 +77,10 @@ func graphAddConfigResources(g *depgraph.Graph, c *config.Config) {
for _, r := range c.Resources { for _, r := range c.Resources {
noun := &depgraph.Noun{ noun := &depgraph.Noun{
Name: r.Id(), Name: r.Id(),
Meta: r, Meta: &GraphNodeResource{
Type: r.Type,
Config: r,
},
} }
nouns[noun.Name] = noun nouns[noun.Name] = noun
} }
@ -77,7 +101,13 @@ func graphAddOrphans(g *depgraph.Graph, c *config.Config, s *State) {
rs := s.Resources[k] rs := s.Resources[k]
noun := &depgraph.Noun{ noun := &depgraph.Noun{
Name: k, Name: k,
Meta: rs, Meta: &GraphNodeResource{
Type: rs.Type,
Orphan: true,
Resource: &Resource{
State: rs,
},
},
} }
g.Nouns = append(g.Nouns, noun) g.Nouns = append(g.Nouns, noun)
} }
@ -89,18 +119,10 @@ func graphAddProviderConfigs(g *depgraph.Graph, c *config.Config) {
nounsList := make([]*depgraph.Noun, 0, 2) nounsList := make([]*depgraph.Noun, 0, 2)
pcNouns := make(map[string]*depgraph.Noun) pcNouns := make(map[string]*depgraph.Noun)
for _, noun := range g.Nouns { for _, noun := range g.Nouns {
var rtype string resourceNode := noun.Meta.(*GraphNodeResource)
switch m := noun.Meta.(type) {
case *config.Resource:
rtype = m.Type
case *ResourceState:
rtype = m.Type
default:
continue
}
// Look up the provider config for this resource // Look up the provider config for this resource
pcName := config.ProviderConfigName(rtype, c.ProviderConfigs) pcName := config.ProviderConfigName(resourceNode.Type, c.ProviderConfigs)
if pcName == "" { if pcName == "" {
continue continue
} }
@ -110,12 +132,20 @@ func graphAddProviderConfigs(g *depgraph.Graph, c *config.Config) {
if !ok { if !ok {
pcNoun = &depgraph.Noun{ pcNoun = &depgraph.Noun{
Name: fmt.Sprintf("provider.%s", pcName), Name: fmt.Sprintf("provider.%s", pcName),
Meta: c.ProviderConfigs[pcName], Meta: &GraphNodeResourceProvider{
ID: pcName,
Config: c.ProviderConfigs[pcName],
},
} }
pcNouns[pcName] = pcNoun pcNouns[pcName] = pcNoun
nounsList = append(nounsList, pcNoun) nounsList = append(nounsList, pcNoun)
} }
// Set the resource provider ID for this noun so we can look it
// up later easily.
resourceNode.ResourceProviderID = pcName
// Add the provider configuration noun as a dependency
dep := &depgraph.Dependency{ dep := &depgraph.Dependency{
Name: pcName, Name: pcName,
Source: noun, Source: noun,
@ -148,10 +178,12 @@ func graphAddVariableDeps(g *depgraph.Graph) {
for _, n := range g.Nouns { for _, n := range g.Nouns {
var vars map[string]config.InterpolatedVariable var vars map[string]config.InterpolatedVariable
switch m := n.Meta.(type) { switch m := n.Meta.(type) {
case *config.Resource: case *GraphNodeResource:
vars = m.RawConfig.Variables if !m.Orphan {
case *config.ProviderConfig: vars = m.Config.RawConfig.Variables
vars = m.RawConfig.Variables }
case *GraphNodeResourceProvider:
vars = m.Config.RawConfig.Variables
default: default:
continue continue
} }

View File

@ -78,13 +78,6 @@ func New(c *Config) (*Terraform, error) {
errs = append(errs, tpErrs...) errs = append(errs, tpErrs...)
} }
} }
// Build the resource graph
graph := c.Config.Graph()
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
@ -100,13 +93,13 @@ func New(c *Config) (*Terraform, error) {
} }
func (t *Terraform) Apply(p *Plan) (*State, error) { func (t *Terraform) Apply(p *Plan) (*State, error) {
graph := t.config.Graph() graph, err := t.Graph(p.Config, p.State)
if err := graph.Validate(); err != nil { if err != nil {
panic(fmt.Sprintf("Graph invalid after prior validation: %s", err)) return nil, err
} }
result := new(State) result := new(State)
err := graph.Walk(t.applyWalkFn(p, result)) err = graph.Walk(t.applyWalkFn(p, result))
return result, err return result, err
} }
@ -116,22 +109,27 @@ func (t *Terraform) Apply(p *Plan) (*State, error) {
// The resulting graph may have more resources than the configuration, because // The resulting graph may have more resources than the configuration, because
// it can contain resources in the state file that need to be modified. // it can contain resources in the state file that need to be modified.
func (t *Terraform) Graph(c *config.Config, s *State) (*depgraph.Graph, error) { func (t *Terraform) Graph(c *config.Config, s *State) (*depgraph.Graph, error) {
// Get the basic graph with the raw metadata
g := Graph(c, s) g := Graph(c, s)
if err := g.Validate(); err != nil { if err := g.Validate(); err != nil {
return nil, err return nil, err
} }
// Next, we want to go through the graph and make sure that we
// map a provider to each of the resources.
return g, nil return g, nil
} }
func (t *Terraform) Plan(s *State) (*Plan, error) { func (t *Terraform) Plan(s *State) (*Plan, error) {
graph := t.config.Graph() // TODO: add config param
if err := graph.Validate(); err != nil { graph, err := t.Graph(nil, s)
panic(fmt.Sprintf("Graph invalid after prior validation: %s", err)) if err != nil {
return nil, err
} }
result := new(Plan) result := new(Plan)
err := graph.Walk(t.planWalkFn(s, result)) err = graph.Walk(t.planWalkFn(s, result))
if err != nil { if err != nil {
return nil, err return nil, err
} }