summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/shadow_components.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/shadow_components.go273
1 files changed, 273 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go
new file mode 100644
index 00000000..116cf84f
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go
@@ -0,0 +1,273 @@
+package terraform
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/hashicorp/go-multierror"
+ "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 := &shadowComponentFactory{
+ shadowComponentFactoryShared: shared,
+ }
+
+ // Create the shadow
+ shadow := &shadowComponentFactory{
+ shadowComponentFactoryShared: shared,
+ Shadow: true,
+ }
+
+ return real, shadow
+}
+
+// shadowComponentFactory is the shadow side. Any components created
+// with this factory are fake and will not cause real work to happen.
+//
+// Unlike other shadowers, the shadow component factory will allow the
+// shadow to create _any_ component even if it is never requested on the
+// real side. This is because errors will happen later downstream as function
+// calls are made to the shadows that are never matched on the real side.
+type shadowComponentFactory struct {
+ *shadowComponentFactoryShared
+
+ Shadow bool // True if this should return the shadow
+ lock sync.Mutex
+}
+
+func (f *shadowComponentFactory) ResourceProvider(
+ n, uid string) (ResourceProvider, error) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ real, shadow, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid)
+ var result ResourceProvider = real
+ if f.Shadow {
+ result = shadow
+ }
+
+ return result, err
+}
+
+func (f *shadowComponentFactory) ResourceProvisioner(
+ n, uid string) (ResourceProvisioner, error) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ real, shadow, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid)
+ var result ResourceProvisioner = real
+ if f.Shadow {
+ result = shadow
+ }
+
+ return result, err
+}
+
+// CloseShadow is called when the _real_ side is complete. This will cause
+// all future blocking operations to return immediately on the shadow to
+// ensure the shadow also completes.
+func (f *shadowComponentFactory) CloseShadow() error {
+ // If we aren't the shadow, just return
+ if !f.Shadow {
+ return nil
+ }
+
+ // Lock ourselves so we don't modify state
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ // Grab our shared state
+ shared := f.shadowComponentFactoryShared
+
+ // If we're already closed, its an error
+ if shared.closed {
+ return fmt.Errorf("component factory shadow already closed")
+ }
+
+ // Close all the providers and provisioners and return the error
+ var result error
+ for _, n := range shared.providerKeys {
+ _, shadow, err := shared.ResourceProvider(n, n)
+ if err == nil && shadow != nil {
+ if err := shadow.CloseShadow(); err != nil {
+ result = multierror.Append(result, err)
+ }
+ }
+ }
+
+ for _, n := range shared.provisionerKeys {
+ _, shadow, err := shared.ResourceProvisioner(n, n)
+ if err == nil && shadow != nil {
+ if err := shadow.CloseShadow(); err != nil {
+ result = multierror.Append(result, err)
+ }
+ }
+ }
+
+ // Mark ourselves as closed
+ shared.closed = true
+
+ return result
+}
+
+func (f *shadowComponentFactory) ShadowError() error {
+ // If we aren't the shadow, just return
+ if !f.Shadow {
+ return nil
+ }
+
+ // Lock ourselves so we don't modify state
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ // Grab our shared state
+ shared := f.shadowComponentFactoryShared
+
+ // If we're not closed, its an error
+ if !shared.closed {
+ return fmt.Errorf("component factory must be closed to retrieve errors")
+ }
+
+ // Close all the providers and provisioners and return the error
+ var result error
+ for _, n := range shared.providerKeys {
+ _, shadow, err := shared.ResourceProvider(n, n)
+ if err == nil && shadow != nil {
+ if err := shadow.ShadowError(); err != nil {
+ result = multierror.Append(result, err)
+ }
+ }
+ }
+
+ for _, n := range shared.provisionerKeys {
+ _, shadow, err := shared.ResourceProvisioner(n, n)
+ if err == nil && shadow != nil {
+ if err := shadow.ShadowError(); err != nil {
+ result = multierror.Append(result, err)
+ }
+ }
+ }
+
+ return result
+}
+
+// shadowComponentFactoryShared is shared data between the two factories.
+//
+// It is NOT SAFE to run any function on this struct in parallel. Lock
+// access to this struct.
+type shadowComponentFactoryShared struct {
+ contextComponentFactory
+
+ closed bool
+ providers shadow.KeyedValue
+ providerKeys []string
+ provisioners shadow.KeyedValue
+ provisionerKeys []string
+}
+
+// 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 shadowResourceProvisioner
+ Err error
+}
+
+func (f *shadowComponentFactoryShared) ResourceProvider(
+ n, uid string) (ResourceProvider, shadowResourceProvider, error) {
+ // 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
+
+ if f.closed {
+ shadow.CloseShadow()
+ }
+ }
+
+ // Store the value
+ f.providers.SetValue(uid, &entry)
+ f.providerKeys = append(f.providerKeys, uid)
+ 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, shadowResourceProvisioner, error) {
+ // 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, shadow := newShadowResourceProvisioner(p)
+ entry.Real = real
+ entry.Shadow = shadow
+
+ if f.closed {
+ shadow.CloseShadow()
+ }
+ }
+
+ // Store the value
+ f.provisioners.SetValue(uid, &entry)
+ f.provisionerKeys = append(f.provisionerKeys, uid)
+ 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
+}