terraform: minimal applies work!
This commit is contained in:
parent
dc9b9eee44
commit
5828a0a9ac
|
@ -362,7 +362,9 @@ func (c *Context) Apply() (*State, error) {
|
||||||
graph, err = c.Graph(&ContextGraphOpts{Validate: true})
|
graph, err = c.Graph(&ContextGraphOpts{Validate: true})
|
||||||
} else {
|
} else {
|
||||||
graph, err = (&ApplyGraphBuilder{
|
graph, err = (&ApplyGraphBuilder{
|
||||||
|
Config: c.module,
|
||||||
Diff: c.diff,
|
Diff: c.diff,
|
||||||
|
State: c.state,
|
||||||
Providers: c.providersList(),
|
Providers: c.providersList(),
|
||||||
Provisioners: c.provisionersList(),
|
Provisioners: c.provisionersList(),
|
||||||
}).Build(RootModulePath)
|
}).Build(RootModulePath)
|
||||||
|
|
|
@ -18,6 +18,9 @@ type ApplyGraphBuilder struct {
|
||||||
// Diff is the diff to apply.
|
// Diff is the diff to apply.
|
||||||
Diff *Diff
|
Diff *Diff
|
||||||
|
|
||||||
|
// State is the current state
|
||||||
|
State *State
|
||||||
|
|
||||||
// Providers is the list of providers supported.
|
// Providers is the list of providers supported.
|
||||||
Providers []string
|
Providers []string
|
||||||
|
|
||||||
|
@ -37,7 +40,11 @@ func (b *ApplyGraphBuilder) Build(path []string) (*Graph, error) {
|
||||||
func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
||||||
steps := []GraphTransformer{
|
steps := []GraphTransformer{
|
||||||
// Creates all the nodes represented in the diff.
|
// Creates all the nodes represented in the diff.
|
||||||
&DiffTransformer{Diff: b.Diff},
|
&DiffTransformer{
|
||||||
|
Diff: b.Diff,
|
||||||
|
Config: b.Config,
|
||||||
|
State: b.State,
|
||||||
|
},
|
||||||
|
|
||||||
// Create all the providers
|
// Create all the providers
|
||||||
&MissingProviderTransformer{Providers: b.Providers},
|
&MissingProviderTransformer{Providers: b.Providers},
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,3 +33,206 @@ func (n *NodeApplyableResource) ProvidedBy() []string {
|
||||||
// Use our type
|
// Use our type
|
||||||
return []string{resourceProvider(n.Addr.Type, "")}
|
return []string{resourceProvider(n.Addr.Type, "")}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphNodeEvalable
|
||||||
|
func (n *NodeApplyableResource) EvalTree() EvalNode {
|
||||||
|
// stateId is the ID to put into the state
|
||||||
|
stateId := n.Addr.stateId()
|
||||||
|
if n.Addr.Index > -1 {
|
||||||
|
stateId = fmt.Sprintf("%s.%d", stateId, n.Addr.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the instance info. More of this will be populated during eval
|
||||||
|
info := &InstanceInfo{
|
||||||
|
Id: stateId,
|
||||||
|
Type: n.Addr.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the resource for eval
|
||||||
|
resource := &Resource{
|
||||||
|
Name: n.Addr.Name,
|
||||||
|
Type: n.Addr.Type,
|
||||||
|
CountIndex: n.Addr.Index,
|
||||||
|
}
|
||||||
|
if resource.CountIndex < 0 {
|
||||||
|
resource.CountIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the dependencies for the state. We use some older
|
||||||
|
// code for this that we've used for a long time.
|
||||||
|
var stateDeps []string
|
||||||
|
{
|
||||||
|
oldN := &graphNodeExpandedResource{Resource: n.Config}
|
||||||
|
stateDeps = oldN.StateDependencies()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a bunch of variables that are used for state during
|
||||||
|
// evaluation. Most of this are written to by-address below.
|
||||||
|
var provider ResourceProvider
|
||||||
|
var diff *InstanceDiff
|
||||||
|
var state *InstanceState
|
||||||
|
var resourceConfig *ResourceConfig
|
||||||
|
var err error
|
||||||
|
var createNew bool
|
||||||
|
var createBeforeDestroyEnabled bool
|
||||||
|
|
||||||
|
return &EvalSequence{
|
||||||
|
Nodes: []EvalNode{
|
||||||
|
// Build the instance info
|
||||||
|
&EvalInstanceInfo{
|
||||||
|
Info: info,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get the saved diff for apply
|
||||||
|
&EvalReadDiff{
|
||||||
|
Name: stateId,
|
||||||
|
Diff: &diff,
|
||||||
|
},
|
||||||
|
|
||||||
|
// We don't want to do any destroys
|
||||||
|
&EvalIf{
|
||||||
|
If: func(ctx EvalContext) (bool, error) {
|
||||||
|
if diff == nil {
|
||||||
|
return true, EvalEarlyExitError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff.GetDestroy() && diff.GetAttributesLen() == 0 {
|
||||||
|
return true, EvalEarlyExitError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff.SetDestroy(false)
|
||||||
|
return true, nil
|
||||||
|
},
|
||||||
|
Then: EvalNoop{},
|
||||||
|
},
|
||||||
|
|
||||||
|
&EvalIf{
|
||||||
|
If: func(ctx EvalContext) (bool, error) {
|
||||||
|
destroy := false
|
||||||
|
if diff != nil {
|
||||||
|
destroy = diff.GetDestroy() || diff.RequiresNew()
|
||||||
|
}
|
||||||
|
|
||||||
|
createBeforeDestroyEnabled =
|
||||||
|
n.Config.Lifecycle.CreateBeforeDestroy &&
|
||||||
|
destroy
|
||||||
|
|
||||||
|
return createBeforeDestroyEnabled, nil
|
||||||
|
},
|
||||||
|
Then: &EvalDeposeState{
|
||||||
|
Name: stateId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
&EvalInterpolate{
|
||||||
|
Config: n.Config.RawConfig.Copy(),
|
||||||
|
Resource: resource,
|
||||||
|
Output: &resourceConfig,
|
||||||
|
},
|
||||||
|
&EvalGetProvider{
|
||||||
|
Name: n.ProvidedBy()[0],
|
||||||
|
Output: &provider,
|
||||||
|
},
|
||||||
|
&EvalReadState{
|
||||||
|
Name: stateId,
|
||||||
|
Output: &state,
|
||||||
|
},
|
||||||
|
// Re-run validation to catch any errors we missed, e.g. type
|
||||||
|
// mismatches on computed values.
|
||||||
|
&EvalValidateResource{
|
||||||
|
Provider: &provider,
|
||||||
|
Config: &resourceConfig,
|
||||||
|
ResourceName: n.Config.Name,
|
||||||
|
ResourceType: n.Config.Type,
|
||||||
|
ResourceMode: n.Config.Mode,
|
||||||
|
IgnoreWarnings: true,
|
||||||
|
},
|
||||||
|
&EvalDiff{
|
||||||
|
Info: info,
|
||||||
|
Config: &resourceConfig,
|
||||||
|
Resource: n.Config,
|
||||||
|
Provider: &provider,
|
||||||
|
Diff: &diff,
|
||||||
|
State: &state,
|
||||||
|
OutputDiff: &diff,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get the saved diff
|
||||||
|
&EvalReadDiff{
|
||||||
|
Name: stateId,
|
||||||
|
Diff: &diff,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Compare the diffs
|
||||||
|
&EvalCompareDiff{
|
||||||
|
Info: info,
|
||||||
|
One: &diff,
|
||||||
|
Two: &diff,
|
||||||
|
},
|
||||||
|
|
||||||
|
&EvalGetProvider{
|
||||||
|
Name: n.ProvidedBy()[0],
|
||||||
|
Output: &provider,
|
||||||
|
},
|
||||||
|
&EvalReadState{
|
||||||
|
Name: stateId,
|
||||||
|
Output: &state,
|
||||||
|
},
|
||||||
|
&EvalApply{
|
||||||
|
Info: info,
|
||||||
|
State: &state,
|
||||||
|
Diff: &diff,
|
||||||
|
Provider: &provider,
|
||||||
|
Output: &state,
|
||||||
|
Error: &err,
|
||||||
|
CreateNew: &createNew,
|
||||||
|
},
|
||||||
|
&EvalWriteState{
|
||||||
|
Name: stateId,
|
||||||
|
ResourceType: n.Config.Type,
|
||||||
|
Provider: n.Config.Provider,
|
||||||
|
Dependencies: stateDeps,
|
||||||
|
State: &state,
|
||||||
|
},
|
||||||
|
&EvalApplyProvisioners{
|
||||||
|
Info: info,
|
||||||
|
State: &state,
|
||||||
|
Resource: n.Config,
|
||||||
|
InterpResource: resource,
|
||||||
|
CreateNew: &createNew,
|
||||||
|
Error: &err,
|
||||||
|
},
|
||||||
|
&EvalIf{
|
||||||
|
If: func(ctx EvalContext) (bool, error) {
|
||||||
|
return createBeforeDestroyEnabled && err != nil, nil
|
||||||
|
},
|
||||||
|
Then: &EvalUndeposeState{
|
||||||
|
Name: stateId,
|
||||||
|
State: &state,
|
||||||
|
},
|
||||||
|
Else: &EvalWriteState{
|
||||||
|
Name: stateId,
|
||||||
|
ResourceType: n.Config.Type,
|
||||||
|
Provider: n.Config.Provider,
|
||||||
|
Dependencies: stateDeps,
|
||||||
|
State: &state,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// We clear the diff out here so that future nodes
|
||||||
|
// don't see a diff that is already complete. There
|
||||||
|
// is no longer a diff!
|
||||||
|
&EvalWriteDiff{
|
||||||
|
Name: stateId,
|
||||||
|
Diff: nil,
|
||||||
|
},
|
||||||
|
|
||||||
|
&EvalApplyPost{
|
||||||
|
Info: info,
|
||||||
|
State: &state,
|
||||||
|
Error: &err,
|
||||||
|
},
|
||||||
|
&EvalUpdateStateHook{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,24 @@ func (r *ResourceAddress) String() string {
|
||||||
return strings.Join(result, ".")
|
return strings.Join(result, ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stateId returns the ID that this resource should be entered with
|
||||||
|
// in the state. This is also used for diffs. In the future, we'd like to
|
||||||
|
// move away from this string field so I don't export this.
|
||||||
|
// TODO: test
|
||||||
|
func (r *ResourceAddress) stateId() string {
|
||||||
|
result := fmt.Sprintf("%s.%s", r.Type, r.Name)
|
||||||
|
switch r.Mode {
|
||||||
|
case config.ManagedResourceMode:
|
||||||
|
// Done
|
||||||
|
case config.DataResourceMode:
|
||||||
|
result = fmt.Sprintf("data.%s", result)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown resource mode: %s", r.Mode))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// parseResourceAddressConfig creates a resource address from a config.Resource
|
// parseResourceAddressConfig creates a resource address from a config.Resource
|
||||||
func parseResourceAddressConfig(r *config.Resource) (*ResourceAddress, error) {
|
func parseResourceAddressConfig(r *config.Resource) (*ResourceAddress, error) {
|
||||||
return &ResourceAddress{
|
return &ResourceAddress{
|
||||||
|
|
Loading…
Reference in New Issue