terraform/config/config.go

207 lines
4.9 KiB
Go
Raw Normal View History

2014-05-24 20:36:22 +02:00
// The config package is responsible for loading and validating the
// configuration.
2014-05-23 01:56:28 +02:00
package config
import (
2014-05-24 22:57:51 +02:00
"fmt"
"strings"
2014-05-24 22:57:51 +02:00
"github.com/hashicorp/terraform/depgraph"
"github.com/mitchellh/reflectwalk"
)
// 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"
2014-05-23 01:56:28 +02:00
// Config is the configuration that comes from loading a collection
// of Terraform templates.
type Config struct {
2014-05-26 03:05:18 +02:00
ProviderConfigs map[string]*ProviderConfig
Resources []*Resource
Variables map[string]*Variable
2014-05-26 03:05:18 +02:00
}
// ProviderConfig is the configuration for a resource provider.
//
// For example, Terraform needs to set the AWS access keys for the AWS
// resource provider.
type ProviderConfig struct {
Config map[string]interface{}
Variables map[string]InterpolatedVariable
2014-05-23 01:56:28 +02:00
}
// A resource represents a single Terraform resource in the configuration.
// A Terraform resource is something that represents some component that
// can be created and managed, and has some properties associated with it.
2014-05-23 01:56:28 +02:00
type Resource struct {
Name string
Type string
Config map[string]interface{}
Variables map[string]InterpolatedVariable
2014-05-23 01:56:28 +02:00
}
// Variable is a variable defined within the configuration.
2014-05-23 01:56:28 +02:00
type Variable struct {
Default string
Description string
defaultSet bool
2014-05-23 01:56:28 +02:00
}
// An InterpolatedVariable is a variable that is embedded within a string
// in the configuration, such as "hello ${world}" (world in this case is
// an interpolated variable).
//
// These variables can come from a variety of sources, represented by
// implementations of this interface.
type InterpolatedVariable interface {
FullKey() string
}
// A ResourceVariable is a variable that is referencing the field
// of a resource, such as "${aws_instance.foo.ami}"
type ResourceVariable struct {
Type string
Name string
Field string
key string
}
// A UserVariable is a variable that is referencing a user variable
// that is inputted from outside the configuration. This looks like
// "${var.foo}"
type UserVariable struct {
Name string
2014-05-24 21:51:31 +02:00
key string
}
2014-05-24 22:57:51 +02:00
// A unique identifier for this resource.
func (r *Resource) Id() string {
return fmt.Sprintf("%s.%s", r.Type, r.Name)
}
2014-06-05 21:21:05 +02:00
// ProviderConfigName returns the name of the provider configuration in
// the given mapping that maps to the proper provider configuration
// for this resource.
func (r *Resource) ProviderConfigName(pcs map[string]*ProviderConfig) string {
lk := ""
for k, _ := range pcs {
if strings.HasPrefix(r.Type, k) && len(k) > len(lk) {
lk = k
}
}
return lk
}
// ReplaceVariables replaces the variables in the configuration
// with the given values.
//
// This replacement is not in place. Instead, this function will
// return a new resource with the variables replaced.
func (r *Resource) ReplaceVariables(vs map[string]string) *Resource {
result := *r
w := &variableReplaceWalker{
Values: vs,
}
if err := reflectwalk.Walk(result.Config, w); err != nil {
panic(err)
}
return &result
}
2014-05-24 22:57:51 +02:00
// ResourceGraph returns a dependency graph of the resources from this
// Terraform configuration.
func (c *Config) ResourceGraph() *depgraph.Graph {
2014-05-24 22:58:33 +02:00
nouns := make(map[string]*depgraph.Noun)
for _, r := range c.Resources {
noun := &depgraph.Noun{
2014-05-24 22:57:51 +02:00
Name: r.Id(),
Meta: r,
}
nouns[noun.Name] = noun
}
for _, noun := range nouns {
r := noun.Meta.(*Resource)
for _, v := range r.Variables {
// Only resource variables impose dependencies
rv, ok := v.(*ResourceVariable)
if !ok {
continue
}
// Build the dependency
dep := &depgraph.Dependency{
Name: rv.ResourceId(),
Source: noun,
Target: nouns[rv.ResourceId()],
}
noun.Deps = append(noun.Deps, dep)
}
}
// Create the list of nouns that the depgraph.Graph struct expects
nounsList := make([]*depgraph.Noun, 0, len(nouns))
for _, n := range nouns {
nounsList = append(nounsList, n)
}
// Create a root that just depends on everything else finishing.
root := &depgraph.Noun{Name: ResourceGraphRoot}
2014-05-24 22:57:51 +02:00
for _, n := range nounsList {
root.Deps = append(root.Deps, &depgraph.Dependency{
Name: n.Name,
Source: root,
Target: n,
})
}
nounsList = append(nounsList, root)
return &depgraph.Graph{
Name: "resources",
Nouns: nounsList,
}
}
// Required tests whether a variable is required or not.
func (v *Variable) Required() bool {
return !v.defaultSet
}
func NewResourceVariable(key string) (*ResourceVariable, error) {
parts := strings.SplitN(key, ".", 3)
return &ResourceVariable{
Type: parts[0],
Name: parts[1],
Field: parts[2],
key: key,
}, nil
}
2014-05-24 22:57:51 +02:00
func (v *ResourceVariable) ResourceId() string {
return fmt.Sprintf("%s.%s", v.Type, v.Name)
}
func (v *ResourceVariable) FullKey() string {
return v.key
}
func NewUserVariable(key string) (*UserVariable, error) {
name := key[len("var."):]
return &UserVariable{
key: key,
Name: name,
}, nil
}
func (v *UserVariable) FullKey() string {
return v.key
}