Example #1
0
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
}
Example #2
0
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
}
Example #3
0
// 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
}