terraform: basic apply, more tests needed
This commit is contained in:
parent
4711850cf3
commit
b3e20a3e85
|
@ -0,0 +1,12 @@
|
|||
package terraform
|
||||
|
||||
// Resource encapsulates a resource, its configuration, its provider,
|
||||
// its current state, and potentially a desired diff from the state it
|
||||
// wants to reach.
|
||||
type Resource struct {
|
||||
Id string
|
||||
Config *ResourceConfig
|
||||
Diff *ResourceDiff
|
||||
Provider ResourceProvider
|
||||
State *ResourceState
|
||||
}
|
|
@ -37,7 +37,9 @@ type ResourceProvider interface {
|
|||
//
|
||||
// If the resource state given has an empty ID, then a new resource
|
||||
// is expected to be created.
|
||||
//Apply(ResourceState, ResourceDiff) (ResourceState, error)
|
||||
Apply(
|
||||
*ResourceState,
|
||||
*ResourceDiff) (*ResourceState, error)
|
||||
|
||||
// Diff diffs a resource versus a desired state and returns
|
||||
// a diff.
|
||||
|
|
|
@ -6,6 +6,12 @@ type MockResourceProvider struct {
|
|||
// Anything you want, in case you need to store extra data with the mock.
|
||||
Meta interface{}
|
||||
|
||||
ApplyCalled bool
|
||||
ApplyState *ResourceState
|
||||
ApplyDiff *ResourceDiff
|
||||
ApplyFn func(*ResourceState, *ResourceDiff) (*ResourceState, error)
|
||||
ApplyReturn *ResourceState
|
||||
ApplyReturnError error
|
||||
ConfigureCalled bool
|
||||
ConfigureConfig *ResourceConfig
|
||||
ConfigureReturnError error
|
||||
|
@ -35,6 +41,19 @@ func (p *MockResourceProvider) Configure(c *ResourceConfig) error {
|
|||
return p.ConfigureReturnError
|
||||
}
|
||||
|
||||
func (p *MockResourceProvider) Apply(
|
||||
state *ResourceState,
|
||||
diff *ResourceDiff) (*ResourceState, error) {
|
||||
p.ApplyCalled = true
|
||||
p.ApplyState = state
|
||||
p.ApplyDiff = diff
|
||||
if p.ApplyFn != nil {
|
||||
return p.ApplyFn(state, diff)
|
||||
}
|
||||
|
||||
return p.ApplyReturn, p.ApplyReturnError
|
||||
}
|
||||
|
||||
func (p *MockResourceProvider) Diff(
|
||||
state *ResourceState,
|
||||
desired *ResourceConfig) (*ResourceDiff, error) {
|
||||
|
|
|
@ -10,13 +10,14 @@ import (
|
|||
// can use to keep track of what real world resources it is actually
|
||||
// managing.
|
||||
type State struct {
|
||||
resources map[string]*ResourceState
|
||||
once sync.Once
|
||||
Resources map[string]*ResourceState
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (s *State) init() {
|
||||
s.once.Do(func() {
|
||||
s.resources = make(map[string]*ResourceState)
|
||||
s.Resources = make(map[string]*ResourceState)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ type terraformProvider struct {
|
|||
sync.Once
|
||||
}
|
||||
|
||||
// This is a function type used to implement a walker for the resource
|
||||
// tree internally on the Terraform structure.
|
||||
type genericWalkFunc func(*Resource) (map[string]string, error)
|
||||
|
||||
// Config is the configuration that must be given to instantiate
|
||||
// a Terraform structure.
|
||||
type Config struct {
|
||||
|
@ -99,8 +103,14 @@ func New(c *Config) (*Terraform, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (t *Terraform) Apply(*State, *Diff) (*State, error) {
|
||||
return nil, nil
|
||||
func (t *Terraform) Apply(s *State, d *Diff) (*State, error) {
|
||||
result := new(State)
|
||||
err := t.graph.Walk(t.applyWalkFn(s, d, result))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (t *Terraform) Diff(s *State) (*Diff, error) {
|
||||
|
@ -117,13 +127,80 @@ func (t *Terraform) Refresh(*State) (*State, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *Terraform) applyWalkFn(
|
||||
state *State,
|
||||
diff *Diff,
|
||||
result *State) depgraph.WalkFunc {
|
||||
var l sync.Mutex
|
||||
|
||||
// Initialize the result
|
||||
result.init()
|
||||
|
||||
cb := func(r *Resource) (map[string]string, error) {
|
||||
rs, err := r.Provider.Apply(r.State, r.Diff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update the resulting diff
|
||||
l.Lock()
|
||||
result.Resources[r.Id] = rs
|
||||
l.Unlock()
|
||||
|
||||
// Determine the new state and update variables
|
||||
vars := make(map[string]string)
|
||||
for ak, av := range rs.Attributes {
|
||||
vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
|
||||
}
|
||||
|
||||
return vars, nil
|
||||
}
|
||||
|
||||
return t.genericWalkFn(state, diff, cb)
|
||||
}
|
||||
|
||||
func (t *Terraform) diffWalkFn(
|
||||
state *State, result *Diff) depgraph.WalkFunc {
|
||||
var l sync.RWMutex
|
||||
var l sync.Mutex
|
||||
|
||||
// Initialize the result diff so we can write to it
|
||||
result.init()
|
||||
|
||||
cb := func(r *Resource) (map[string]string, error) {
|
||||
diff, err := r.Provider.Diff(r.State, r.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there were no diff items, return right away
|
||||
if diff == nil || len(diff.Attributes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Update the resulting diff
|
||||
l.Lock()
|
||||
result.Resources[r.Id] = diff
|
||||
l.Unlock()
|
||||
|
||||
// Determine the new state and update variables
|
||||
vars := make(map[string]string)
|
||||
rs := r.State.MergeDiff(diff.Attributes)
|
||||
for ak, av := range rs.Attributes {
|
||||
vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
|
||||
}
|
||||
|
||||
return vars, nil
|
||||
}
|
||||
|
||||
return t.genericWalkFn(state, nil, cb)
|
||||
}
|
||||
|
||||
func (t *Terraform) genericWalkFn(
|
||||
state *State,
|
||||
diff *Diff,
|
||||
cb genericWalkFunc) depgraph.WalkFunc {
|
||||
var l sync.Mutex
|
||||
|
||||
// Initialize the variables for application
|
||||
vars := make(map[string]string)
|
||||
for k, v := range t.variables {
|
||||
|
@ -157,12 +234,17 @@ func (t *Terraform) diffWalkFn(
|
|||
return err
|
||||
}
|
||||
|
||||
l.RLock()
|
||||
// Get the resource state
|
||||
var rs *ResourceState
|
||||
if state != nil {
|
||||
rs = state.resources[r.Id()]
|
||||
rs = state.Resources[r.Id()]
|
||||
}
|
||||
|
||||
// Get the resource diff
|
||||
var rd *ResourceDiff
|
||||
if diff != nil {
|
||||
rd = diff.Resources[r.Id()]
|
||||
}
|
||||
l.RUnlock()
|
||||
|
||||
if len(vars) > 0 {
|
||||
if err := r.RawConfig.Interpolate(vars); err != nil {
|
||||
|
@ -177,30 +259,30 @@ func (t *Terraform) diffWalkFn(
|
|||
}
|
||||
rs.Type = r.Type
|
||||
|
||||
diff, err := p.Provider.Diff(rs, &ResourceConfig{
|
||||
ComputedKeys: r.RawConfig.UnknownKeys(),
|
||||
Raw: r.RawConfig.Config(),
|
||||
// Call the callack
|
||||
newVars, err := cb(&Resource{
|
||||
Id: r.Id(),
|
||||
Config: &ResourceConfig{
|
||||
ComputedKeys: r.RawConfig.UnknownKeys(),
|
||||
Raw: r.RawConfig.Config(),
|
||||
},
|
||||
Diff: rd,
|
||||
Provider: p.Provider,
|
||||
State: rs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If there were no diff items, return right away
|
||||
if diff == nil || len(diff.Attributes) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(newVars) > 0 {
|
||||
// Acquire a lock since this function is called in parallel
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
// Acquire a lock since this function is called in parallel
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
// Update the resulting diff
|
||||
result.Resources[r.Id()] = diff
|
||||
|
||||
// Determine the new state and update variables
|
||||
rs = rs.MergeDiff(diff.Attributes)
|
||||
for ak, av := range rs.Attributes {
|
||||
vars[fmt.Sprintf("%s.%s", r.Id(), ak)] = av
|
||||
// Update variables
|
||||
for k, v := range newVars {
|
||||
vars[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -203,6 +203,22 @@ func TestNew_variables(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTerraformApply(t *testing.T) {
|
||||
tf := testTerraform(t, "apply-good")
|
||||
|
||||
s := &State{}
|
||||
d := &Diff{}
|
||||
|
||||
state, err := tf.Apply(s, d)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if len(state.Resources) < 2 {
|
||||
t.Fatalf("bad: %#v", state.Resources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerraformDiff(t *testing.T) {
|
||||
tf := testTerraform(t, "diff-good")
|
||||
|
||||
|
@ -291,6 +307,14 @@ func testProviderFunc(n string, rs []string) ResourceProviderFactory {
|
|||
}
|
||||
|
||||
return func() (ResourceProvider, error) {
|
||||
applyFn := func(
|
||||
s *ResourceState,
|
||||
d *ResourceDiff) (*ResourceState, error) {
|
||||
return &ResourceState{
|
||||
ID: "foo",
|
||||
}, nil
|
||||
}
|
||||
|
||||
diffFn := func(
|
||||
s *ResourceState,
|
||||
c *ResourceConfig) (*ResourceDiff, error) {
|
||||
|
@ -343,6 +367,7 @@ func testProviderFunc(n string, rs []string) ResourceProviderFactory {
|
|||
|
||||
result := &MockResourceProvider{
|
||||
Meta: n,
|
||||
ApplyFn: applyFn,
|
||||
DiffFn: diffFn,
|
||||
ResourcesReturn: resources,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
resource "aws_instance" "foo" {
|
||||
num = "2"
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
foo = "${aws_instance.foo.num}"
|
||||
}
|
Loading…
Reference in New Issue