terraform: switch to a component factory
This is necessary so that the shadow version can actually keep track of what provider is used for what. Before, providers for different alises were just initialized but the factory had no idea. Arguably this is fine but when trying to build a shadow graph this presents challenges. With these changes, we now pass an opaque "uid" through that is used to keep track of the providers and what real maps to what shadow.
This commit is contained in:
parent
5053872e82
commit
0b00bbde4e
|
@ -72,19 +72,18 @@ type Context struct {
|
|||
// 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
|
||||
hooks []Hook
|
||||
module *module.Tree
|
||||
providers map[string]ResourceProviderFactory
|
||||
provisioners map[string]ResourceProvisionerFactory
|
||||
sh *stopHook
|
||||
state *State
|
||||
stateLock sync.RWMutex
|
||||
targets []string
|
||||
uiInput UIInput
|
||||
variables map[string]interface{}
|
||||
components contextComponentFactory
|
||||
destroy bool
|
||||
diff *Diff
|
||||
diffLock sync.RWMutex
|
||||
hooks []Hook
|
||||
module *module.Tree
|
||||
sh *stopHook
|
||||
state *State
|
||||
stateLock sync.RWMutex
|
||||
targets []string
|
||||
uiInput UIInput
|
||||
variables map[string]interface{}
|
||||
|
||||
l sync.Mutex // Lock acquired during any task
|
||||
parallelSem Semaphore
|
||||
|
@ -153,16 +152,18 @@ func NewContext(opts *ContextOpts) (*Context, error) {
|
|||
}
|
||||
|
||||
return &Context{
|
||||
destroy: opts.Destroy,
|
||||
diff: opts.Diff,
|
||||
hooks: hooks,
|
||||
module: opts.Module,
|
||||
providers: opts.Providers,
|
||||
provisioners: opts.Provisioners,
|
||||
state: state,
|
||||
targets: opts.Targets,
|
||||
uiInput: opts.UIInput,
|
||||
variables: variables,
|
||||
components: &basicComponentFactory{
|
||||
providers: opts.Providers,
|
||||
provisioners: opts.Provisioners,
|
||||
},
|
||||
destroy: opts.Destroy,
|
||||
diff: opts.Diff,
|
||||
hooks: hooks,
|
||||
module: opts.Module,
|
||||
state: state,
|
||||
targets: opts.Targets,
|
||||
uiInput: opts.UIInput,
|
||||
variables: variables,
|
||||
|
||||
parallelSem: NewSemaphore(par),
|
||||
providerInputConfig: make(map[string]map[string]interface{}),
|
||||
|
@ -183,22 +184,11 @@ func (c *Context) Graph(g *ContextGraphOpts) (*Graph, error) {
|
|||
// GraphBuilder returns the GraphBuilder that will be used to create
|
||||
// the graphs for this context.
|
||||
func (c *Context) graphBuilder(g *ContextGraphOpts) GraphBuilder {
|
||||
// TODO test
|
||||
providers := make([]string, 0, len(c.providers))
|
||||
for k, _ := range c.providers {
|
||||
providers = append(providers, k)
|
||||
}
|
||||
|
||||
provisioners := make([]string, 0, len(c.provisioners))
|
||||
for k, _ := range c.provisioners {
|
||||
provisioners = append(provisioners, k)
|
||||
}
|
||||
|
||||
return &BuiltinGraphBuilder{
|
||||
Root: c.module,
|
||||
Diff: c.diff,
|
||||
Providers: providers,
|
||||
Provisioners: provisioners,
|
||||
Providers: c.components.ResourceProviders(),
|
||||
Provisioners: c.components.ResourceProvisioners(),
|
||||
State: c.state,
|
||||
Targets: c.targets,
|
||||
Destroy: c.destroy,
|
||||
|
@ -375,7 +365,7 @@ func (c *Context) Apply() (*State, error) {
|
|||
if c.destroy {
|
||||
walker, err = c.walk(graph, nil, walkDestroy)
|
||||
} else {
|
||||
walker, err = c.walk(graph, graph, walkApply)
|
||||
walker, err = c.walk(graph, nil, walkApply)
|
||||
}
|
||||
|
||||
if len(walker.ValidationErrors) > 0 {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// contextComponentFactory is the interface that Context uses
|
||||
// to initialize various components such as providers and provisioners.
|
||||
// This factory gets more information than the raw maps using to initialize
|
||||
// a Context. This information is used for debugging.
|
||||
type contextComponentFactory interface {
|
||||
// ResourceProvider creates a new ResourceProvider with the given
|
||||
// type. The "uid" is a unique identifier for this provider being
|
||||
// initialized that can be used for internal tracking.
|
||||
ResourceProvider(typ, uid string) (ResourceProvider, error)
|
||||
ResourceProviders() []string
|
||||
|
||||
// ResourceProvisioner creates a new ResourceProvisioner with the
|
||||
// given type. The "uid" is a unique identifier for this provisioner
|
||||
// being initialized that can be used for internal tracking.
|
||||
ResourceProvisioner(typ, uid string) (ResourceProvisioner, error)
|
||||
ResourceProvisioners() []string
|
||||
}
|
||||
|
||||
// basicComponentFactory just calls a factory from a map directly.
|
||||
type basicComponentFactory struct {
|
||||
providers map[string]ResourceProviderFactory
|
||||
provisioners map[string]ResourceProvisionerFactory
|
||||
}
|
||||
|
||||
func (c *basicComponentFactory) ResourceProviders() []string {
|
||||
result := make([]string, len(c.providers))
|
||||
for k, _ := range c.providers {
|
||||
result = append(result, k)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *basicComponentFactory) ResourceProvisioners() []string {
|
||||
result := make([]string, len(c.provisioners))
|
||||
for k, _ := range c.provisioners {
|
||||
result = append(result, k)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *basicComponentFactory) ResourceProvider(typ, uid string) (ResourceProvider, error) {
|
||||
f, ok := c.providers[typ]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown provider %q", typ)
|
||||
}
|
||||
|
||||
return f()
|
||||
}
|
||||
|
||||
func (c *basicComponentFactory) ResourceProvisioner(typ, uid string) (ResourceProvisioner, error) {
|
||||
f, ok := c.provisioners[typ]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown provisioner %q", typ)
|
||||
}
|
||||
|
||||
return f()
|
||||
}
|
|
@ -43,17 +43,11 @@ func (c *Context) Import(opts *ImportOpts) (*State, error) {
|
|||
// Copy our own state
|
||||
c.state = c.state.DeepCopy()
|
||||
|
||||
// Get supported providers (for the graph builder)
|
||||
providers := make([]string, 0, len(c.providers))
|
||||
for k, _ := range c.providers {
|
||||
providers = append(providers, k)
|
||||
}
|
||||
|
||||
// Initialize our graph builder
|
||||
builder := &ImportGraphBuilder{
|
||||
ImportTargets: opts.Targets,
|
||||
Module: opts.Module,
|
||||
Providers: providers,
|
||||
Providers: c.components.ResourceProviders(),
|
||||
}
|
||||
|
||||
// Build the graph!
|
||||
|
|
|
@ -26,14 +26,13 @@ type BuiltinEvalContext struct {
|
|||
InterpolaterVars map[string]map[string]interface{}
|
||||
InterpolaterVarLock *sync.Mutex
|
||||
|
||||
Components contextComponentFactory
|
||||
Hooks []Hook
|
||||
InputValue UIInput
|
||||
Providers map[string]ResourceProviderFactory
|
||||
ProviderCache map[string]ResourceProvider
|
||||
ProviderConfigCache map[string]*ResourceConfig
|
||||
ProviderInputConfig map[string]map[string]interface{}
|
||||
ProviderLock *sync.Mutex
|
||||
Provisioners map[string]ResourceProvisionerFactory
|
||||
ProvisionerCache map[string]ResourceProvisioner
|
||||
ProvisionerLock *sync.Mutex
|
||||
DiffValue *Diff
|
||||
|
@ -82,13 +81,9 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
|
|||
defer ctx.ProviderLock.Unlock()
|
||||
|
||||
typeName := strings.SplitN(n, ".", 2)[0]
|
||||
uid := n
|
||||
|
||||
f, ok := ctx.Providers[typeName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Provider '%s' not found", typeName)
|
||||
}
|
||||
|
||||
p, err := f()
|
||||
p, err := ctx.Components.ResourceProvider(typeName, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -231,12 +226,7 @@ func (ctx *BuiltinEvalContext) InitProvisioner(
|
|||
ctx.ProvisionerLock.Lock()
|
||||
defer ctx.ProvisionerLock.Unlock()
|
||||
|
||||
f, ok := ctx.Provisioners[n]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Provisioner '%s' not found", n)
|
||||
}
|
||||
|
||||
p, err := f()
|
||||
p, err := ctx.Components.ResourceProvisioner(n, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -341,9 +331,4 @@ func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
|
|||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) init() {
|
||||
// We nil-check the things below because they're meant to be configured,
|
||||
// and we just default them to non-nil.
|
||||
if ctx.Providers == nil {
|
||||
ctx.Providers = make(map[string]ResourceProviderFactory)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,12 +68,11 @@ func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
|
|||
PathValue: path,
|
||||
Hooks: w.Context.hooks,
|
||||
InputValue: w.Context.uiInput,
|
||||
Providers: w.Context.providers,
|
||||
Components: w.Context.components,
|
||||
ProviderCache: w.providerCache,
|
||||
ProviderConfigCache: w.providerConfigCache,
|
||||
ProviderInputConfig: w.Context.providerInputConfig,
|
||||
ProviderLock: &w.providerLock,
|
||||
Provisioners: w.Context.provisioners,
|
||||
ProvisionerCache: w.provisionerCache,
|
||||
ProvisionerLock: &w.provisionerLock,
|
||||
DiffValue: w.Context.diff,
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/shadow"
|
||||
)
|
||||
|
||||
// newShadowComponentFactory creates a shadowed contextComponentFactory
|
||||
// so that requests to create new components result in both a real and
|
||||
// shadow side.
|
||||
func newShadowComponentFactory(
|
||||
f contextComponentFactory) (contextComponentFactory, *shadowComponentFactory) {
|
||||
// Create the shared data
|
||||
shared := &shadowComponentFactoryShared{contextComponentFactory: f}
|
||||
|
||||
// Create the real side
|
||||
real := &shadowComponentFactoryReal{
|
||||
shadowComponentFactoryShared: shared,
|
||||
}
|
||||
|
||||
// Create the shadow
|
||||
shadow := &shadowComponentFactory{
|
||||
shadowComponentFactoryShared: shared,
|
||||
}
|
||||
|
||||
return real, shadow
|
||||
}
|
||||
|
||||
// shadowComponentFactory is the shadow side. Any components created
|
||||
// with this factory are fake and will not cause real work to happen.
|
||||
type shadowComponentFactory struct {
|
||||
*shadowComponentFactoryShared
|
||||
}
|
||||
|
||||
func (f *shadowComponentFactory) ResourceProvider(
|
||||
n, uid string) (ResourceProvider, error) {
|
||||
_, shadow, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid)
|
||||
return shadow, err
|
||||
}
|
||||
|
||||
func (f *shadowComponentFactory) ResourceProvisioner(
|
||||
n, uid string) (ResourceProvisioner, error) {
|
||||
_, shadow, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid)
|
||||
return shadow, err
|
||||
}
|
||||
|
||||
// shadowComponentFactoryReal is the real side of the component factory.
|
||||
// Operations here result in real components that do real work.
|
||||
type shadowComponentFactoryReal struct {
|
||||
*shadowComponentFactoryShared
|
||||
}
|
||||
|
||||
func (f *shadowComponentFactoryReal) ResourceProvider(
|
||||
n, uid string) (ResourceProvider, error) {
|
||||
real, _, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid)
|
||||
return real, err
|
||||
}
|
||||
|
||||
func (f *shadowComponentFactoryReal) ResourceProvisioner(
|
||||
n, uid string) (ResourceProvisioner, error) {
|
||||
real, _, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid)
|
||||
return real, err
|
||||
}
|
||||
|
||||
// shadowComponentFactoryShared is shared data between the two factories.
|
||||
type shadowComponentFactoryShared struct {
|
||||
contextComponentFactory
|
||||
|
||||
providers shadow.KeyedValue
|
||||
provisioners shadow.KeyedValue
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// shadowResourceProviderFactoryEntry is the entry that is stored in
|
||||
// the Shadows key/value for a provider.
|
||||
type shadowComponentFactoryProviderEntry struct {
|
||||
Real ResourceProvider
|
||||
Shadow shadowResourceProvider
|
||||
Err error
|
||||
}
|
||||
|
||||
type shadowComponentFactoryProvisionerEntry struct {
|
||||
Real ResourceProvisioner
|
||||
Shadow ResourceProvisioner
|
||||
Err error
|
||||
}
|
||||
|
||||
func (f *shadowComponentFactoryShared) ResourceProvider(
|
||||
n, uid string) (ResourceProvider, shadowResourceProvider, error) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
|
||||
// Determine if we already have a value
|
||||
raw, ok := f.providers.ValueOk(uid)
|
||||
if !ok {
|
||||
// Build the entry
|
||||
var entry shadowComponentFactoryProviderEntry
|
||||
|
||||
// No value, initialize. Create the original
|
||||
p, err := f.contextComponentFactory.ResourceProvider(n, uid)
|
||||
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.providers.SetValue(uid, &entry)
|
||||
raw = &entry
|
||||
}
|
||||
|
||||
// Read the entry
|
||||
entry, ok := raw.(*shadowComponentFactoryProviderEntry)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Unknown value for shadow provider: %#v", raw)
|
||||
}
|
||||
|
||||
// Return
|
||||
return entry.Real, entry.Shadow, entry.Err
|
||||
}
|
||||
|
||||
func (f *shadowComponentFactoryShared) ResourceProvisioner(
|
||||
n, uid string) (ResourceProvisioner, ResourceProvisioner, error) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
|
||||
// Determine if we already have a value
|
||||
raw, ok := f.provisioners.ValueOk(uid)
|
||||
if !ok {
|
||||
// Build the entry
|
||||
var entry shadowComponentFactoryProvisionerEntry
|
||||
|
||||
// No value, initialize. Create the original
|
||||
p, err := f.contextComponentFactory.ResourceProvisioner(n, uid)
|
||||
if err != nil {
|
||||
entry.Err = err
|
||||
p = nil // Just to be sure
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
// For now, just create a mock since we don't support provisioners yet
|
||||
real := p
|
||||
shadow := new(MockResourceProvisioner)
|
||||
entry.Real = real
|
||||
entry.Shadow = shadow
|
||||
}
|
||||
|
||||
// Store the value
|
||||
f.provisioners.SetValue(uid, &entry)
|
||||
raw = &entry
|
||||
}
|
||||
|
||||
// Read the entry
|
||||
entry, ok := raw.(*shadowComponentFactoryProvisionerEntry)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("Unknown value for shadow provisioner: %#v", raw)
|
||||
}
|
||||
|
||||
// Return
|
||||
return entry.Real, entry.Shadow, entry.Err
|
||||
}
|
|
@ -31,20 +31,19 @@ func newShadowContext(c *Context) (*Context, *Context, io.Closer) {
|
|||
}
|
||||
|
||||
// The factories
|
||||
providerFactory := &shadowResourceProviderFactory{Original: c.providers}
|
||||
componentsReal, componentsShadow := newShadowComponentFactory(c.components)
|
||||
|
||||
// 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{}),
|
||||
components: componentsShadow,
|
||||
destroy: c.destroy,
|
||||
diff: c.diff.DeepCopy(),
|
||||
hooks: nil, // TODO: do we need to copy? stop hook?
|
||||
module: c.module,
|
||||
state: c.state.DeepCopy(),
|
||||
targets: targetRaw.([]string),
|
||||
uiInput: nil, // TODO
|
||||
variables: varRaw.(map[string]interface{}),
|
||||
|
||||
// Hardcoded to 4 since parallelism in the shadow doesn't matter
|
||||
// a ton since we're doing far less compared to the real side
|
||||
|
@ -57,17 +56,17 @@ func newShadowContext(c *Context) (*Context, *Context, io.Closer) {
|
|||
// 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()
|
||||
real.components = componentsReal
|
||||
|
||||
return &real, shadow, &shadowContextCloser{
|
||||
Providers: providerFactory,
|
||||
Components: componentsShadow,
|
||||
}
|
||||
}
|
||||
|
||||
// shadowContextCloser is the io.Closer returned by newShadowContext that
|
||||
// closes all the shadows and returns the results.
|
||||
type shadowContextCloser struct {
|
||||
Providers interface{}
|
||||
Components interface{}
|
||||
}
|
||||
|
||||
// Close closes the shadow context.
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue