diff --git a/terraform/graph.go b/terraform/graph.go index 16e16d971c..1e1fb25be2 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -11,19 +11,40 @@ import ( // graph. This node is just a placemarker and has no associated functionality. 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. // +// 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 // to be operated on. // // 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. // -// *config.Resource - A resource itself -// *config.ProviderConfig - The configuration for a provider that -// should be initialized. -// *ResourceState - An orphan resource that we only have the state of -// and no more configuration. +// *GraphNodeResource - A resource. See the documentation of this +// struct for more details. +// *GraphNodeResourceProvider - A resource provider that needs to be +// configured at this point. // func Graph(c *config.Config, s *State) *depgraph.Graph { g := new(depgraph.Graph) @@ -56,7 +77,10 @@ func graphAddConfigResources(g *depgraph.Graph, c *config.Config) { for _, r := range c.Resources { noun := &depgraph.Noun{ Name: r.Id(), - Meta: r, + Meta: &GraphNodeResource{ + Type: r.Type, + Config: r, + }, } nouns[noun.Name] = noun } @@ -77,7 +101,13 @@ func graphAddOrphans(g *depgraph.Graph, c *config.Config, s *State) { rs := s.Resources[k] noun := &depgraph.Noun{ Name: k, - Meta: rs, + Meta: &GraphNodeResource{ + Type: rs.Type, + Orphan: true, + Resource: &Resource{ + State: rs, + }, + }, } g.Nouns = append(g.Nouns, noun) } @@ -89,18 +119,10 @@ func graphAddProviderConfigs(g *depgraph.Graph, c *config.Config) { nounsList := make([]*depgraph.Noun, 0, 2) pcNouns := make(map[string]*depgraph.Noun) for _, noun := range g.Nouns { - var rtype string - switch m := noun.Meta.(type) { - case *config.Resource: - rtype = m.Type - case *ResourceState: - rtype = m.Type - default: - continue - } + resourceNode := noun.Meta.(*GraphNodeResource) // Look up the provider config for this resource - pcName := config.ProviderConfigName(rtype, c.ProviderConfigs) + pcName := config.ProviderConfigName(resourceNode.Type, c.ProviderConfigs) if pcName == "" { continue } @@ -110,12 +132,20 @@ func graphAddProviderConfigs(g *depgraph.Graph, c *config.Config) { if !ok { pcNoun = &depgraph.Noun{ Name: fmt.Sprintf("provider.%s", pcName), - Meta: c.ProviderConfigs[pcName], + Meta: &GraphNodeResourceProvider{ + ID: pcName, + Config: c.ProviderConfigs[pcName], + }, } pcNouns[pcName] = 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{ Name: pcName, Source: noun, @@ -148,10 +178,12 @@ func graphAddVariableDeps(g *depgraph.Graph) { for _, n := range g.Nouns { var vars map[string]config.InterpolatedVariable switch m := n.Meta.(type) { - case *config.Resource: - vars = m.RawConfig.Variables - case *config.ProviderConfig: - vars = m.RawConfig.Variables + case *GraphNodeResource: + if !m.Orphan { + vars = m.Config.RawConfig.Variables + } + case *GraphNodeResourceProvider: + vars = m.Config.RawConfig.Variables default: continue } diff --git a/terraform/terraform.go b/terraform/terraform.go index f5ee1060c5..0cb58e309d 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -78,13 +78,6 @@ func New(c *Config) (*Terraform, error) { 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 @@ -100,13 +93,13 @@ func New(c *Config) (*Terraform, error) { } func (t *Terraform) Apply(p *Plan) (*State, error) { - graph := t.config.Graph() - if err := graph.Validate(); err != nil { - panic(fmt.Sprintf("Graph invalid after prior validation: %s", err)) + graph, err := t.Graph(p.Config, p.State) + if err != nil { + return nil, err } result := new(State) - err := graph.Walk(t.applyWalkFn(p, result)) + err = graph.Walk(t.applyWalkFn(p, result)) 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 // 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) { + // Get the basic graph with the raw metadata g := Graph(c, s) if err := g.Validate(); err != nil { 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 } func (t *Terraform) Plan(s *State) (*Plan, error) { - graph := t.config.Graph() - if err := graph.Validate(); err != nil { - panic(fmt.Sprintf("Graph invalid after prior validation: %s", err)) + // TODO: add config param + graph, err := t.Graph(nil, s) + if err != nil { + return nil, err } result := new(Plan) - err := graph.Walk(t.planWalkFn(s, result)) + err = graph.Walk(t.planWalkFn(s, result)) if err != nil { return nil, err }