terraform: Refresh tests
This commit is contained in:
parent
abe205fc27
commit
8a44ca984e
|
@ -112,7 +112,9 @@ func graphAddConfigResources(g *depgraph.Graph, c *config.Config) {
|
||||||
Meta: &GraphNodeResource{
|
Meta: &GraphNodeResource{
|
||||||
Type: r.Type,
|
Type: r.Type,
|
||||||
Config: r,
|
Config: r,
|
||||||
Resource: new(Resource),
|
Resource: &Resource{
|
||||||
|
Id: r.Id(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
nouns[noun.Name] = noun
|
nouns[noun.Name] = noun
|
||||||
|
@ -197,6 +199,7 @@ func graphAddOrphans(g *depgraph.Graph, c *config.Config, s *State) {
|
||||||
Type: rs.Type,
|
Type: rs.Type,
|
||||||
Orphan: true,
|
Orphan: true,
|
||||||
Resource: &Resource{
|
Resource: &Resource{
|
||||||
|
Id: k,
|
||||||
State: rs,
|
State: rs,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,6 +2,7 @@ package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
|
@ -13,7 +14,6 @@ import (
|
||||||
// all resources, a resource tree, a specific resource, etc.
|
// all resources, a resource tree, a specific resource, etc.
|
||||||
type Terraform struct {
|
type Terraform struct {
|
||||||
providers map[string]ResourceProviderFactory
|
providers map[string]ResourceProviderFactory
|
||||||
variables map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// terraformProvider contains internal state information about a resource
|
// terraformProvider contains internal state information about a resource
|
||||||
|
@ -85,7 +85,6 @@ func New(c *Config) (*Terraform, error) {
|
||||||
|
|
||||||
return &Terraform{
|
return &Terraform{
|
||||||
providers: c.Providers,
|
providers: c.Providers,
|
||||||
variables: c.Variables,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +116,11 @@ func (t *Terraform) Graph(c *config.Config, s *State) (*depgraph.Graph, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the graph so that it can setup a root and such
|
||||||
|
if err := g.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,15 +142,42 @@ func (t *Terraform) Plan(s *State) (*Plan, error) {
|
||||||
|
|
||||||
// Refresh goes through all the resources in the state and refreshes them
|
// Refresh goes through all the resources in the state and refreshes them
|
||||||
// to their latest status.
|
// to their latest status.
|
||||||
func (t *Terraform) Refresh(c *config.Config, s *State) (*State, error) {
|
func (t *Terraform) Refresh(
|
||||||
_, err := t.Graph(c, s)
|
c *config.Config, s *State, vs map[string]string) (*State, error) {
|
||||||
|
g, err := t.Graph(c, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := new(State)
|
return t.refresh(g, vs)
|
||||||
//err = graph.Walk(t.refreshWalkFn(s, result))
|
}
|
||||||
return result, err
|
|
||||||
|
func (t *Terraform) refresh(g *depgraph.Graph, vars map[string]string) (*State, error) {
|
||||||
|
s := new(State)
|
||||||
|
err := g.Walk(t.refreshWalkFn(vars, s))
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terraform) refreshWalkFn(vars map[string]string, result *State) depgraph.WalkFunc {
|
||||||
|
var l sync.Mutex
|
||||||
|
|
||||||
|
// Initialize the result so we don't have to nil check everywhere
|
||||||
|
result.init()
|
||||||
|
|
||||||
|
cb := func(r *Resource) (map[string]string, error) {
|
||||||
|
rs, err := r.Provider.Refresh(r.State)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Lock()
|
||||||
|
result.Resources[r.Id] = rs
|
||||||
|
l.Unlock()
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.genericWalkFn(vars, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terraform) applyWalkFn(
|
func (t *Terraform) applyWalkFn(
|
||||||
|
@ -209,7 +240,7 @@ func (t *Terraform) applyWalkFn(
|
||||||
return vars, err
|
return vars, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.genericWalkFn(p.State, p.Diff, p.Vars, cb)
|
return t.genericWalkFn(p.Vars, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terraform) planWalkFn(
|
func (t *Terraform) planWalkFn(
|
||||||
|
@ -223,10 +254,12 @@ func (t *Terraform) planWalkFn(
|
||||||
//result.Config = t.config
|
//result.Config = t.config
|
||||||
|
|
||||||
// Copy the variables
|
// Copy the variables
|
||||||
|
/*
|
||||||
result.Vars = make(map[string]string)
|
result.Vars = make(map[string]string)
|
||||||
for k, v := range t.variables {
|
for k, v := range t.variables {
|
||||||
result.Vars[k] = v
|
result.Vars[k] = v
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
cb := func(r *Resource) (map[string]string, error) {
|
cb := func(r *Resource) (map[string]string, error) {
|
||||||
// Refresh the state so we're working with the latest resource info
|
// Refresh the state so we're working with the latest resource info
|
||||||
|
@ -271,15 +304,13 @@ func (t *Terraform) planWalkFn(
|
||||||
return vars, nil
|
return vars, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.genericWalkFn(state, nil, t.variables, cb)
|
return t.genericWalkFn(nil, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terraform) genericWalkFn(
|
func (t *Terraform) genericWalkFn(
|
||||||
state *State,
|
|
||||||
diff *Diff,
|
|
||||||
invars map[string]string,
|
invars map[string]string,
|
||||||
cb genericWalkFunc) depgraph.WalkFunc {
|
cb genericWalkFunc) depgraph.WalkFunc {
|
||||||
//var l sync.Mutex
|
var l sync.Mutex
|
||||||
|
|
||||||
// Initialize the variables for application
|
// Initialize the variables for application
|
||||||
vars := make(map[string]string)
|
vars := make(map[string]string)
|
||||||
|
@ -288,66 +319,45 @@ func (t *Terraform) genericWalkFn(
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(n *depgraph.Noun) error {
|
return func(n *depgraph.Noun) error {
|
||||||
/*
|
|
||||||
// If it is the root node, ignore
|
// If it is the root node, ignore
|
||||||
if n.Meta == nil {
|
if n.Name == GraphRootNode {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch n.Meta.(type) {
|
switch m := n.Meta.(type) {
|
||||||
case *config.ProviderConfig:
|
case *GraphNodeResource:
|
||||||
// Ignore, we don't treat this any differently since we always
|
case *GraphNodeResourceProvider:
|
||||||
// initialize the provider on first use and use a lock to make
|
var rc *ResourceConfig
|
||||||
// sure we only do this once.
|
if m.Config != nil {
|
||||||
return nil
|
if err := m.Config.RawConfig.Interpolate(vars); err != nil {
|
||||||
case *config.Resource:
|
panic(err)
|
||||||
// Continue
|
}
|
||||||
|
rc = NewResourceConfig(m.Config.RawConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := n.Meta.(*config.Resource)
|
for k, p := range m.Providers {
|
||||||
p := t.mapping[r]
|
log.Printf("Configuring provider: %s", k)
|
||||||
if p == nil {
|
err := p.Configure(rc)
|
||||||
panic(fmt.Sprintf("No provider for resource: %s", r.Id()))
|
if err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the provider if we haven't already
|
|
||||||
if err := p.init(vars); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the resource state
|
|
||||||
var rs *ResourceState
|
|
||||||
if state != nil {
|
|
||||||
rs = state.Resources[r.Id()]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the resource diff
|
return nil
|
||||||
var rd *ResourceDiff
|
|
||||||
if diff != nil {
|
|
||||||
rd = diff.Resources[r.Id()]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(vars) > 0 {
|
rn := n.Meta.(*GraphNodeResource)
|
||||||
if err := r.RawConfig.Interpolate(vars); err != nil {
|
if len(vars) > 0 && rn.Config != nil {
|
||||||
|
if err := rn.Config.RawConfig.Interpolate(vars); err != nil {
|
||||||
panic(fmt.Sprintf("Interpolate error: %s", err))
|
panic(fmt.Sprintf("Interpolate error: %s", err))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If we have no state, then create an empty state with the
|
// Set the config
|
||||||
// type fulfilled at the least.
|
rn.Resource.Config = NewResourceConfig(rn.Config.RawConfig)
|
||||||
if rs == nil {
|
|
||||||
rs = new(ResourceState)
|
|
||||||
}
|
}
|
||||||
rs.Type = r.Type
|
|
||||||
|
|
||||||
// Call the callack
|
// Call the callack
|
||||||
newVars, err := cb(&Resource{
|
newVars, err := cb(rn.Resource)
|
||||||
Id: r.Id(),
|
|
||||||
Config: NewResourceConfig(r.RawConfig),
|
|
||||||
Diff: rd,
|
|
||||||
Provider: p.Provider,
|
|
||||||
State: rs,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -362,25 +372,7 @@ func (t *Terraform) genericWalkFn(
|
||||||
vars[k] = v
|
vars[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terraformProvider) init(vars map[string]string) (err error) {
|
|
||||||
t.Once.Do(func() {
|
|
||||||
var rc *ResourceConfig
|
|
||||||
if t.Config != nil {
|
|
||||||
if err := t.Config.RawConfig.Interpolate(vars); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = NewResourceConfig(t.Config.RawConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = t.Provider.Configure(rc)
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package terraform
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ func TestTerraformApply_unknownAttribute(t *testing.T) {
|
||||||
|
|
||||||
func TestTerraformApply_vars(t *testing.T) {
|
func TestTerraformApply_vars(t *testing.T) {
|
||||||
tf := testTerraform(t, "apply-vars")
|
tf := testTerraform(t, "apply-vars")
|
||||||
tf.variables = map[string]string{"foo": "baz"}
|
//tf.variables = map[string]string{"foo": "baz"}
|
||||||
|
|
||||||
s := &State{}
|
s := &State{}
|
||||||
p, err := tf.Plan(s)
|
p, err := tf.Plan(s)
|
||||||
|
@ -208,6 +209,35 @@ func TestTerraformPlan_refreshNil(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTerraformRefresh(t *testing.T) {
|
||||||
|
rpAWS := new(MockResourceProvider)
|
||||||
|
rpAWS.ResourcesReturn = []ResourceType{
|
||||||
|
ResourceType{Name: "aws_instance"},
|
||||||
|
}
|
||||||
|
|
||||||
|
c := testConfig(t, "refresh-basic")
|
||||||
|
tf := testTerraform2(t, &Config{
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(rpAWS),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
rpAWS.RefreshReturn = &ResourceState{
|
||||||
|
ID: "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := tf.Refresh(c, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if !rpAWS.RefreshCalled {
|
||||||
|
t.Fatal("refresh should be called")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(s.Resources["aws_instance.web"], rpAWS.RefreshReturn) {
|
||||||
|
t.Fatalf("bad: %#v", s.Resources)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testConfig(t *testing.T, name string) *config.Config {
|
func testConfig(t *testing.T, name string) *config.Config {
|
||||||
c, err := config.Load(filepath.Join(fixtureDir, name, "main.tf"))
|
c, err := config.Load(filepath.Join(fixtureDir, name, "main.tf"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
resource "aws_instance" "web" {}
|
Loading…
Reference in New Issue