2014-05-28 15:56:43 -05:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
2014-06-03 17:08:00 -05:00
|
|
|
"fmt"
|
2014-06-05 04:32:10 -05:00
|
|
|
"sync"
|
2014-06-03 17:08:00 -05:00
|
|
|
|
2014-05-28 15:56:43 -05:00
|
|
|
"github.com/hashicorp/terraform/config"
|
2014-06-03 19:14:19 -05:00
|
|
|
"github.com/hashicorp/terraform/depgraph"
|
2014-05-28 15:56:43 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Terraform is the primary structure that is used to interact with
|
|
|
|
// Terraform from code, and can perform operations such as returning
|
|
|
|
// all resources, a resource tree, a specific resource, etc.
|
|
|
|
type Terraform struct {
|
2014-06-03 17:58:24 -05:00
|
|
|
config *config.Config
|
2014-06-03 19:14:19 -05:00
|
|
|
graph *depgraph.Graph
|
2014-06-05 21:56:35 -05:00
|
|
|
mapping map[*config.Resource]*terraformProvider
|
2014-06-03 17:58:24 -05:00
|
|
|
variables map[string]string
|
2014-05-28 15:56:43 -05:00
|
|
|
}
|
|
|
|
|
2014-06-05 21:56:35 -05:00
|
|
|
// terraformProvider contains internal state information about a resource
|
|
|
|
// provider for Terraform.
|
|
|
|
type terraformProvider struct {
|
2014-06-06 02:20:23 -05:00
|
|
|
Provider ResourceProvider
|
|
|
|
Config *config.ProviderConfig
|
2014-06-05 21:56:35 -05:00
|
|
|
|
2014-06-06 02:20:23 -05:00
|
|
|
sync.Once
|
2014-06-05 21:56:35 -05:00
|
|
|
}
|
|
|
|
|
2014-05-28 15:56:43 -05:00
|
|
|
// Config is the configuration that must be given to instantiate
|
|
|
|
// a Terraform structure.
|
|
|
|
type Config struct {
|
|
|
|
Config *config.Config
|
|
|
|
Providers map[string]ResourceProviderFactory
|
|
|
|
Variables map[string]string
|
2014-06-05 14:09:25 -05:00
|
|
|
|
|
|
|
computedPlaceholder string
|
2014-05-28 15:56:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new Terraform structure, initializes resource providers
|
|
|
|
// for the given configuration, etc.
|
|
|
|
//
|
|
|
|
// Semantic checks of the entire configuration structure are done at this
|
|
|
|
// time, as well as richer checks such as verifying that the resource providers
|
|
|
|
// can be properly initialized, can be configured, etc.
|
|
|
|
func New(c *Config) (*Terraform, error) {
|
2014-06-03 18:11:02 -05:00
|
|
|
var errs []error
|
|
|
|
|
2014-06-05 14:09:25 -05:00
|
|
|
// Calculate the computed key placeholder
|
|
|
|
c.computedPlaceholder = "tf_computed_placeholder"
|
|
|
|
|
2014-06-03 17:56:43 -05:00
|
|
|
// Validate that all required variables have values
|
2014-06-05 13:53:07 -05:00
|
|
|
if err := smcVariables(c); err != nil {
|
|
|
|
errs = append(errs, err...)
|
2014-06-03 17:56:43 -05:00
|
|
|
}
|
|
|
|
|
2014-06-05 13:53:07 -05:00
|
|
|
// Match all the resources with a provider and initialize the providers
|
|
|
|
mapping, err := smcProviders(c)
|
|
|
|
if err != nil {
|
|
|
|
errs = append(errs, err...)
|
2014-06-03 17:08:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-03 19:14:19 -05:00
|
|
|
// Build the resource graph
|
2014-06-05 14:55:54 -05:00
|
|
|
graph := c.Config.Graph()
|
2014-06-03 19:14:19 -05:00
|
|
|
if err := graph.Validate(); err != nil {
|
|
|
|
errs = append(errs, fmt.Errorf(
|
|
|
|
"Resource graph has an error: %s", err))
|
|
|
|
}
|
|
|
|
|
2014-06-03 18:11:02 -05:00
|
|
|
// If we accumulated any errors, then return them all
|
|
|
|
if len(errs) > 0 {
|
|
|
|
return nil, &MultiError{Errors: errs}
|
|
|
|
}
|
|
|
|
|
2014-06-03 17:08:00 -05:00
|
|
|
return &Terraform{
|
2014-06-03 17:58:24 -05:00
|
|
|
config: c.Config,
|
2014-06-03 19:14:19 -05:00
|
|
|
graph: graph,
|
2014-06-03 17:58:24 -05:00
|
|
|
mapping: mapping,
|
|
|
|
variables: c.Variables,
|
2014-06-03 17:08:00 -05:00
|
|
|
}, nil
|
2014-05-28 15:56:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terraform) Apply(*State, *Diff) (*State, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2014-06-05 04:32:10 -05:00
|
|
|
func (t *Terraform) Diff(s *State) (*Diff, error) {
|
|
|
|
result := new(Diff)
|
|
|
|
err := t.graph.Walk(t.diffWalkFn(s, result))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
2014-05-28 15:56:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Terraform) Refresh(*State) (*State, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2014-06-03 17:08:00 -05:00
|
|
|
|
2014-06-05 04:32:10 -05:00
|
|
|
func (t *Terraform) diffWalkFn(
|
|
|
|
state *State, result *Diff) depgraph.WalkFunc {
|
2014-06-05 09:27:01 -05:00
|
|
|
var l sync.RWMutex
|
|
|
|
|
|
|
|
// Initialize the result diff so we can write to it
|
|
|
|
result.init()
|
|
|
|
|
2014-06-05 13:08:27 -05:00
|
|
|
// Initialize the variables for application
|
|
|
|
vars := make(map[string]string)
|
|
|
|
for k, v := range t.variables {
|
|
|
|
vars[k] = v
|
|
|
|
}
|
|
|
|
|
2014-06-05 04:32:10 -05:00
|
|
|
return func(n *depgraph.Noun) error {
|
|
|
|
// If it is the root node, ignore
|
2014-06-05 14:59:55 -05:00
|
|
|
if n.Meta == nil {
|
2014-06-05 04:32:10 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-06-05 15:05:26 -05:00
|
|
|
switch n.Meta.(type) {
|
|
|
|
case *config.ProviderConfig:
|
2014-06-05 22:03:16 -05:00
|
|
|
// Ignore, we don't treat this any differently since we always
|
|
|
|
// initialize the provider on first use and use a lock to make
|
|
|
|
// sure we only do this once.
|
2014-06-05 15:05:26 -05:00
|
|
|
return nil
|
|
|
|
case *config.Resource:
|
|
|
|
// Continue
|
|
|
|
}
|
|
|
|
|
2014-06-05 04:32:10 -05:00
|
|
|
r := n.Meta.(*config.Resource)
|
|
|
|
p := t.mapping[r]
|
|
|
|
if p == nil {
|
|
|
|
panic(fmt.Sprintf("No provider for resource: %s", r.Id()))
|
|
|
|
}
|
|
|
|
|
2014-06-06 02:20:23 -05:00
|
|
|
// Initialize the provider if we haven't already
|
2014-06-06 02:28:57 -05:00
|
|
|
if err := p.init(vars); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-06-05 21:56:35 -05:00
|
|
|
|
2014-06-05 09:27:01 -05:00
|
|
|
l.RLock()
|
2014-06-05 13:12:10 -05:00
|
|
|
var rs *ResourceState
|
|
|
|
if state != nil {
|
|
|
|
rs = state.resources[r.Id()]
|
|
|
|
}
|
2014-06-05 13:08:27 -05:00
|
|
|
if len(vars) > 0 {
|
2014-06-12 19:51:38 -05:00
|
|
|
if err := r.RawConfig.Interpolate(vars); err != nil {
|
|
|
|
panic(fmt.Sprintf("Interpolate error: %s", err))
|
|
|
|
}
|
2014-06-05 09:27:01 -05:00
|
|
|
}
|
|
|
|
l.RUnlock()
|
|
|
|
|
2014-06-12 19:59:59 -05:00
|
|
|
diff, err := p.Provider.Diff(rs, &ResourceConfig{
|
2014-06-12 20:11:21 -05:00
|
|
|
ComputedKeys: r.RawConfig.UnknownKeys(),
|
|
|
|
Raw: r.RawConfig.Config(),
|
2014-06-12 19:59:59 -05:00
|
|
|
})
|
2014-06-05 04:32:10 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there were no diff items, return right away
|
2014-06-10 12:50:23 -05:00
|
|
|
if diff == nil || len(diff.Attributes) == 0 {
|
2014-06-05 04:32:10 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-06-05 09:27:01 -05:00
|
|
|
// Acquire a lock since this function is called in parallel
|
|
|
|
l.Lock()
|
|
|
|
defer l.Unlock()
|
|
|
|
|
|
|
|
// Update the resulting diff
|
2014-06-10 13:22:32 -05:00
|
|
|
result.Resources[r.Id()] = diff
|
2014-06-05 04:32:10 -05:00
|
|
|
|
2014-06-05 13:08:27 -05:00
|
|
|
// Determine the new state and update variables
|
2014-06-12 19:51:38 -05:00
|
|
|
rs = rs.MergeDiff(diff.Attributes)
|
2014-06-05 13:08:27 -05:00
|
|
|
for ak, av := range rs.Attributes {
|
|
|
|
vars[fmt.Sprintf("%s.%s", r.Id(), ak)] = av
|
|
|
|
}
|
2014-06-05 09:27:01 -05:00
|
|
|
|
2014-06-05 04:32:10 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2014-06-06 02:20:23 -05:00
|
|
|
|
2014-06-06 02:28:57 -05:00
|
|
|
func (t *terraformProvider) init(vars map[string]string) (err error) {
|
2014-06-06 02:20:23 -05:00
|
|
|
t.Once.Do(func() {
|
2014-06-12 20:11:21 -05:00
|
|
|
var rc *ResourceConfig
|
2014-06-06 02:20:23 -05:00
|
|
|
if t.Config != nil {
|
2014-06-12 19:51:38 -05:00
|
|
|
if err := t.Config.RawConfig.Interpolate(vars); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2014-06-12 20:11:21 -05:00
|
|
|
rc = &ResourceConfig{
|
|
|
|
ComputedKeys: t.Config.RawConfig.UnknownKeys(),
|
|
|
|
Raw: t.Config.RawConfig.Config(),
|
|
|
|
}
|
2014-06-06 02:20:23 -05:00
|
|
|
}
|
|
|
|
|
2014-06-12 20:11:21 -05:00
|
|
|
err = t.Provider.Configure(rc)
|
2014-06-06 02:20:23 -05:00
|
|
|
})
|
|
|
|
|
2014-06-06 02:28:57 -05:00
|
|
|
return
|
2014-06-06 02:20:23 -05:00
|
|
|
}
|