func (bs *BaseStack) destroyAsync(ctx context.Context, req *stack.ApplyRequest) error { if rt, ok := stack.RequestTraceFromContext(ctx); ok { defer rt.Send() } bs.Eventer.Push(&eventer.Event{ Message: "Fetching and validating credentials", Percentage: 30, Status: machinestate.Terminating, }) credIDs := FlattenValues(bs.Builder.Stack.Credentials) bs.Log.Debug("Fetching '%d' credentials from user '%s'", len(credIDs), bs.Req.Username) if err := bs.Builder.BuildCredentials(bs.Req.Method, bs.Req.Username, req.GroupName, credIDs); err != nil { return err } bs.Log.Debug("Fetched terraform data: koding=%+v, template=%+v", bs.Builder.Koding, bs.Builder.Template) if bs.Builder.Stack.Stack.State() != stackstate.NotInitialized { bs.Log.Debug("Connection to Terraformer") tfKite, err := terraformer.Connect(bs.Session.Terraformer) if err != nil { return err } defer tfKite.Close() tfReq := &tf.TerraformRequest{ ContentID: req.GroupName + "-" + req.StackID, TraceID: bs.TraceID, } bs.Log.Debug("Calling terraform.destroy method with context: %+v", tfReq) _, err = tfKite.Destroy(tfReq) if err != nil { return err } } return bs.Builder.Database.Destroy() }
// HandleApply builds and expands compute stack template for the given ID and // sends an apply request to terraformer. // // When destroy=false, building and expanding the stack prior to // terraformer request is done asynchronously, and the result of // that operation is communicated back with eventer. // // When destroy=true, fetching machines from DB is done synchronously, as // as soon as Apply method returns, allowed user list for each machine // is zeroed, which could make the destroy oepration to fail - we // first build machines and rest of the destroy is perfomed asynchronously. func (bs *BaseStack) HandleApply(ctx context.Context) (interface{}, error) { arg, ok := ctx.Value(stack.ApplyRequestKey).(*stack.ApplyRequest) if !ok { arg = &stack.ApplyRequest{} if err := bs.Req.Args.One().Unmarshal(arg); err != nil { return nil, err } } if err := arg.Valid(); err != nil { return nil, err } err := bs.Builder.BuildStack(arg.StackID, arg.Credentials) if err != nil && !(arg.Destroy && models.IsNotFound(err, "jStackTemplate")) { return nil, err } if state := bs.Builder.Stack.Stack.State(); state.InProgress() { return nil, fmt.Errorf("State is currently %s. Please try again later", state) } if rt, ok := stack.RequestTraceFromContext(ctx); ok { rt.Hijack() } bs.Arg = arg if arg.Destroy { err = bs.destroy(ctx, arg) } else { err = bs.apply(ctx, arg) } if err != nil { return nil, err } return &stack.ControlResult{ EventId: bs.Eventer.ID(), }, nil }
func (bs *BaseStack) applyAsync(ctx context.Context, req *stack.ApplyRequest) error { if rt, ok := stack.RequestTraceFromContext(ctx); ok { defer rt.Send() } bs.Eventer.Push(&eventer.Event{ Message: "Fetching and validating machines", Percentage: 20, Status: machinestate.Building, }) if err := bs.Builder.BuildMachines(ctx); err != nil { return err } bs.Eventer.Push(&eventer.Event{ Message: "Fetching and validating credentials", Percentage: 30, Status: machinestate.Building, }) credIDs := FlattenValues(bs.Builder.Stack.Credentials) bs.Log.Debug("Fetching '%d' credentials from user '%s'", len(credIDs), bs.Req.Username) if err := bs.Builder.BuildCredentials(bs.Req.Method, bs.Req.Username, req.GroupName, credIDs); err != nil { return err } cred, err := bs.Builder.CredentialByProvider(bs.Provider.Name) if err != nil { return err } bs.Log.Debug("Fetched terraform data: koding=%+v, template=%+v", bs.Builder.Koding, bs.Builder.Template) tfKite, err := terraformer.Connect(bs.Session.Terraformer) if err != nil { return err } defer tfKite.Close() defaultContentID := req.GroupName + "-" + req.StackID bs.Log.Debug("Building template: %s", defaultContentID) if err := bs.Builder.BuildTemplate(bs.Builder.Stack.Template, defaultContentID); err != nil { return err } if len(req.Variables) != 0 { if err := bs.Builder.Template.InjectVariables("", req.Variables); err != nil { return err } } bs.Log.Debug("Stack template before injecting Koding data: %s", bs.Builder.Template) t, err := bs.stack.ApplyTemplate(cred) if err != nil { return err } if t.Key == "" { t.Key = defaultContentID } bs.Log.Debug("Stack template after injecting Koding data: %s", t) bs.Builder.Stack.Template = t.Content done := make(chan struct{}) // because apply can last long, we are going to increment the eventer's // percentage as long as we build automatically. go func() { start := 45 ticker := time.NewTicker(time.Second * 5) defer ticker.Stop() for { select { case <-done: return case <-ticker.C: if start < 70 { start += 5 } bs.Eventer.Push(&eventer.Event{ Message: "Building stack resources", Percentage: start, Status: machinestate.Building, }) } } }() tfReq := &tf.TerraformRequest{ Content: bs.Builder.Stack.Template, ContentID: t.Key, TraceID: bs.TraceID, } bs.Log.Debug("Final stack template. Calling terraform.apply method:") bs.Log.Debug("%+v", tfReq) state, err := tfKite.Apply(tfReq) close(done) if err != nil { return err } bs.Eventer.Push(&eventer.Event{ Message: "Checking VM connections", Percentage: 70, Status: machinestate.Building, }) if bs.Klients, err = bs.Planner.DialKlients(ctx, bs.KlientIDs); err != nil { return err } bs.Eventer.Push(&eventer.Event{ Message: "Updating machine settings", Percentage: 90, Status: machinestate.Building, }) err = bs.UpdateResources(state) if e := bs.Builder.UpdateStack(); e != nil && err == nil { err = e } return err }