func (bs *BaseStack) Plan() (stack.Machines, error) { out, err := bs.Builder.Template.JsonOutput() if err != nil { return nil, err } tfKite, err := terraformer.Connect(bs.Session.Terraformer) if err != nil { return nil, err } defer tfKite.Close() tfReq := &tf.TerraformRequest{ Content: out, ContentID: bs.Req.Username + "-" + bs.Arg.(*stack.PlanRequest).StackTemplateID, TraceID: bs.TraceID, } bs.Log.Debug("Calling plan with content: %+v", tfReq) plan, err := tfKite.Plan(tfReq) if err != nil { return nil, err } return bs.Planner.MachinesFromPlan(plan) }
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() }
func (bs *BaseStack) bootstrap(arg *stack.BootstrapRequest) (interface{}, error) { if arg.Destroy { bs.Log.Debug("Bootstrap destroy is called") } else { bs.Log.Debug("Bootstrap apply is called") } if err := bs.Builder.BuildCredentials(bs.Req.Method, bs.Req.Username, arg.GroupName, arg.Identifiers); err != nil { return nil, err } bs.Log.Debug("Connecting to terraformer kite") tfKite, err := terraformer.Connect(bs.Session.Terraformer) if err != nil { return nil, err } defer tfKite.Close() bs.Log.Debug("Iterating over credentials") updatedCreds := make(map[string]*stack.Credential) for _, cred := range bs.Builder.Credentials { if cred.Provider != bs.Planner.Provider { continue } var bootstrapMixin interface{} if bs.SSHKeyPairFunc != nil { keypair, err := stack.GenerateSSHKeyPair() if err != nil { return nil, fmt.Errorf("error generating SSH key-pair: %s", err) } if err := bs.SSHKeyPairFunc(keypair); err != nil { return nil, fmt.Errorf("error injecting SSH key-pair: %s", err) } bootstrapMixin = keypair } templates, err := bs.stack.BootstrapTemplates(cred) if err != nil { return nil, err } destroyUniq := make(map[string]struct{}) // protects from double-destroy for _, tmpl := range templates { if tmpl.Key == "" { tmpl.Key = bs.Planner.Provider + "-" + arg.GroupName + "-" + cred.Identifier } if arg.Destroy { if _, ok := destroyUniq[tmpl.Key]; !ok { bs.Log.Info("Destroying bootstrap resources belonging to identifier '%s'", cred.Identifier) _, err := tfKite.Destroy(&tf.TerraformRequest{ ContentID: tmpl.Key, TraceID: bs.TraceID, }) if err != nil { return nil, err } cred.Bootstrap = bs.Provider.newBootstrap() destroyUniq[tmpl.Key] = struct{}{} } } else { bs.Log.Debug("Bootstrap template for %s: %s", cred.Identifier, tmpl) // TODO(rjeczalik): use []byte for templates to avoid allocations if err := bs.Builder.BuildTemplate(string(tmpl.Content), tmpl.Key); err != nil { return nil, fmt.Errorf("error building template: %s", err) } content, err := bs.Builder.Template.JsonOutput() if err != nil { return nil, err } bs.Log.Debug("Final bootstrap template: %s", content) state, err := tfKite.Apply(&tf.TerraformRequest{ Content: content, ContentID: tmpl.Key, TraceID: bs.TraceID, }) if err != nil { return nil, err } bs.Log.Debug("[%s] state.RootModule().Outputs = %+v\n", cred.Identifier, state.RootModule().Outputs) if err := bs.Builder.Object.Decode(state.RootModule().Outputs, cred.Bootstrap); err != nil { return nil, err } bs.Log.Debug("[%s] resp = %+v\n", cred.Identifier, cred.Bootstrap) if v, ok := cred.Bootstrap.(stack.Validator); ok { if err := v.Valid(); err != nil { return nil, fmt.Errorf("invalid bootstrap metadata for %q: %s", cred.Identifier, err) } } } if bootstrapMixin != nil { cred.Bootstrap = object.Inline(cred.Bootstrap, bootstrapMixin) } updatedCreds[cred.Identifier] = cred bs.Log.Debug("[%s] Bootstrap response: %+v", cred.Identifier, cred.Bootstrap) } } creds := make([]*stack.Credential, 0, len(updatedCreds)) for _, cred := range updatedCreds { creds = append(creds, cred) } if err := bs.Builder.PutCredentials(bs.Req.Username, creds...); err != nil { return nil, err } return true, 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 }