Example #1
0
func (build *dbBuild) Abort() error {
	// the order below is very important to avoid races with build creation.

	lock, err := build.locker.AcquireWriteLockImmediately([]db.NamedLock{db.BuildTrackingLock(build.id)})
	if err != nil {
		// someone else is tracking the build; abort it, which will notify them
		return build.db.AbortBuild(build.id)
	}

	defer lock.Release()

	// no one is tracking the build; abort it ourselves

	// first save the status so that CreateBuild will see a conflict when it
	// tries to mark the build as started.
	err = build.db.AbortBuild(build.id)
	if err != nil {
		return err
	}

	// reload the model *after* saving the status for the following check to see
	// if it was already started
	model, err := build.db.GetBuild(build.id)
	if err != nil {
		return err
	}

	// if there's an engine, there's a real build to abort
	if model.Engine == "" {
		// otherwise, CreateBuild had not yet tried to start the build, and so it
		// will see the conflict when it tries to transition, and abort itself.
		//
		// finish the build so that the aborted event is put into the event stream
		// even if the build has not started yet
		return build.db.FinishBuild(build.id, db.StatusAborted)
	}

	buildEngine, found := build.engines.Lookup(model.Engine)
	if !found {
		return UnknownEngineError{model.Engine}
	}

	// find the real build to abort...
	engineBuild, err := buildEngine.LookupBuild(model)
	if err != nil {
		return err
	}

	// ...and abort it.
	return engineBuild.Abort()
}
Example #2
0
				BeforeEach(func() {
					fakeLock = new(dbfakes.FakeLock)
					fakeLocker.AcquireWriteLockImmediatelyReturns(fakeLock, nil)
				})

				Context("when the build is active", func() {
					BeforeEach(func() {
						model.Engine = "fake-engine-b"

						fakeBuildDB.GetBuildReturns(model, nil)

						fakeBuildDB.AbortBuildStub = func(int) error {
							Ω(fakeLocker.AcquireWriteLockImmediatelyCallCount()).Should(Equal(1))

							lockedBuild := fakeLocker.AcquireWriteLockImmediatelyArgsForCall(0)
							Ω(lockedBuild).Should(Equal([]db.NamedLock{db.BuildTrackingLock(model.ID)}))

							Ω(fakeLock.ReleaseCallCount()).Should(BeZero())

							return nil
						}
					})

					Context("when the engine build exists", func() {
						var realBuild *fakes.FakeBuild

						BeforeEach(func() {
							fakeBuildDB.GetBuildReturns(model, nil)

							realBuild = new(fakes.FakeBuild)
							fakeEngineB.LookupBuildReturns(realBuild, nil)
Example #3
0
func (build *dbBuild) Resume(logger lager.Logger) {
	lock, err := build.locker.AcquireWriteLockImmediately([]db.NamedLock{db.BuildTrackingLock(build.id)})
	if err != nil {
		// already being tracked somewhere; short-circuit
		return
	}

	defer lock.Release()

	model, err := build.db.GetBuild(build.id)
	if err != nil {
		logger.Error("failed-to-load-build-from-db", err)
		return
	}

	if model.Engine == "" {
		logger.Error("build-has-no-engine", err)
		return
	}

	if !model.IsRunning() {
		logger.Info("build-already-finished", lager.Data{
			"build-id": build.id,
		})
		return
	}

	buildEngine, found := build.engines.Lookup(model.Engine)
	if !found {
		logger.Error("unknown-build-engine", nil, lager.Data{
			"engine": model.Engine,
		})
		build.finishWithError(model.ID, logger)
		return
	}

	engineBuild, err := buildEngine.LookupBuild(model)
	if err != nil {
		logger.Error("failed-to-lookup-build-from-engine", err)
		build.finishWithError(model.ID, logger)
		return
	}

	aborts, err := build.db.AbortNotifier(build.id)
	if err != nil {
		logger.Error("failed-to-listen-for-aborts", err)
		return
	}

	defer aborts.Close()

	done := make(chan struct{})
	defer close(done)

	go func() {
		select {
		case <-aborts.Notify():
			logger.Info("aborting")

			err := engineBuild.Abort()
			if err != nil {
				logger.Error("failed-to-abort", err)
			}
		case <-done:
		}
	}()

	engineBuild.Resume(logger)
}