func (bs *BaseStack) apply(ctx context.Context, req *stack.ApplyRequest) error { log := bs.Log.New(req.StackID) bs.Eventer.Push(&eventer.Event{ Message: bs.Req.Method + " started", Status: machinestate.Building, }) go func() { finalEvent := &eventer.Event{ Message: bs.Req.Method + " finished", Status: machinestate.Running, Percentage: 100, } start := time.Now() modelhelper.SetStackState(req.StackID, "Stack building started", stackstate.Building) log.Info("======> %s started <======", strings.ToUpper(bs.Req.Method)) if err := bs.applyAsync(ctx, req); err != nil { modelhelper.SetStackState(req.StackID, "Stack building failed", stackstate.NotInitialized) finalEvent.Status = machinestate.NotInitialized // don't pass the error directly to the eventer, mask it to avoid // error leaking to the client. We just log it here. finalEvent.Error = err.Error() log.Error("======> %s finished with error (time: %s): '%s' <======", strings.ToUpper(bs.Req.Method), time.Since(start), err.Error()) } else { modelhelper.SetStackState(req.StackID, "Stack building finished", stackstate.Initialized) log.Info("======> %s finished (time: %s) <======", strings.ToUpper(bs.Req.Method), time.Since(start)) } bs.Eventer.Push(finalEvent) }() return nil }
func (bs *BaseStack) destroy(ctx context.Context, req *stack.ApplyRequest) error { log := bs.Log.New(req.StackID) bs.Eventer.Push(&eventer.Event{ Message: bs.Req.Method + " started", Status: machinestate.Terminating, }) start := time.Now() modelhelper.SetStackState(req.StackID, "Stack destroying started", stackstate.Destroying) log.Info("======> %s (destroy) started <======", strings.ToUpper(bs.Req.Method)) bs.Eventer.Push(&eventer.Event{ Message: "Fetching and validating machines", Percentage: 20, Status: machinestate.Terminating, }) if err := bs.Builder.BuildMachines(ctx); err != nil { return err } if err := bs.Builder.Database.Detach(); err != nil { return err } // This part is done asynchronously. go func() { finalEvent := &eventer.Event{ Message: bs.Req.Method + " finished", Percentage: 100, Status: machinestate.Terminated, } err := bs.destroyAsync(ctx, req) if err != nil { // don't pass the error directly to the eventer, mask it to avoid // error leaking to the client. We just log it here. finalEvent.Error = err.Error() log.Error("======> %s finished with error (time: %s): '%s' <======", strings.ToUpper(bs.Req.Method), time.Since(start), err) } else { log.Info("======> %s finished (time: %s) <======", strings.ToUpper(bs.Req.Method), time.Since(start)) } bs.Eventer.Push(finalEvent) }() return nil }
// Detach implements the Database interface. func (db *mongoDatabase) Detach(opts *DetachOptions) error { const detachReason = "Stack destroy requested." if err := opts.Valid(); err != nil { return err } // 1) Detach stack from user. Failure is critical, as upon return // a user would not be able to create new stack. detachStack := modelhelper.Selector{ "targetName": "JStackTemplate", "targetId": opts.Stack.BaseStackId, "sourceName": "JAccount", "sourceId": opts.Stack.OriginId, "as": "user", } err := modelhelper.DeleteRelationships(detachStack) if err != nil && err != mgo.ErrNotFound { return err } // 2) Set stack to "destroying" state. id := opts.Stack.Id.Hex() err = modelhelper.SetStackState(id, detachReason, stackstate.Destroying) if err != nil && err != mgo.ErrNotFound { // Stack state update failure is not critical, as jComputeStack // is going to be removed either way at the end of destroy op. opts.Log.Error("unable to set stack state to %q", stackstate.Destroying) } // 3) Update counters. err = modelhelper.DecrementOrCreateCounter(opts.Stack.Group, modelhelper.CounterStacks, 1) if err != nil { // Counter update is not crucial, nevertheless we log an error // if updating failed for whatever reason. opts.Log.Error("failure updating %q counter", modelhelper.CounterStacks) } err = modelhelper.DecrementOrCreateCounter(opts.Stack.Group, modelhelper.CounterInstances, len(opts.Stack.Machines)) if err != nil { // Counter update is not crucial, nevertheless we log an error // if updating failed for whatever reason. opts.Log.Error("failure updating %q counter", modelhelper.CounterInstances) } // 4) Detach machines from user. detachMachines := bson.M{ "$set": bson.M{ "status.state": "Terminated", "users": []interface{}{}, }, } err = modelhelper.UpdateMachines(detachMachines, opts.Stack.Machines...) if err != nil && err != mgo.ErrNotFound { // Detaching users from machines error is not critical, as the jMachine // documents are going to be deleted at the end of destroy operation. // Nevertheless we log error in case a troubleshooting would be needed. opts.Log.Error("detaching users from machines failed: %s", err) } return nil }