terraform: shadowResourceProviderFactory

This helper helps create the factory maps for the context.
This commit is contained in:
Mitchell Hashimoto 2016-10-03 00:01:11 -07:00
parent f7134d95e4
commit 02e93f5920
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 143 additions and 1 deletions

View File

@ -68,6 +68,10 @@ type ContextOpts struct {
//
// Extra functions on Context can be found in context_*.go files.
type Context struct {
// Maintainer note: Anytime this struct is changed, please verify
// that newShadowContext still does the right thing. Tests should
// fail regardless but putting this note here as well.
destroy bool
diff *Diff
diffLock sync.RWMutex

View File

@ -2,6 +2,8 @@ package terraform
import (
"io"
"github.com/mitchellh/copystructure"
)
// newShadowContext creates a new context that will shadow the given context
@ -16,5 +18,40 @@ import (
// context) will NEVER affect the real context. All structures are deep
// copied, no real providers or resources are used, etc.
func newShadowContext(c *Context) (*Context, *Context, io.Closer) {
return c, nil, nil
// Copy the targets
targetRaw, err := copystructure.Config{Lock: true}.Copy(c.targets)
if err != nil {
panic(err)
}
// Copy the variables
varRaw, err := copystructure.Config{Lock: true}.Copy(c.variables)
if err != nil {
panic(err)
}
// The factories
providerFactory := &shadowResourceProviderFactory{Original: c.providers}
// Create the shadow
shadow := &Context{
destroy: c.destroy,
diff: c.diff.DeepCopy(),
hooks: nil, // TODO: do we need to copy? stop hook?
module: c.module,
providers: providerFactory.ShadowMap(),
provisioners: nil, //TODO
state: c.state.DeepCopy(),
targets: targetRaw.([]string),
uiInput: nil, // TODO
variables: varRaw.(map[string]interface{}),
}
// Create the real context. This is effectively just a copy of
// the context given except we need to modify some of the values
// to point to the real side of a shadow so the shadow can compare values.
real := *c
real.providers = providerFactory.RealMap()
return &real, shadow, nil
}

View File

@ -0,0 +1,101 @@
package terraform
import (
"fmt"
"github.com/hashicorp/terraform/helper/shadow"
)
// shadowResourceProviderFactory is a helper that takes an actual, original
// map of ResourceProvider factories and provides methods to create mappings
// for shadowed resource providers.
type shadowResourceProviderFactory struct {
// Original is the original factory map
Original map[string]ResourceProviderFactory
shadows shadow.KeyedValue
}
type shadowResourceProviderFactoryEntry struct {
Real ResourceProvider
Shadow shadowResourceProvider
Err error
}
// RealMap returns the factory map for the "real" side of the shadow. This
// is the side that does actual work.
// TODO: test
func (f *shadowResourceProviderFactory) RealMap() map[string]ResourceProviderFactory {
m := make(map[string]ResourceProviderFactory)
for k, _ := range f.Original {
m[k] = f.realFactory(k)
}
return m
}
// ShadowMap returns the factory map for the "shadow" side of the shadow. This
// is the side that doesn't do any actual work but does compare results
// with the real side.
// TODO: test
func (f *shadowResourceProviderFactory) ShadowMap() map[string]ResourceProviderFactory {
m := make(map[string]ResourceProviderFactory)
for k, _ := range f.Original {
m[k] = f.shadowFactory(k)
}
return m
}
func (f *shadowResourceProviderFactory) realFactory(n string) ResourceProviderFactory {
return func() (ResourceProvider, error) {
// Get the original factory function
originalF, ok := f.Original[n]
if !ok {
return nil, fmt.Errorf("unknown provider initialized: %s", n)
}
// Build the entry
var entry shadowResourceProviderFactoryEntry
// Initialize it
p, err := originalF()
if err != nil {
entry.Err = err
p = nil // Just to be sure
}
if p != nil {
// Create the shadow
real, shadow := newShadowResourceProvider(p)
entry.Real = real
entry.Shadow = shadow
}
// Store the value
f.shadows.SetValue(n, &entry)
// Return
return entry.Real, entry.Err
}
}
func (f *shadowResourceProviderFactory) shadowFactory(n string) ResourceProviderFactory {
return func() (ResourceProvider, error) {
// Get the value
raw := f.shadows.Value(n)
if raw == nil {
return nil, fmt.Errorf(
"Nil shadow value for provider %q. Please report this bug.",
n)
}
entry, ok := raw.(*shadowResourceProviderFactoryEntry)
if !ok {
return nil, fmt.Errorf("Unknown value for shadow provider: %#v", raw)
}
// Return
return entry.Shadow, entry.Err
}
}