Exemple #1
0
func dbSharedBehavior(database *dbSharedBehaviorInput) func() {
	return func() {

		Describe("CreatePipe", func() {
			It("saves a pipe to the db", func() {
				myGuid, err := uuid.NewV4()
				Ω(err).ShouldNot(HaveOccurred())

				err = database.CreatePipe(myGuid.String(), "a-url")
				Ω(err).ShouldNot(HaveOccurred())

				pipe, err := database.GetPipe(myGuid.String())
				Ω(err).ShouldNot(HaveOccurred())
				Ω(pipe.ID).Should(Equal(myGuid.String()))
				Ω(pipe.URL).Should(Equal("a-url"))
			})
		})

		It("saves and propagates events correctly", func() {
			build, err := database.CreateOneOffBuild()
			Ω(err).ShouldNot(HaveOccurred())
			Ω(build.Name).Should(Equal("1"))

			By("allowing you to subscribe when no events have yet occurred")
			events, err := database.GetBuildEvents(build.ID, 0)
			Ω(err).ShouldNot(HaveOccurred())

			defer events.Close()

			By("saving them in order")
			err = database.SaveBuildEvent(build.ID, event.Log{
				Payload: "some ",
			})
			Ω(err).ShouldNot(HaveOccurred())

			Ω(events.Next()).Should(Equal(event.Log{
				Payload: "some ",
			}))

			err = database.SaveBuildEvent(build.ID, event.Log{
				Payload: "log",
			})
			Ω(err).ShouldNot(HaveOccurred())

			Ω(events.Next()).Should(Equal(event.Log{
				Payload: "log",
			}))

			By("allowing you to subscribe from an offset")
			eventsFrom1, err := database.GetBuildEvents(build.ID, 1)
			Ω(err).ShouldNot(HaveOccurred())

			defer eventsFrom1.Close()

			Ω(eventsFrom1.Next()).Should(Equal(event.Log{
				Payload: "log",
			}))

			By("notifying those waiting on events as soon as they're saved")
			nextEvent := make(chan atc.Event)
			nextErr := make(chan error)

			go func() {
				event, err := events.Next()
				if err != nil {
					nextErr <- err
				} else {
					nextEvent <- event
				}
			}()

			Consistently(nextEvent).ShouldNot(Receive())
			Consistently(nextErr).ShouldNot(Receive())

			err = database.SaveBuildEvent(build.ID, event.Log{
				Payload: "log 2",
			})
			Ω(err).ShouldNot(HaveOccurred())

			Eventually(nextEvent).Should(Receive(Equal(event.Log{
				Payload: "log 2",
			})))

			By("returning ErrBuildEventStreamClosed for Next calls after Close")
			events3, err := database.GetBuildEvents(build.ID, 0)
			Ω(err).ShouldNot(HaveOccurred())

			events3.Close()

			_, err = events3.Next()
			Ω(err).Should(Equal(db.ErrBuildEventStreamClosed))
		})

		It("saves and emits status events", func() {
			build, err := database.CreateOneOffBuild()
			Ω(err).ShouldNot(HaveOccurred())
			Ω(build.Name).Should(Equal("1"))

			By("allowing you to subscribe when no events have yet occurred")
			events, err := database.GetBuildEvents(build.ID, 0)
			Ω(err).ShouldNot(HaveOccurred())

			defer events.Close()

			By("emitting a status event when started")
			started, err := database.StartBuild(build.ID, "engine", "metadata")
			Ω(err).ShouldNot(HaveOccurred())
			Ω(started).Should(BeTrue())

			startedBuild, err := database.GetBuild(build.ID)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(events.Next()).Should(Equal(event.Status{
				Status: atc.StatusStarted,
				Time:   startedBuild.StartTime.Unix(),
			}))

			By("emitting a status event when finished")
			err = database.FinishBuild(build.ID, db.StatusSucceeded)
			Ω(err).ShouldNot(HaveOccurred())

			finishedBuild, err := database.GetBuild(build.ID)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(events.Next()).Should(Equal(event.Status{
				Status: atc.StatusSucceeded,
				Time:   finishedBuild.EndTime.Unix(),
			}))

			By("ending the stream when finished")
			_, err = events.Next()
			Ω(err).Should(Equal(db.ErrEndOfBuildEventStream))
		})

		It("can keep track of workers", func() {
			Ω(database.Workers()).Should(BeEmpty())

			infoA := db.WorkerInfo{
				Addr:             "1.2.3.4:7777",
				ActiveContainers: 42,
				ResourceTypes: []atc.WorkerResourceType{
					{Type: "some-resource-a", Image: "some-image-a"},
				},
				Platform: "webos",
				Tags:     []string{"palm", "was", "great"},
			}

			infoB := db.WorkerInfo{
				Addr:             "1.2.3.4:8888",
				ActiveContainers: 42,
				ResourceTypes: []atc.WorkerResourceType{
					{Type: "some-resource-b", Image: "some-image-b"},
				},
				Platform: "plan9",
				Tags:     []string{"russ", "cox", "was", "here"},
			}

			By("persisting workers with no TTLs")
			err := database.SaveWorker(infoA, 0)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(database.Workers()).Should(ConsistOf(infoA))

			By("being idempotent")
			err = database.SaveWorker(infoA, 0)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(database.Workers()).Should(ConsistOf(infoA))

			By("expiring TTLs")
			ttl := 1 * time.Second

			err = database.SaveWorker(infoB, ttl)
			Ω(err).ShouldNot(HaveOccurred())

			Consistently(database.Workers, ttl/2).Should(ConsistOf(infoA, infoB))
			Eventually(database.Workers, 2*ttl).Should(ConsistOf(infoA))

			By("overwriting TTLs")
			err = database.SaveWorker(infoA, ttl)
			Ω(err).ShouldNot(HaveOccurred())

			Consistently(database.Workers, ttl/2).Should(ConsistOf(infoA))
			Eventually(database.Workers, 2*ttl).Should(BeEmpty())
		})

		It("can create one-off builds with increasing names", func() {
			oneOff, err := database.CreateOneOffBuild()
			Ω(err).ShouldNot(HaveOccurred())
			Ω(oneOff.ID).ShouldNot(BeZero())
			Ω(oneOff.JobName).Should(BeZero())
			Ω(oneOff.Name).Should(Equal("1"))
			Ω(oneOff.Status).Should(Equal(db.StatusPending))

			oneOffGot, err := database.GetBuild(oneOff.ID)
			Ω(err).ShouldNot(HaveOccurred())
			Ω(oneOffGot).Should(Equal(oneOff))

			jobBuild, err := database.PipelineDB.CreateJobBuild("some-other-job")
			Ω(err).ShouldNot(HaveOccurred())
			Ω(jobBuild.Name).Should(Equal("1"))

			nextOneOff, err := database.CreateOneOffBuild()
			Ω(err).ShouldNot(HaveOccurred())
			Ω(nextOneOff.ID).ShouldNot(BeZero())
			Ω(nextOneOff.ID).ShouldNot(Equal(oneOff.ID))
			Ω(nextOneOff.JobName).Should(BeZero())
			Ω(nextOneOff.Name).Should(Equal("2"))
			Ω(nextOneOff.Status).Should(Equal(db.StatusPending))

			allBuilds, err := database.GetAllBuilds()
			Ω(err).ShouldNot(HaveOccurred())
			Ω(allBuilds).Should(Equal([]db.Build{nextOneOff, jobBuild, oneOff}))
		})

		Describe("GetAllStartedBuilds", func() {
			var build1 db.Build
			var build2 db.Build
			var build3 db.Build
			BeforeEach(func() {
				var err error

				build1, err = database.CreateOneOffBuild()
				Ω(err).ShouldNot(HaveOccurred())

				build2, err = database.PipelineDB.CreateJobBuild("some-job")
				Ω(err).ShouldNot(HaveOccurred())

				build3, err = database.CreateOneOffBuild()
				Ω(err).ShouldNot(HaveOccurred())

				started, err := database.StartBuild(build1.ID, "some-engine", "so-meta")
				Ω(err).ShouldNot(HaveOccurred())
				Ω(started).Should(BeTrue())

				started, err = database.StartBuild(build2.ID, "some-engine", "so-meta")
				Ω(err).ShouldNot(HaveOccurred())
				Ω(started).Should(BeTrue())
			})

			It("returns all builds that have been started, regardless of pipeline", func() {
				builds, err := database.GetAllStartedBuilds()
				Ω(err).ShouldNot(HaveOccurred())

				Ω(len(builds)).Should(Equal(2))

				build1, err := database.GetBuild(build1.ID)
				Ω(err).ShouldNot(HaveOccurred())
				build2, err := database.GetBuild(build2.ID)
				Ω(err).ShouldNot(HaveOccurred())

				Ω(builds).Should(ConsistOf(build1, build2))
			})
		})

		Describe("locking", func() {
			It("can be done generically with a unique name", func() {
				lock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
				Ω(err).ShouldNot(HaveOccurred())

				secondLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					secondLock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
					Ω(err).ShouldNot(HaveOccurred())

					secondLockCh <- secondLock
				}()

				Consistently(secondLockCh).ShouldNot(Receive())

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				var secondLock db.Lock
				Eventually(secondLockCh).Should(Receive(&secondLock))

				err = secondLock.Release()
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("can be done without waiting", func() {
				lock, err := database.AcquireWriteLockImmediately([]db.NamedLock{db.ResourceCheckingLock("a-name")})
				Ω(err).ShouldNot(HaveOccurred())

				secondLock, err := database.AcquireWriteLockImmediately([]db.NamedLock{db.ResourceCheckingLock("a-name")})
				Ω(err).Should(HaveOccurred())
				Ω(secondLock).Should(BeNil())

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("does not let anyone write if someone is reading", func() {
				lock, err := database.AcquireReadLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
				Ω(err).ShouldNot(HaveOccurred())

				secondLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					secondLock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
					Ω(err).ShouldNot(HaveOccurred())

					secondLockCh <- secondLock
				}()

				Consistently(secondLockCh).ShouldNot(Receive())

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				var secondLock db.Lock
				Eventually(secondLockCh).Should(Receive(&secondLock))

				err = secondLock.Release()
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("does not let anyone read if someone is writing", func() {
				lock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
				Ω(err).ShouldNot(HaveOccurred())

				secondLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					secondLock, err := database.AcquireReadLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
					Ω(err).ShouldNot(HaveOccurred())

					secondLockCh <- secondLock
				}()

				Consistently(secondLockCh).ShouldNot(Receive())

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				var secondLock db.Lock
				Eventually(secondLockCh).Should(Receive(&secondLock))

				err = secondLock.Release()
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("lets many reads simultaneously", func() {
				lock, err := database.AcquireReadLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
				Ω(err).ShouldNot(HaveOccurred())

				secondLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					secondLock, err := database.AcquireReadLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
					Ω(err).ShouldNot(HaveOccurred())

					secondLockCh <- secondLock
				}()

				var secondLock db.Lock
				Eventually(secondLockCh).Should(Receive(&secondLock))

				err = secondLock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("can be done multiple times if using different locks", func() {
				lock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("name-1")})
				Ω(err).ShouldNot(HaveOccurred())

				var secondLock db.Lock
				secondLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					secondLock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("name-2")})
					Ω(err).ShouldNot(HaveOccurred())

					secondLockCh <- secondLock
				}()

				Eventually(secondLockCh).Should(Receive(&secondLock))

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				err = secondLock.Release()
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("can be done for multiple locks at a time", func() {
				lock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("name-1"), db.ResourceCheckingLock("name-2")})
				Ω(err).ShouldNot(HaveOccurred())

				secondLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					secondLock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("name-1")})
					Ω(err).ShouldNot(HaveOccurred())

					secondLockCh <- secondLock
				}()

				Consistently(secondLockCh).ShouldNot(Receive())

				thirdLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					thirdLock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("name-2")})
					Ω(err).ShouldNot(HaveOccurred())

					thirdLockCh <- thirdLock
				}()

				Consistently(thirdLockCh).ShouldNot(Receive())

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				var secondLock db.Lock
				Eventually(secondLockCh).Should(Receive(&secondLock))

				var thirdLock db.Lock
				Eventually(thirdLockCh).Should(Receive(&thirdLock))

				err = secondLock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				err = thirdLock.Release()
				Ω(err).ShouldNot(HaveOccurred())
			})

			It("cleans up after releasing", func() {
				lock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
				Ω(err).ShouldNot(HaveOccurred())
				Ω(database.ListLocks()).Should(ContainElement(db.ResourceCheckingLock("a-name").Name()))

				secondLockCh := make(chan db.Lock, 1)

				go func() {
					defer GinkgoRecover()

					secondLock, err := database.AcquireWriteLock([]db.NamedLock{db.ResourceCheckingLock("a-name")})
					Ω(err).ShouldNot(HaveOccurred())

					secondLockCh <- secondLock
				}()

				Consistently(secondLockCh).ShouldNot(Receive())

				err = lock.Release()
				Ω(err).ShouldNot(HaveOccurred())
				Ω(database.ListLocks()).Should(ContainElement(db.ResourceCheckingLock("a-name").Name()))

				var secondLock db.Lock
				Eventually(secondLockCh).Should(Receive(&secondLock))

				err = secondLock.Release()
				Ω(err).ShouldNot(HaveOccurred())

				Ω(database.ListLocks()).Should(BeEmpty())
			})
		})
	}
}
Exemple #2
0
			var time1 time.Time
			var time2 time.Time

			Eventually(times).Should(Receive(&time1))
			Eventually(times).Should(Receive(&time2))

			Ω(time2.Sub(time1)).Should(BeNumerically("~", interval, interval/4))
		})

		It("grabs a resource checking lock before checking, releases after done", func() {
			Eventually(times).Should(Receive())

			Ω(locker.AcquireWriteLockImmediatelyCallCount()).Should(Equal(1))

			lockedInputs := locker.AcquireWriteLockImmediatelyArgsForCall(0)
			Ω(lockedInputs).Should(Equal([]db.NamedLock{db.ResourceCheckingLock("pipeline:some-resource")}))

			Ω(writeImmediatelyLock.ReleaseCallCount()).Should(Equal(1))
		})

		It("releases after checking", func() {
			Eventually(times).Should(Receive())

			Ω(fakeResource.ReleaseCallCount()).Should(Equal(1))
		})

		Context("when there is no current version", func() {
			It("checks from nil", func() {
				Eventually(times).Should(Receive())

				_, version := fakeResource.CheckArgsForCall(0)
Exemple #3
0
func (radar *Radar) checkLock(resourceName string) []db.NamedLock {
	return []db.NamedLock{db.ResourceCheckingLock(resourceName)}
}