terraform: shadow component factory supports closing

This commit is contained in:
Mitchell Hashimoto 2016-10-03 21:19:59 -07:00
parent 8ef35d7561
commit 3e7665db09
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 67 additions and 12 deletions

View File

@ -365,7 +365,8 @@ func (c *Context) Apply() (*State, error) {
if c.destroy { if c.destroy {
walker, err = c.walk(graph, nil, walkDestroy) walker, err = c.walk(graph, nil, walkDestroy)
} else { } else {
walker, err = c.walk(graph, nil, walkApply) //walker, err = c.walk(graph, nil, walkApply)
walker, err = c.walk(graph, graph, walkApply)
} }
if len(walker.ValidationErrors) > 0 { if len(walker.ValidationErrors) > 0 {

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/helper/shadow" "github.com/hashicorp/terraform/helper/shadow"
) )
@ -31,14 +32,23 @@ func newShadowComponentFactory(
// shadowComponentFactory is the shadow side. Any components created // shadowComponentFactory is the shadow side. Any components created
// with this factory are fake and will not cause real work to happen. // 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 { type shadowComponentFactory struct {
*shadowComponentFactoryShared *shadowComponentFactoryShared
Shadow bool // True if this should return the shadow Shadow bool // True if this should return the shadow
lock sync.Mutex
} }
func (f *shadowComponentFactory) ResourceProvider( func (f *shadowComponentFactory) ResourceProvider(
n, uid string) (ResourceProvider, error) { n, uid string) (ResourceProvider, error) {
f.lock.Lock()
defer f.lock.Unlock()
real, shadow, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid) real, shadow, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid)
var result ResourceProvider = real var result ResourceProvider = real
if f.Shadow { if f.Shadow {
@ -50,6 +60,9 @@ func (f *shadowComponentFactory) ResourceProvider(
func (f *shadowComponentFactory) ResourceProvisioner( func (f *shadowComponentFactory) ResourceProvisioner(
n, uid string) (ResourceProvisioner, error) { n, uid string) (ResourceProvisioner, error) {
f.lock.Lock()
defer f.lock.Unlock()
real, shadow, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid) real, shadow, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid)
var result ResourceProvisioner = real var result ResourceProvisioner = real
if f.Shadow { if f.Shadow {
@ -59,13 +72,58 @@ func (f *shadowComponentFactory) ResourceProvisioner(
return result, err 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)
}
}
}
// TODO: provisioners once they're done
// Mark ourselves as closed
shared.closed = true
return result
}
// shadowComponentFactoryShared is shared data between the two factories. // 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 { type shadowComponentFactoryShared struct {
contextComponentFactory contextComponentFactory
closed bool
providers shadow.KeyedValue providers shadow.KeyedValue
providerKeys []string
provisioners shadow.KeyedValue provisioners shadow.KeyedValue
lock sync.Mutex provisionerKeys []string
} }
// shadowResourceProviderFactoryEntry is the entry that is stored in // shadowResourceProviderFactoryEntry is the entry that is stored in
@ -84,9 +142,6 @@ type shadowComponentFactoryProvisionerEntry struct {
func (f *shadowComponentFactoryShared) ResourceProvider( func (f *shadowComponentFactoryShared) ResourceProvider(
n, uid string) (ResourceProvider, shadowResourceProvider, error) { n, uid string) (ResourceProvider, shadowResourceProvider, error) {
f.lock.Lock()
defer f.lock.Unlock()
// Determine if we already have a value // Determine if we already have a value
raw, ok := f.providers.ValueOk(uid) raw, ok := f.providers.ValueOk(uid)
if !ok { if !ok {
@ -109,6 +164,7 @@ func (f *shadowComponentFactoryShared) ResourceProvider(
// Store the value // Store the value
f.providers.SetValue(uid, &entry) f.providers.SetValue(uid, &entry)
f.providerKeys = append(f.providerKeys, uid)
raw = &entry raw = &entry
} }
@ -124,9 +180,6 @@ func (f *shadowComponentFactoryShared) ResourceProvider(
func (f *shadowComponentFactoryShared) ResourceProvisioner( func (f *shadowComponentFactoryShared) ResourceProvisioner(
n, uid string) (ResourceProvisioner, ResourceProvisioner, error) { n, uid string) (ResourceProvisioner, ResourceProvisioner, error) {
f.lock.Lock()
defer f.lock.Unlock()
// Determine if we already have a value // Determine if we already have a value
raw, ok := f.provisioners.ValueOk(uid) raw, ok := f.provisioners.ValueOk(uid)
if !ok { if !ok {
@ -150,6 +203,7 @@ func (f *shadowComponentFactoryShared) ResourceProvisioner(
// Store the value // Store the value
f.provisioners.SetValue(uid, &entry) f.provisioners.SetValue(uid, &entry)
f.provisionerKeys = append(f.provisionerKeys, uid)
raw = &entry raw = &entry
} }

View File

@ -66,10 +66,10 @@ func newShadowContext(c *Context) (*Context, *Context, io.Closer) {
// shadowContextCloser is the io.Closer returned by newShadowContext that // shadowContextCloser is the io.Closer returned by newShadowContext that
// closes all the shadows and returns the results. // closes all the shadows and returns the results.
type shadowContextCloser struct { type shadowContextCloser struct {
Components interface{} Components *shadowComponentFactory
} }
// Close closes the shadow context. // Close closes the shadow context.
func (c *shadowContextCloser) Close() error { func (c *shadowContextCloser) Close() error {
return nil return c.Components.CloseShadow()
} }