terraform: fill in the graph with the providers

This commit is contained in:
Mitchell Hashimoto 2014-06-25 14:47:38 -07:00
parent cdab89d7c1
commit 9d4f7b71c4
3 changed files with 114 additions and 14 deletions

View File

@ -2,6 +2,7 @@ package terraform
import (
"fmt"
"sort"
"strings"
"github.com/hashicorp/terraform/config"
@ -24,9 +25,10 @@ type GraphNodeResource struct {
// GraphNodeResourceProvider is a node type in the graph that represents
// the configuration for a resource provider.
type GraphNodeResourceProvider struct {
ID string
Providers []ResourceProvider
Config *config.ProviderConfig
ID string
Providers map[string]ResourceProvider
ProviderKeys []string
Config *config.ProviderConfig
}
// Graph builds a dependency graph for the given configuration and state.
@ -71,6 +73,25 @@ func Graph(c *config.Config, s *State) *depgraph.Graph {
return g
}
func GraphFull(g *depgraph.Graph, ps map[string]ResourceProviderFactory) error {
// Add missing providers from the mapping
if err := graphAddMissingResourceProviders(g, ps); err != nil {
return err
}
// Initialize all the providers
if err := graphInitResourceProviders(g, ps); err != nil {
return err
}
// Map the providers to resources
if err := graphMapResourceProviders(g); err != nil {
return err
}
return nil
}
// configGraph turns a configuration structure into a dependency graph.
func graphAddConfigResources(g *depgraph.Graph, c *config.Config) {
// This tracks all the resource nouns
@ -97,6 +118,65 @@ func graphAddConfigResources(g *depgraph.Graph, c *config.Config) {
g.Nouns = append(g.Nouns, nounsList...)
}
// graphAddMissingResourceProviders adds GraphNodeResourceProvider nodes for
// the resources that do not have an explicit resource provider specified
// because no provider configuration was given.
func graphAddMissingResourceProviders(
g *depgraph.Graph,
ps map[string]ResourceProviderFactory) error {
var errs []error
for _, n := range g.Nouns {
rn, ok := n.Meta.(*GraphNodeResource)
if !ok {
continue
}
if rn.ResourceProviderID != "" {
continue
}
prefixes := matchingPrefixes(rn.Type, ps)
if len(prefixes) == 0 {
errs = append(errs, fmt.Errorf(
"No matching provider for type: %s",
rn.Type))
continue
}
// The resource provider ID is simply the shortest matching
// prefix, since that'll give us the most resource providers
// to choose from.
rn.ResourceProviderID = prefixes[len(prefixes)-1]
// If we don't have a matching noun for this yet, insert it.
pn := g.Noun(fmt.Sprintf("provider.%s", rn.ResourceProviderID))
if pn == nil {
pn = &depgraph.Noun{
Name: fmt.Sprintf("provider.%s", rn.ResourceProviderID),
Meta: &GraphNodeResourceProvider{
ID: rn.ResourceProviderID,
Config: nil,
},
}
g.Nouns = append(g.Nouns, pn)
}
// Add the provider configuration noun as a dependency
dep := &depgraph.Dependency{
Name: pn.Name,
Source: n,
Target: pn,
}
n.Deps = append(n.Deps, dep)
}
if len(errs) > 0 {
return &MultiError{Errors: errs}
}
return nil
}
// graphAddOrphans adds the orphans to the graph.
func graphAddOrphans(g *depgraph.Graph, c *config.Config, s *State) {
for _, k := range s.Orphans(c) {
@ -249,6 +329,8 @@ func graphInitResourceProviders(
// Go through each prefix and instantiate if necessary, then
// verify if this provider is of use to us or not.
rn.Providers = make(map[string]ResourceProvider)
rn.ProviderKeys = prefixes
for _, prefix := range prefixes {
p, err := ps[prefix]()
if err != nil {
@ -263,7 +345,7 @@ func graphInitResourceProviders(
continue
}
rn.Providers = append(rn.Providers, p)
rn.Providers[prefix] = p
}
// If we never found a provider, then error and continue
@ -317,7 +399,13 @@ func graphMapResourceProviders(g *depgraph.Graph) error {
}
var provider ResourceProvider
for _, rp := range rpn.Providers {
for _, k := range rpn.ProviderKeys {
// Only try this provider if it has the right prefix
if !strings.HasPrefix(rn.Type, k) {
continue
}
rp := rpn.Providers[k]
if ProviderSatisfies(rp, rn.Type) {
provider = rp
break
@ -356,7 +444,24 @@ func matchingPrefixes(
}
}
// TODO(mitchellh): Order by longest prefix first
// Sort by longest first
sort.Sort(stringLenSort(result))
return result
}
// stringLenSort implements sort.Interface and sorts strings in increasing
// length order. i.e. "a", "aa", "aaa"
type stringLenSort []string
func (s stringLenSort) Len() int {
return len(s)
}
func (s stringLenSort) Less(i, j int) bool {
return len(s[i]) < len(s[j])
}
func (s stringLenSort) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

View File

@ -112,13 +112,8 @@ func (t *Terraform) Graph(c *config.Config, s *State) (*depgraph.Graph, error) {
return nil, err
}
// Initialize all the providers
if err := graphInitResourceProviders(g, t.providers); err != nil {
return nil, err
}
// Map the providers to resources
if err := graphMapResourceProviders(g); err != nil {
// Fill the graph with the providers
if err := GraphFull(g, t.providers); err != nil {
return nil, err
}

View File

@ -7,7 +7,7 @@ provider "aws" {
foo = "${openstack_floating_ip.random.value}"
}
#resource "openstack_floating_ip" "random" {}
resource "openstack_floating_ip" "random" {}
resource "aws_security_group" "firewall" {}