예제 #1
0
파일: plan.go 프로젝트: koding/koding
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)
}
예제 #2
0
파일: apply.go 프로젝트: koding/koding
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()
}
예제 #3
0
파일: bootstrap.go 프로젝트: koding/koding
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
}
예제 #4
0
파일: apply.go 프로젝트: koding/koding
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
}