1000 lines
32 KiB
Go
1000 lines
32 KiB
Go
package db_test
|
|
|
|
import (
|
|
"time"
|
|
|
|
sq "github.com/Masterminds/squirrel"
|
|
"github.com/concourse/concourse/atc"
|
|
"github.com/concourse/concourse/atc/db"
|
|
"github.com/lib/pq"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("ContainerRepository", func() {
|
|
Describe("FindOrphanedContainers", func() {
|
|
Describe("check containers", func() {
|
|
var (
|
|
creatingContainer db.CreatingContainer
|
|
resourceConfig db.ResourceConfig
|
|
)
|
|
|
|
expiries := db.ContainerOwnerExpiries{
|
|
Min: 5 * time.Minute,
|
|
Max: 1 * time.Hour,
|
|
}
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
resourceConfig, err = resourceConfigFactory.FindOrCreateResourceConfig(
|
|
"some-base-resource-type",
|
|
atc.Source{"some": "source"},
|
|
atc.VersionedResourceTypes{},
|
|
)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
creatingContainer, err = defaultWorker.CreateContainer(db.NewResourceConfigCheckSessionContainerOwner(resourceConfig, expiries), fullMetadata)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
JustBeforeEach(func() {
|
|
err := resourceConfigCheckSessionLifecycle.CleanExpiredResourceConfigCheckSessions()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
Context("when check container best if use by date is expired", func() {
|
|
BeforeEach(func() {
|
|
var rccsID int
|
|
err := psql.Select("id").From("resource_config_check_sessions").
|
|
Where(sq.Eq{"resource_config_id": resourceConfig.ID()}).RunWith(dbConn).QueryRow().Scan(&rccsID)
|
|
|
|
_, err = psql.Update("resource_config_check_sessions").
|
|
Set("expires_at", sq.Expr("NOW() - '1 second'::INTERVAL")).
|
|
Where(sq.Eq{"id": rccsID}).
|
|
RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
Context("when container is creating", func() {
|
|
It("finds the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when container is created", func() {
|
|
BeforeEach(func() {
|
|
_, err := creatingContainer.Created()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("finds the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(HaveLen(1))
|
|
Expect(createdContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when container is destroying", func() {
|
|
BeforeEach(func() {
|
|
createdContainer, err := creatingContainer.Created()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = createdContainer.Destroying()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("finds the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(HaveLen(1))
|
|
Expect(destroyingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when check container best if use by date did not expire", func() {
|
|
BeforeEach(func() {
|
|
var rccsID int
|
|
err := psql.Select("id").From("resource_config_check_sessions").
|
|
Where(sq.Eq{"resource_config_id": resourceConfig.ID()}).RunWith(dbConn).QueryRow().Scan(&rccsID)
|
|
|
|
_, err = psql.Update("resource_config_check_sessions").
|
|
Set("expires_at", sq.Expr("NOW() + '1 hour'::INTERVAL")).
|
|
Where(sq.Eq{"id": rccsID}).
|
|
RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("does not find the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when resource configs are cleaned up", func() {
|
|
BeforeEach(func() {
|
|
_, err := psql.Delete("resource_config_check_sessions").
|
|
RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
err = resourceConfigFactory.CleanUnreferencedConfigs()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("finds the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the worker base resource type has a new version", func() {
|
|
BeforeEach(func() {
|
|
var err error
|
|
newlyUpdatedWorker := defaultWorkerPayload
|
|
newlyUpdatedResource := defaultWorkerPayload.ResourceTypes[0]
|
|
newlyUpdatedResource.Version = newlyUpdatedResource.Version + "-new"
|
|
newlyUpdatedWorker.ResourceTypes = []atc.WorkerResourceType{newlyUpdatedResource}
|
|
|
|
defaultWorker, err = workerFactory.SaveWorker(newlyUpdatedWorker, 0)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("finds the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(HaveLen(0))
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the same worker base resource type is saved", func() {
|
|
BeforeEach(func() {
|
|
var err error
|
|
sameWorker := defaultWorkerPayload
|
|
|
|
defaultWorker, err = workerFactory.SaveWorker(sameWorker, 0)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("does not find the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("containers owned by a build", func() {
|
|
var (
|
|
creatingContainer db.CreatingContainer
|
|
build db.Build
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
build, err = defaultJob.CreateBuild()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
creatingContainer, err = defaultWorker.CreateContainer(
|
|
db.NewBuildStepContainerOwner(build.ID(), "simple-plan", defaultTeam.ID()),
|
|
fullMetadata,
|
|
)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
Context("when the build is interceptible", func() {
|
|
BeforeEach(func() {
|
|
err := build.SetInterceptible(true)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("does not find container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the build is marked as non-interceptible", func() {
|
|
BeforeEach(func() {
|
|
err := build.SetInterceptible(false)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
Context("when the container is creating", func() {
|
|
It("finds container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the container is created", func() {
|
|
BeforeEach(func() {
|
|
_, err := creatingContainer.Created()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("finds container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(HaveLen(1))
|
|
Expect(createdContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the container is destroying", func() {
|
|
BeforeEach(func() {
|
|
createdContainer, err := creatingContainer.Created()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
_, err = createdContainer.Destroying()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("finds container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(HaveLen(1))
|
|
Expect(destroyingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when build is deleted", func() {
|
|
BeforeEach(func() {
|
|
err := defaultPipeline.Destroy()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
It("finds container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("containers for checking images for creating containers", func() {
|
|
var (
|
|
creatingTaskContainer db.CreatingContainer
|
|
creatingContainer db.CreatingContainer
|
|
build db.Build
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
build, err = defaultJob.CreateBuild()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
creatingTaskContainer, err = defaultWorker.CreateContainer(
|
|
db.NewBuildStepContainerOwner(build.ID(), "simple-plan", defaultTeam.ID()),
|
|
fullMetadata,
|
|
)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
creatingContainer, err = defaultWorker.CreateContainer(db.NewImageCheckContainerOwner(creatingTaskContainer, defaultTeam.ID()), fullMetadata)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
Context("when the container they're for is still creating", func() {
|
|
It("does not find the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the container they're for is created", func() {
|
|
BeforeEach(func() {
|
|
_, err := creatingTaskContainer.Created()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("does not find the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the container they're for is gone", func() {
|
|
BeforeEach(func() {
|
|
_, err := creatingTaskContainer.Created()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("finds the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("containers for fetching images for creating containers", func() {
|
|
var (
|
|
creatingTaskContainer db.CreatingContainer
|
|
creatingContainer db.CreatingContainer
|
|
build db.Build
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
build, err = defaultJob.CreateBuild()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
creatingTaskContainer, err = defaultWorker.CreateContainer(
|
|
db.NewBuildStepContainerOwner(build.ID(), "simple-plan", defaultTeam.ID()),
|
|
fullMetadata,
|
|
)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
creatingContainer, err = defaultWorker.CreateContainer(db.NewImageGetContainerOwner(creatingTaskContainer, defaultTeam.ID()), fullMetadata)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
Context("when the container they're for is still creating", func() {
|
|
It("does not find the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(BeEmpty())
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the container they're for is created", func() {
|
|
BeforeEach(func() {
|
|
_, err := creatingTaskContainer.Created()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("does not find the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
|
|
Context("when the container they're for is gone", func() {
|
|
BeforeEach(func() {
|
|
_, err := creatingTaskContainer.Created()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("finds the container for deletion", func() {
|
|
creatingContainers, createdContainers, destroyingContainers, err := containerRepository.FindOrphanedContainers()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(creatingContainers).To(HaveLen(1))
|
|
Expect(creatingContainers[0].Handle()).To(Equal(creatingContainer.Handle()))
|
|
Expect(createdContainers).To(BeEmpty())
|
|
Expect(destroyingContainers).To(BeEmpty())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("DestroyFailedContainers", func() {
|
|
var failedErr error
|
|
var failedContainersLen int
|
|
|
|
JustBeforeEach(func() {
|
|
failedContainersLen, failedErr = containerRepository.DestroyFailedContainers()
|
|
})
|
|
|
|
Context("when there are failed containers", func() {
|
|
BeforeEach(func() {
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": atc.ContainerStateFailed,
|
|
"handle": "123-456-abc-def",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("returns all failed containers", func() {
|
|
Expect(failedContainersLen).To(Equal(1))
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("when there are no failed containers", func() {
|
|
It("returns an empty array", func() {
|
|
Expect(failedContainersLen).To(Equal(0))
|
|
})
|
|
It("does not return an error", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Describe("errors", func() {
|
|
Context("when the query cannot be executed", func() {
|
|
BeforeEach(func() {
|
|
err := dbConn.Close()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
AfterEach(func() {
|
|
dbConn = postgresRunner.OpenConn()
|
|
})
|
|
It("returns an error", func() {
|
|
Expect(failedErr).To(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("when there is an invalid row", func() {
|
|
BeforeEach(func() {
|
|
By("adding a row without expected values")
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": "failed",
|
|
"handle": "123-456-abc-def",
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("destroy the invalid row", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
Expect(failedContainersLen).To(Equal(1))
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("FindDestroyingContainers", func() {
|
|
var failedErr error
|
|
var destroyingContainers []string
|
|
|
|
JustBeforeEach(func() {
|
|
destroyingContainers, failedErr = containerRepository.FindDestroyingContainers(defaultWorker.Name())
|
|
})
|
|
ItClosesConnection := func() {
|
|
It("closes the connection", func() {
|
|
closed := make(chan bool)
|
|
|
|
go func() {
|
|
_, _ = containerRepository.FindDestroyingContainers(defaultWorker.Name())
|
|
closed <- true
|
|
}()
|
|
|
|
Eventually(closed).Should(Receive())
|
|
})
|
|
}
|
|
|
|
Context("when there are destroying containers", func() {
|
|
BeforeEach(func() {
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": "destroying",
|
|
"handle": "123-456-abc-def",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("returns all destroying containers", func() {
|
|
Expect(destroyingContainers).To(HaveLen(1))
|
|
Expect(destroyingContainers[0]).To(Equal("123-456-abc-def"))
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
})
|
|
|
|
ItClosesConnection()
|
|
})
|
|
|
|
Describe("errors", func() {
|
|
Context("when the query cannot be executed", func() {
|
|
BeforeEach(func() {
|
|
err := dbConn.Close()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
AfterEach(func() {
|
|
dbConn = postgresRunner.OpenConn()
|
|
})
|
|
|
|
It("returns an error", func() {
|
|
Expect(failedErr).To(HaveOccurred())
|
|
})
|
|
|
|
ItClosesConnection()
|
|
})
|
|
|
|
Context("when there is an error iterating through the rows", func() {
|
|
BeforeEach(func() {
|
|
By("adding a row without expected values")
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": "destroying",
|
|
"handle": "123-456-abc-def",
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
|
|
})
|
|
|
|
It("returns empty list", func() {
|
|
Expect(destroyingContainers).To(HaveLen(0))
|
|
})
|
|
|
|
ItClosesConnection()
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("RemoveMissingContainers", func() {
|
|
var (
|
|
today time.Time
|
|
gracePeriod time.Duration
|
|
rowsAffected int
|
|
err error
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
today = time.Now()
|
|
|
|
_, err = psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"handle": "some-handle-1",
|
|
"state": atc.ContainerStateCreated,
|
|
}).RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
_, err = psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"handle": "some-handle-2",
|
|
"state": atc.ContainerStateCreated,
|
|
"missing_since": today,
|
|
}).RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
_, err = psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"handle": "some-handle-3",
|
|
"state": atc.ContainerStateFailed,
|
|
"missing_since": today.Add(-5 * time.Minute),
|
|
}).RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
_, err = psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"handle": "some-handle-4",
|
|
"state": atc.ContainerStateDestroying,
|
|
"missing_since": today.Add(-10 * time.Minute),
|
|
}).RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
})
|
|
|
|
JustBeforeEach(func() {
|
|
rowsAffected, err = containerRepository.RemoveMissingContainers(gracePeriod)
|
|
})
|
|
|
|
Context("when no created/failed containers have expired", func() {
|
|
BeforeEach(func() {
|
|
gracePeriod = 7 * time.Minute
|
|
})
|
|
|
|
It("affects no containers", func() {
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(rowsAffected).To(Equal(0))
|
|
})
|
|
})
|
|
|
|
Context("when some created/failed containers have expired", func() {
|
|
BeforeEach(func() {
|
|
gracePeriod = 3 * time.Minute
|
|
})
|
|
|
|
It("affects some containers", func() {
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(rowsAffected).To(Equal(1))
|
|
})
|
|
|
|
It("affects the right containers", func() {
|
|
result, err := psql.Select("*").From("containers").
|
|
RunWith(dbConn).Exec()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(3)))
|
|
|
|
result, err = psql.Select("*").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle-1"}).RunWith(dbConn).Exec()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
|
|
result, err = psql.Select("*").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle-2"}).RunWith(dbConn).Exec()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
|
|
result, err = psql.Select("*").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle-4"}).RunWith(dbConn).Exec()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("RemoveDestroyingContainers", func() {
|
|
var failedErr error
|
|
var numDeleted int
|
|
var handles []string
|
|
|
|
JustBeforeEach(func() {
|
|
numDeleted, failedErr = containerRepository.RemoveDestroyingContainers(defaultWorker.Name(), handles)
|
|
})
|
|
|
|
Context("when there are containers to destroy", func() {
|
|
|
|
Context("when container is in destroying state", func() {
|
|
BeforeEach(func() {
|
|
handles = []string{"some-handle1", "some-handle2"}
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": atc.ContainerStateDestroying,
|
|
"handle": "123-456-abc-def",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
It("should destroy", func() {
|
|
result, err := psql.Select("*").From("containers").
|
|
Where(sq.Eq{"handle": "123-456-abc-def"}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(0)))
|
|
})
|
|
It("returns the correct number of rows removed", func() {
|
|
Expect(numDeleted).To(Equal(1))
|
|
})
|
|
It("does not return an error", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("when handles are empty list", func() {
|
|
BeforeEach(func() {
|
|
handles = []string{}
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": atc.ContainerStateDestroying,
|
|
"handle": "123-456-abc-def",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("should destroy", func() {
|
|
result, err := psql.Select("*").From("containers").
|
|
Where(sq.Eq{"handle": "123-456-abc-def"}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(0)))
|
|
})
|
|
|
|
It("returns the correct number of rows removed", func() {
|
|
Expect(numDeleted).To(Equal(1))
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("when container is in create/creating state", func() {
|
|
BeforeEach(func() {
|
|
handles = []string{"some-handle1", "some-handle2"}
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": "creating",
|
|
"handle": "123-456-abc-def",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
It("should not destroy", func() {
|
|
result, err := psql.Select("*").From("containers").
|
|
Where(sq.Eq{"handle": "123-456-abc-def"}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
It("returns the correct number of rows removed", func() {
|
|
Expect(numDeleted).To(Equal(0))
|
|
})
|
|
It("does not return an error", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when there are no containers to destroy", func() {
|
|
BeforeEach(func() {
|
|
handles = []string{"some-handle1", "some-handle2"}
|
|
|
|
result, err := psql.Insert("containers").SetMap(
|
|
map[string]interface{}{
|
|
"state": "destroying",
|
|
"handle": "some-handle1",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
},
|
|
).RunWith(dbConn).Exec()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
|
|
result, err = psql.Insert("containers").SetMap(
|
|
map[string]interface{}{
|
|
"state": "destroying",
|
|
"handle": "some-handle2",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
},
|
|
).RunWith(dbConn).Exec()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("doesn't destroy containers that are in handles", func() {
|
|
result, err := psql.Select("*").From("containers").
|
|
Where(sq.Eq{"handle": handles}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(2)))
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(failedErr).ToNot(HaveOccurred())
|
|
})
|
|
It("returns the correct number of rows removed", func() {
|
|
Expect(numDeleted).To(Equal(0))
|
|
})
|
|
})
|
|
|
|
Describe("errors", func() {
|
|
Context("when the query cannot be executed", func() {
|
|
BeforeEach(func() {
|
|
err := dbConn.Close()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
AfterEach(func() {
|
|
dbConn = postgresRunner.OpenConn()
|
|
})
|
|
|
|
It("returns an error", func() {
|
|
Expect(failedErr).To(HaveOccurred())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("UpdateContainersMissingSince", func() {
|
|
var (
|
|
today time.Time
|
|
err error
|
|
handles []string
|
|
missingSince pq.NullTime
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
result, err := psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": atc.ContainerStateDestroying,
|
|
"handle": "some-handle1",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
|
|
result, err = psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": atc.ContainerStateDestroying,
|
|
"handle": "some-handle2",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
|
|
today = time.Date(2018, 9, 24, 0, 0, 0, 0, time.UTC)
|
|
|
|
result, err = psql.Insert("containers").SetMap(map[string]interface{}{
|
|
"state": atc.ContainerStateCreated,
|
|
"handle": "some-handle3",
|
|
"worker_name": defaultWorker.Name(),
|
|
"hijacked": false,
|
|
"discontinued": false,
|
|
"missing_since": today,
|
|
}).RunWith(dbConn).Exec()
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
|
|
JustBeforeEach(func() {
|
|
err = containerRepository.UpdateContainersMissingSince(defaultWorker.Name(), handles)
|
|
})
|
|
|
|
Context("when the reported handles is a subset", func() {
|
|
BeforeEach(func() {
|
|
handles = []string{"some-handle1"}
|
|
})
|
|
|
|
Context("having the containers in the creating state in the db", func() {
|
|
BeforeEach(func() {
|
|
result, err := psql.Update("containers").
|
|
Where(sq.Eq{"handle": "some-handle3"}).
|
|
SetMap(map[string]interface{}{
|
|
"state": atc.ContainerStateCreating,
|
|
"missing_since": nil,
|
|
}).RunWith(dbConn).Exec()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(result.RowsAffected()).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("does not mark as missing", func() {
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle3"}).RunWith(dbConn).QueryRow().
|
|
Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeFalse())
|
|
})
|
|
})
|
|
|
|
It("should mark containers not in the subset and not already marked as missing", func() {
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle1"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeFalse())
|
|
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle2"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeTrue())
|
|
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle3"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeTrue())
|
|
Expect(missingSince.Time.Unix()).To(Equal(today.Unix()))
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("when the reported handles is the full set", func() {
|
|
BeforeEach(func() {
|
|
handles = []string{"some-handle1", "some-handle2"}
|
|
})
|
|
|
|
It("should not update", func() {
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle1"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeFalse())
|
|
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle2"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeFalse())
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
|
|
Context("when the reported handles includes a container marked as missing", func() {
|
|
BeforeEach(func() {
|
|
handles = []string{"some-handle1", "some-handle2", "some-handle3"}
|
|
})
|
|
|
|
It("should mark the previously missing container as not missing", func() {
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle1"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeFalse())
|
|
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle2"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeFalse())
|
|
|
|
err = psql.Select("missing_since").From("containers").
|
|
Where(sq.Eq{"handle": "some-handle3"}).RunWith(dbConn).QueryRow().Scan(&missingSince)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(missingSince.Valid).To(BeFalse())
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
})
|
|
})
|
|
})
|