diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/eval_apply.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/eval_apply.go | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go b/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go new file mode 100644 index 00000000..2f6a4973 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go @@ -0,0 +1,359 @@ +package terraform + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform/config" +) + +// EvalApply is an EvalNode implementation that writes the diff to +// the full diff. +type EvalApply struct { + Info *InstanceInfo + State **InstanceState + Diff **InstanceDiff + Provider *ResourceProvider + Output **InstanceState + CreateNew *bool + Error *error +} + +// TODO: test +func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) { + diff := *n.Diff + provider := *n.Provider + state := *n.State + + // If we have no diff, we have nothing to do! + if diff.Empty() { + log.Printf( + "[DEBUG] apply: %s: diff is empty, doing nothing.", n.Info.Id) + return nil, nil + } + + // Remove any output values from the diff + for k, ad := range diff.CopyAttributes() { + if ad.Type == DiffAttrOutput { + diff.DelAttribute(k) + } + } + + // If the state is nil, make it non-nil + if state == nil { + state = new(InstanceState) + } + state.init() + + // Flag if we're creating a new instance + if n.CreateNew != nil { + *n.CreateNew = state.ID == "" && !diff.GetDestroy() || diff.RequiresNew() + } + + // With the completed diff, apply! + log.Printf("[DEBUG] apply: %s: executing Apply", n.Info.Id) + state, err := provider.Apply(n.Info, state, diff) + if state == nil { + state = new(InstanceState) + } + state.init() + + // Force the "id" attribute to be our ID + if state.ID != "" { + state.Attributes["id"] = state.ID + } + + // If the value is the unknown variable value, then it is an error. + // In this case we record the error and remove it from the state + for ak, av := range state.Attributes { + if av == config.UnknownVariableValue { + err = multierror.Append(err, fmt.Errorf( + "Attribute with unknown value: %s", ak)) + delete(state.Attributes, ak) + } + } + + // Write the final state + if n.Output != nil { + *n.Output = state + } + + // If there are no errors, then we append it to our output error + // if we have one, otherwise we just output it. + if err != nil { + if n.Error != nil { + helpfulErr := fmt.Errorf("%s: %s", n.Info.Id, err.Error()) + *n.Error = multierror.Append(*n.Error, helpfulErr) + } else { + return nil, err + } + } + + return nil, nil +} + +// EvalApplyPre is an EvalNode implementation that does the pre-Apply work +type EvalApplyPre struct { + Info *InstanceInfo + State **InstanceState + Diff **InstanceDiff +} + +// TODO: test +func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) { + state := *n.State + diff := *n.Diff + + // If the state is nil, make it non-nil + if state == nil { + state = new(InstanceState) + } + state.init() + + { + // Call post-apply hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreApply(n.Info, state, diff) + }) + if err != nil { + return nil, err + } + } + + return nil, nil +} + +// EvalApplyPost is an EvalNode implementation that does the post-Apply work +type EvalApplyPost struct { + Info *InstanceInfo + State **InstanceState + Error *error +} + +// TODO: test +func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) { + state := *n.State + + { + // Call post-apply hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostApply(n.Info, state, *n.Error) + }) + if err != nil { + return nil, err + } + } + + return nil, *n.Error +} + +// EvalApplyProvisioners is an EvalNode implementation that executes +// the provisioners for a resource. +// +// TODO(mitchellh): This should probably be split up into a more fine-grained +// ApplyProvisioner (single) that is looped over. +type EvalApplyProvisioners struct { + Info *InstanceInfo + State **InstanceState + Resource *config.Resource + InterpResource *Resource + CreateNew *bool + Error *error + + // When is the type of provisioner to run at this point + When config.ProvisionerWhen +} + +// TODO: test +func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) { + state := *n.State + + if n.CreateNew != nil && !*n.CreateNew { + // If we're not creating a new resource, then don't run provisioners + return nil, nil + } + + provs := n.filterProvisioners() + if len(provs) == 0 { + // We have no provisioners, so don't do anything + return nil, nil + } + + // taint tells us whether to enable tainting. + taint := n.When == config.ProvisionerWhenCreate + + if n.Error != nil && *n.Error != nil { + if taint { + state.Tainted = true + } + + // We're already tainted, so just return out + return nil, nil + } + + { + // Call pre hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreProvisionResource(n.Info, state) + }) + if err != nil { + return nil, err + } + } + + // If there are no errors, then we append it to our output error + // if we have one, otherwise we just output it. + err := n.apply(ctx, provs) + if err != nil { + if taint { + state.Tainted = true + } + + if n.Error != nil { + *n.Error = multierror.Append(*n.Error, err) + } else { + return nil, err + } + } + + { + // Call post hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostProvisionResource(n.Info, state) + }) + if err != nil { + return nil, err + } + } + + return nil, nil +} + +// filterProvisioners filters the provisioners on the resource to only +// the provisioners specified by the "when" option. +func (n *EvalApplyProvisioners) filterProvisioners() []*config.Provisioner { + // Fast path the zero case + if n.Resource == nil { + return nil + } + + if len(n.Resource.Provisioners) == 0 { + return nil + } + + result := make([]*config.Provisioner, 0, len(n.Resource.Provisioners)) + for _, p := range n.Resource.Provisioners { + if p.When == n.When { + result = append(result, p) + } + } + + return result +} + +func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*config.Provisioner) error { + state := *n.State + + // Store the original connection info, restore later + origConnInfo := state.Ephemeral.ConnInfo + defer func() { + state.Ephemeral.ConnInfo = origConnInfo + }() + + for _, prov := range provs { + // Get the provisioner + provisioner := ctx.Provisioner(prov.Type) + + // Interpolate the provisioner config + provConfig, err := ctx.Interpolate(prov.RawConfig.Copy(), n.InterpResource) + if err != nil { + return err + } + + // Interpolate the conn info, since it may contain variables + connInfo, err := ctx.Interpolate(prov.ConnInfo.Copy(), n.InterpResource) + if err != nil { + return err + } + + // Merge the connection information + overlay := make(map[string]string) + if origConnInfo != nil { + for k, v := range origConnInfo { + overlay[k] = v + } + } + for k, v := range connInfo.Config { + switch vt := v.(type) { + case string: + overlay[k] = vt + case int64: + overlay[k] = strconv.FormatInt(vt, 10) + case int32: + overlay[k] = strconv.FormatInt(int64(vt), 10) + case int: + overlay[k] = strconv.FormatInt(int64(vt), 10) + case float32: + overlay[k] = strconv.FormatFloat(float64(vt), 'f', 3, 32) + case float64: + overlay[k] = strconv.FormatFloat(vt, 'f', 3, 64) + case bool: + overlay[k] = strconv.FormatBool(vt) + default: + overlay[k] = fmt.Sprintf("%v", vt) + } + } + state.Ephemeral.ConnInfo = overlay + + { + // Call pre hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreProvision(n.Info, prov.Type) + }) + if err != nil { + return err + } + } + + // The output function + outputFn := func(msg string) { + ctx.Hook(func(h Hook) (HookAction, error) { + h.ProvisionOutput(n.Info, prov.Type, msg) + return HookActionContinue, nil + }) + } + + // Invoke the Provisioner + output := CallbackUIOutput{OutputFn: outputFn} + applyErr := provisioner.Apply(&output, state, provConfig) + + // Call post hook + hookErr := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostProvision(n.Info, prov.Type, applyErr) + }) + + // Handle the error before we deal with the hook + if applyErr != nil { + // Determine failure behavior + switch prov.OnFailure { + case config.ProvisionerOnFailureContinue: + log.Printf( + "[INFO] apply: %s [%s]: error during provision, continue requested", + n.Info.Id, prov.Type) + + case config.ProvisionerOnFailureFail: + return applyErr + } + } + + // Deal with the hook + if hookErr != nil { + return hookErr + } + } + + return nil + +} |