8 "github.com/hashicorp/go-multierror"
9 "github.com/hashicorp/terraform/config"
12 // EvalApply is an EvalNode implementation that writes the diff to
14 type EvalApply struct {
18 Provider *ResourceProvider
19 Output **InstanceState
25 func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
27 provider := *n.Provider
30 // If we have no diff, we have nothing to do!
33 "[DEBUG] apply: %s: diff is empty, doing nothing.", n.Info.Id)
37 // Remove any output values from the diff
38 for k, ad := range diff.CopyAttributes() {
39 if ad.Type == DiffAttrOutput {
44 // If the state is nil, make it non-nil
46 state = new(InstanceState)
50 // Flag if we're creating a new instance
51 if n.CreateNew != nil {
52 *n.CreateNew = state.ID == "" && !diff.GetDestroy() || diff.RequiresNew()
55 // With the completed diff, apply!
56 log.Printf("[DEBUG] apply: %s: executing Apply", n.Info.Id)
57 state, err := provider.Apply(n.Info, state, diff)
59 state = new(InstanceState)
63 // Force the "id" attribute to be our ID
65 state.Attributes["id"] = state.ID
68 // If the value is the unknown variable value, then it is an error.
69 // In this case we record the error and remove it from the state
70 for ak, av := range state.Attributes {
71 if av == config.UnknownVariableValue {
72 err = multierror.Append(err, fmt.Errorf(
73 "Attribute with unknown value: %s", ak))
74 delete(state.Attributes, ak)
78 // Write the final state
83 // If there are no errors, then we append it to our output error
84 // if we have one, otherwise we just output it.
87 helpfulErr := fmt.Errorf("%s: %s", n.Info.Id, err.Error())
88 *n.Error = multierror.Append(*n.Error, helpfulErr)
97 // EvalApplyPre is an EvalNode implementation that does the pre-Apply work
98 type EvalApplyPre struct {
100 State **InstanceState
105 func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) {
109 // If the state is nil, make it non-nil
111 state = new(InstanceState)
115 if resourceHasUserVisibleApply(n.Info) {
116 // Call post-apply hook
117 err := ctx.Hook(func(h Hook) (HookAction, error) {
118 return h.PreApply(n.Info, state, diff)
128 // EvalApplyPost is an EvalNode implementation that does the post-Apply work
129 type EvalApplyPost struct {
131 State **InstanceState
136 func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) {
139 if resourceHasUserVisibleApply(n.Info) {
140 // Call post-apply hook
141 err := ctx.Hook(func(h Hook) (HookAction, error) {
142 return h.PostApply(n.Info, state, *n.Error)
152 // resourceHasUserVisibleApply returns true if the given resource is one where
153 // apply actions should be exposed to the user.
155 // Certain resources do apply actions only as an implementation detail, so
156 // these should not be advertised to code outside of this package.
157 func resourceHasUserVisibleApply(info *InstanceInfo) bool {
158 addr := info.ResourceAddress()
160 // Only managed resources have user-visible apply actions.
161 // In particular, this excludes data resources since we "apply" these
162 // only as an implementation detail of removing them from state when
163 // they are destroyed. (When reading, they don't get here at all because
164 // we present them as "Refresh" actions.)
165 return addr.Mode == config.ManagedResourceMode
168 // EvalApplyProvisioners is an EvalNode implementation that executes
169 // the provisioners for a resource.
171 // TODO(mitchellh): This should probably be split up into a more fine-grained
172 // ApplyProvisioner (single) that is looped over.
173 type EvalApplyProvisioners struct {
175 State **InstanceState
176 Resource *config.Resource
177 InterpResource *Resource
181 // When is the type of provisioner to run at this point
182 When config.ProvisionerWhen
186 func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) {
189 if n.CreateNew != nil && !*n.CreateNew {
190 // If we're not creating a new resource, then don't run provisioners
194 provs := n.filterProvisioners()
196 // We have no provisioners, so don't do anything
200 // taint tells us whether to enable tainting.
201 taint := n.When == config.ProvisionerWhenCreate
203 if n.Error != nil && *n.Error != nil {
208 // We're already tainted, so just return out
214 err := ctx.Hook(func(h Hook) (HookAction, error) {
215 return h.PreProvisionResource(n.Info, state)
222 // If there are no errors, then we append it to our output error
223 // if we have one, otherwise we just output it.
224 err := n.apply(ctx, provs)
230 *n.Error = multierror.Append(*n.Error, err)
236 err := ctx.Hook(func(h Hook) (HookAction, error) {
237 return h.PostProvisionResource(n.Info, state)
247 // filterProvisioners filters the provisioners on the resource to only
248 // the provisioners specified by the "when" option.
249 func (n *EvalApplyProvisioners) filterProvisioners() []*config.Provisioner {
250 // Fast path the zero case
251 if n.Resource == nil {
255 if len(n.Resource.Provisioners) == 0 {
259 result := make([]*config.Provisioner, 0, len(n.Resource.Provisioners))
260 for _, p := range n.Resource.Provisioners {
261 if p.When == n.When {
262 result = append(result, p)
269 func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*config.Provisioner) error {
272 // Store the original connection info, restore later
273 origConnInfo := state.Ephemeral.ConnInfo
275 state.Ephemeral.ConnInfo = origConnInfo
278 for _, prov := range provs {
279 // Get the provisioner
280 provisioner := ctx.Provisioner(prov.Type)
282 // Interpolate the provisioner config
283 provConfig, err := ctx.Interpolate(prov.RawConfig.Copy(), n.InterpResource)
288 // Interpolate the conn info, since it may contain variables
289 connInfo, err := ctx.Interpolate(prov.ConnInfo.Copy(), n.InterpResource)
294 // Merge the connection information
295 overlay := make(map[string]string)
296 if origConnInfo != nil {
297 for k, v := range origConnInfo {
301 for k, v := range connInfo.Config {
302 switch vt := v.(type) {
306 overlay[k] = strconv.FormatInt(vt, 10)
308 overlay[k] = strconv.FormatInt(int64(vt), 10)
310 overlay[k] = strconv.FormatInt(int64(vt), 10)
312 overlay[k] = strconv.FormatFloat(float64(vt), 'f', 3, 32)
314 overlay[k] = strconv.FormatFloat(vt, 'f', 3, 64)
316 overlay[k] = strconv.FormatBool(vt)
318 overlay[k] = fmt.Sprintf("%v", vt)
321 state.Ephemeral.ConnInfo = overlay
325 err := ctx.Hook(func(h Hook) (HookAction, error) {
326 return h.PreProvision(n.Info, prov.Type)
333 // The output function
334 outputFn := func(msg string) {
335 ctx.Hook(func(h Hook) (HookAction, error) {
336 h.ProvisionOutput(n.Info, prov.Type, msg)
337 return HookActionContinue, nil
341 // Invoke the Provisioner
342 output := CallbackUIOutput{OutputFn: outputFn}
343 applyErr := provisioner.Apply(&output, state, provConfig)
346 hookErr := ctx.Hook(func(h Hook) (HookAction, error) {
347 return h.PostProvision(n.Info, prov.Type, applyErr)
350 // Handle the error before we deal with the hook
352 // Determine failure behavior
353 switch prov.OnFailure {
354 case config.ProvisionerOnFailureContinue:
356 "[INFO] apply: %s [%s]: error during provision, continue requested",
357 n.Info.Id, prov.Type)
359 case config.ProvisionerOnFailureFail:
364 // Deal with the hook