terraform: working on the resource provider shadow, not working yet
This commit is contained in:
parent
35f13f9c52
commit
1df3bbdc37
|
@ -0,0 +1,51 @@
|
||||||
|
package shadow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Value is a struct that coordinates a value between two
|
||||||
|
// parallel routines. It is similar to atomic.Value except that when
|
||||||
|
// Value is called if it isn't set it will wait for it.
|
||||||
|
type Value struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
cond *sync.Cond
|
||||||
|
value interface{}
|
||||||
|
valueSet bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value that was set.
|
||||||
|
func (w *Value) Value() interface{} {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
|
// If we already have a value just return
|
||||||
|
for !w.valueSet {
|
||||||
|
// No value, setup the condition variable if we have to
|
||||||
|
if w.cond == nil {
|
||||||
|
w.cond = sync.NewCond(&w.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait on it
|
||||||
|
w.cond.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the value
|
||||||
|
return w.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValue sets the value.
|
||||||
|
func (w *Value) SetValue(v interface{}) {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
|
// Set the value
|
||||||
|
w.valueSet = true
|
||||||
|
w.value = v
|
||||||
|
|
||||||
|
// If we have a condition, clear it
|
||||||
|
if w.cond != nil {
|
||||||
|
w.cond.Broadcast()
|
||||||
|
w.cond = nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package shadow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValue(t *testing.T) {
|
||||||
|
var v Value
|
||||||
|
|
||||||
|
// Start trying to get the value
|
||||||
|
valueCh := make(chan interface{})
|
||||||
|
go func() {
|
||||||
|
valueCh <- v.Value()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We should not get the value
|
||||||
|
select {
|
||||||
|
case <-valueCh:
|
||||||
|
t.Fatal("shouldn't receive value")
|
||||||
|
case <-time.After(10 * time.Millisecond):
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value
|
||||||
|
v.SetValue(42)
|
||||||
|
val := <-valueCh
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
if val != 42 {
|
||||||
|
t.Fatalf("bad: %#v", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should be able to ask for the value again immediately
|
||||||
|
if val := v.Value(); val != 42 {
|
||||||
|
t.Fatalf("bad: %#v", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can change the value
|
||||||
|
v.SetValue(84)
|
||||||
|
if val := v.Value(); val != 84 {
|
||||||
|
t.Fatalf("bad: %#v", val)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,6 +80,14 @@ func NewShadowEvalContext(ctx EvalContext) (EvalContext, ShadowEvalContext) {
|
||||||
return real, shadow
|
return real, shadow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errShadow is the error returned by the shadow context when
|
||||||
|
// things go wrong. This should be ignored and the error result from
|
||||||
|
// Close should be checked instead since that'll contain more detailed
|
||||||
|
// error.
|
||||||
|
errShadow = errors.New("shadow error")
|
||||||
|
)
|
||||||
|
|
||||||
// shadowEvalContextReal is the EvalContext that does real work.
|
// shadowEvalContextReal is the EvalContext that does real work.
|
||||||
type shadowEvalContextReal struct {
|
type shadowEvalContextReal struct {
|
||||||
EvalContext
|
EvalContext
|
||||||
|
@ -85,6 +96,8 @@ type shadowEvalContextReal struct {
|
||||||
// shadowEvalContextShadow is the EvalContext that shadows the real one
|
// shadowEvalContextShadow is the EvalContext that shadows the real one
|
||||||
// and leans on that for data.
|
// and leans on that for data.
|
||||||
type shadowEvalContextShadow struct {
|
type shadowEvalContextShadow struct {
|
||||||
|
Shared *shadowEvalContextShared
|
||||||
|
|
||||||
PathValue []string
|
PathValue []string
|
||||||
Providers map[string]ResourceProvider
|
Providers map[string]ResourceProvider
|
||||||
DiffValue *Diff
|
DiffValue *Diff
|
||||||
|
@ -92,6 +105,9 @@ type shadowEvalContextShadow struct {
|
||||||
StateValue *State
|
StateValue *State
|
||||||
StateLock *sync.RWMutex
|
StateLock *sync.RWMutex
|
||||||
|
|
||||||
|
// The collection of errors that were found during the shadow run
|
||||||
|
Error error
|
||||||
|
|
||||||
// Fields relating to closing the context. Closing signals that
|
// Fields relating to closing the context. Closing signals that
|
||||||
// the execution of the real context completed.
|
// the execution of the real context completed.
|
||||||
closeLock sync.Mutex
|
closeLock sync.Mutex
|
||||||
|
@ -117,11 +133,6 @@ type shadowEvalContextShared struct {
|
||||||
ProviderWaiters map[string]*sync.Cond
|
ProviderWaiters map[string]*sync.Cond
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *shadowEvalContextShadow) Close() error {
|
|
||||||
// TODO
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *shadowEvalContextShadow) Path() []string {
|
func (c *shadowEvalContextShadow) Path() []string {
|
||||||
return c.PathValue
|
return c.PathValue
|
||||||
}
|
}
|
||||||
|
@ -133,16 +144,34 @@ func (c *shadowEvalContextShadow) Hook(f func(Hook) (HookAction, error)) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *shadowEvalContextShadow) Input() UIInput {
|
|
||||||
// TODO
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *shadowEvalContextShadow) InitProvider(n string) (ResourceProvider, error) {
|
func (c *shadowEvalContextShadow) InitProvider(n string) (ResourceProvider, error) {
|
||||||
// Initialize our shadow provider here. We also wait for the
|
// Initialize our shadow provider here. We also wait for the
|
||||||
// real context to initialize the same provider. If it doesn't
|
// real context to initialize the same provider. If it doesn't
|
||||||
// before close, then an error is reported.
|
// before close, then an error is reported.
|
||||||
|
|
||||||
|
c.Shared.Lock()
|
||||||
|
defer c.Shared.Unlock()
|
||||||
|
|
||||||
|
// We must only initialize once
|
||||||
|
if _, ok := c.Providers[n]; ok {
|
||||||
|
c.Error = multierror.Append(c.Error, fmt.Errorf(
|
||||||
|
"Provider %q already initialized", n))
|
||||||
|
return nil, errShadow
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have the provider yet, wait for it to initialize
|
||||||
|
if _, ok := c.Shared.Providers[n]; !ok {
|
||||||
|
cond := c.Shared.ProviderWaiters[n]
|
||||||
|
if cond == nil {
|
||||||
|
cond = sync.NewCond(&c.Shared.Mutex)
|
||||||
|
c.Shared.ProviderWaiters[n] = cond
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: break on close
|
||||||
|
cond.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have the provider, set it up if it isn't in our list
|
||||||
// TODO: shadow provider
|
// TODO: shadow provider
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -156,6 +185,10 @@ func (c *shadowEvalContextShadow) State() (*State, *sync.RWMutex) {
|
||||||
return c.StateValue, c.StateLock
|
return c.StateValue, c.StateLock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: All the functions below are EvalContext functions that must be impl.
|
||||||
|
|
||||||
|
func (c *shadowEvalContextShadow) Close() error { return nil }
|
||||||
|
func (c *shadowEvalContextShadow) Input() UIInput { return nil }
|
||||||
func (c *shadowEvalContextShadow) Provider(n string) ResourceProvider { return nil }
|
func (c *shadowEvalContextShadow) Provider(n string) ResourceProvider { return nil }
|
||||||
func (c *shadowEvalContextShadow) CloseProvider(n string) error { return nil }
|
func (c *shadowEvalContextShadow) CloseProvider(n string) error { return nil }
|
||||||
func (c *shadowEvalContextShadow) ConfigureProvider(string, *ResourceConfig) error { return nil }
|
func (c *shadowEvalContextShadow) ConfigureProvider(string, *ResourceConfig) error { return nil }
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/hashicorp/terraform/helper/shadow"
|
||||||
|
)
|
||||||
|
|
||||||
|
// shadowResourceProvider implements ResourceProvider for the shadow
|
||||||
|
// eval context defined in eval_context_shadow.go.
|
||||||
|
//
|
||||||
|
// This is used to verify behavior with a real provider. This shouldn't
|
||||||
|
// be used directly.
|
||||||
|
type shadowResourceProvider interface {
|
||||||
|
ResourceProvider
|
||||||
|
|
||||||
|
// CloseShadow should be called when the _real_ side is complete.
|
||||||
|
// This will immediately end any blocked calls and return any errors.
|
||||||
|
//
|
||||||
|
// Any operations on the shadow provider after this is undefined. It
|
||||||
|
// could be fine, it could result in crashes, etc. Do not use the
|
||||||
|
// shadow after this is called.
|
||||||
|
CloseShadow() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// newShadowResourceProvider creates a new shadowed ResourceProvider.
|
||||||
|
//
|
||||||
|
// This will assume a well behaved real ResourceProvider. For example,
|
||||||
|
// it assumes that the `Resources` call underneath doesn't change values
|
||||||
|
// since once it is called on the real provider, it will be cached and
|
||||||
|
// returned in the shadow since number of calls to that shouldn't affect
|
||||||
|
// actual behavior.
|
||||||
|
//
|
||||||
|
// However, with calls like Apply, call order is taken into account,
|
||||||
|
// parameters are checked for equality, etc.
|
||||||
|
func newShadowResourceProvider(p ResourceProvider) (ResourceProvider, shadowResourceProvider) {
|
||||||
|
// Create the shared data
|
||||||
|
shared := shadowResourceProviderShared{}
|
||||||
|
|
||||||
|
// Create the real provider that does actual work
|
||||||
|
real := &shadowResourceProviderReal{
|
||||||
|
ResourceProvider: p,
|
||||||
|
Shared: &shared,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the shadow that watches the real value
|
||||||
|
shadow := &shadowResourceProviderShadow{
|
||||||
|
Shared: &shared,
|
||||||
|
}
|
||||||
|
|
||||||
|
return real, shadow
|
||||||
|
}
|
||||||
|
|
||||||
|
// shadowResourceProviderReal is the real resource provider. Function calls
|
||||||
|
// to this will perform real work. This records the parameters and return
|
||||||
|
// values and call order for the shadow to reproduce.
|
||||||
|
type shadowResourceProviderReal struct {
|
||||||
|
ResourceProvider
|
||||||
|
|
||||||
|
Shared *shadowResourceProviderShared
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderReal) Resources() []ResourceType {
|
||||||
|
result := p.ResourceProvider.Resources()
|
||||||
|
p.Shared.Resources.SetValue(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderReal) DataSources() []DataSource {
|
||||||
|
result := p.ResourceProvider.DataSources()
|
||||||
|
p.Shared.DataSources.SetValue(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderReal) Close() error {
|
||||||
|
var result error
|
||||||
|
if c, ok := p.ResourceProvider.(ResourceProviderCloser); ok {
|
||||||
|
result = c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Shared.CloseErr.SetValue(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// shadowResourceProviderShadow is the shadow resource provider. Function
|
||||||
|
// calls never affect real resources. This is paired with the "real" side
|
||||||
|
// which must be called properly to enable recording.
|
||||||
|
type shadowResourceProviderShadow struct {
|
||||||
|
Shared *shadowResourceProviderShared
|
||||||
|
|
||||||
|
Error error // Error is the list of errors from the shadow
|
||||||
|
ErrorLock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type shadowResourceProviderShared struct {
|
||||||
|
CloseErr shadow.Value
|
||||||
|
Input shadow.Value
|
||||||
|
Resources shadow.Value
|
||||||
|
DataSources shadow.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) CloseShadow() error { return nil }
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Resources() []ResourceType {
|
||||||
|
v := p.Shared.Resources.Value()
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.([]ResourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) DataSources() []DataSource {
|
||||||
|
v := p.Shared.DataSources.Value()
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.([]DataSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Close() error {
|
||||||
|
v := p.Shared.CloseErr.Value()
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type shadowResourceProviderInput struct {
|
||||||
|
Config *ResourceConfig
|
||||||
|
Result *ResourceConfig
|
||||||
|
ResultErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Input(
|
||||||
|
input UIInput, c *ResourceConfig) (*ResourceConfig, error) {
|
||||||
|
// Get the result of the input call
|
||||||
|
raw := p.Shared.Input.Value()
|
||||||
|
if raw == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result, ok := raw.(*shadowResourceProviderInput)
|
||||||
|
if !ok {
|
||||||
|
p.ErrorLock.Lock()
|
||||||
|
defer p.ErrorLock.Unlock()
|
||||||
|
p.Error = multierror.Append(p.Error, fmt.Errorf(
|
||||||
|
"Unknown 'input' shadow value: %#v", raw))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the parameters, which should be identical
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Return the results
|
||||||
|
return result.Result, result.ResultErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// TODO
|
||||||
|
// TODO
|
||||||
|
// TODO
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Validate(c *ResourceConfig) ([]string, []error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) ValidateResource(t string, c *ResourceConfig) ([]string, []error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Configure(c *ResourceConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Apply(
|
||||||
|
info *InstanceInfo,
|
||||||
|
state *InstanceState,
|
||||||
|
diff *InstanceDiff) (*InstanceState, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Diff(
|
||||||
|
info *InstanceInfo,
|
||||||
|
state *InstanceState,
|
||||||
|
desired *ResourceConfig) (*InstanceDiff, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) Refresh(
|
||||||
|
info *InstanceInfo,
|
||||||
|
s *InstanceState) (*InstanceState, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) ValidateDataSource(t string, c *ResourceConfig) ([]string, []error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) ReadDataDiff(
|
||||||
|
info *InstanceInfo,
|
||||||
|
desired *ResourceConfig) (*InstanceDiff, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *shadowResourceProviderShadow) ReadDataApply(
|
||||||
|
info *InstanceInfo,
|
||||||
|
d *InstanceDiff) (*InstanceState, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
Loading…
Reference in New Issue