concourse/atc/db/worker_test.go

389 lines
10 KiB
Go

package db_test
import (
"database/sql"
"fmt"
"time"
"github.com/concourse/concourse/atc"
. "github.com/concourse/concourse/atc/db"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/types"
)
var _ = Describe("Worker", func() {
var (
atcWorker atc.Worker
worker Worker
)
BeforeEach(func() {
atcWorker = atc.Worker{
GardenAddr: "some-garden-addr",
BaggageclaimURL: "some-bc-url",
HTTPProxyURL: "some-http-proxy-url",
HTTPSProxyURL: "some-https-proxy-url",
NoProxy: "some-no-proxy",
Ephemeral: true,
ActiveContainers: 140,
ResourceTypes: []atc.WorkerResourceType{
{
Type: "some-resource-type",
Image: "some-image",
Version: "some-version",
},
{
Type: "other-resource-type",
Image: "other-image",
Version: "other-version",
},
},
Platform: "some-platform",
Tags: atc.Tags{"some", "tags"},
Name: "some-name",
StartTime: 55912945,
}
})
Describe("Land", func() {
BeforeEach(func() {
var err error
worker, err = workerFactory.SaveWorker(atcWorker, 5*time.Minute)
Expect(err).NotTo(HaveOccurred())
})
Context("when the worker is present", func() {
It("marks the worker as `landing`", func() {
err := worker.Land()
Expect(err).NotTo(HaveOccurred())
_, err = worker.Reload()
Expect(err).NotTo(HaveOccurred())
Expect(worker.Name()).To(Equal(atcWorker.Name))
Expect(worker.State()).To(Equal(WorkerStateLanding))
})
Context("when worker is already landed", func() {
BeforeEach(func() {
err := worker.Land()
Expect(err).NotTo(HaveOccurred())
_, err = workerLifecycle.LandFinishedLandingWorkers()
Expect(err).NotTo(HaveOccurred())
})
It("keeps worker state as landed", func() {
err := worker.Land()
Expect(err).NotTo(HaveOccurred())
_, err = worker.Reload()
Expect(err).NotTo(HaveOccurred())
Expect(worker.Name()).To(Equal(atcWorker.Name))
Expect(worker.State()).To(Equal(WorkerStateLanded))
})
})
})
Context("when the worker is not present", func() {
It("returns an error", func() {
err := worker.Delete()
Expect(err).NotTo(HaveOccurred())
err = worker.Land()
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(ErrWorkerNotPresent))
})
})
})
Describe("Retire", func() {
BeforeEach(func() {
var err error
worker, err = workerFactory.SaveWorker(atcWorker, 5*time.Minute)
Expect(err).NotTo(HaveOccurred())
})
Context("when the worker is present", func() {
It("marks the worker as `retiring`", func() {
err := worker.Retire()
Expect(err).NotTo(HaveOccurred())
_, err = worker.Reload()
Expect(err).NotTo(HaveOccurred())
Expect(worker.Name()).To(Equal(atcWorker.Name))
Expect(worker.State()).To(Equal(WorkerStateRetiring))
})
})
Context("when the worker is not present", func() {
BeforeEach(func() {
err := worker.Delete()
Expect(err).NotTo(HaveOccurred())
})
It("returns an error", func() {
err := worker.Retire()
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(ErrWorkerNotPresent))
})
})
})
Describe("Delete", func() {
BeforeEach(func() {
var err error
worker, err = workerFactory.SaveWorker(atcWorker, 5*time.Minute)
Expect(err).NotTo(HaveOccurred())
})
It("deletes the record for the worker", func() {
err := worker.Delete()
Expect(err).NotTo(HaveOccurred())
_, found, err := workerFactory.GetWorker(atcWorker.Name)
Expect(err).ToNot(HaveOccurred())
Expect(found).To(BeFalse())
})
})
Describe("Prune", func() {
Context("when worker exists", func() {
DescribeTable("worker in state",
func(workerState string, errMatch GomegaMatcher) {
worker, err := workerFactory.SaveWorker(atc.Worker{
Name: "worker-to-prune",
GardenAddr: "1.2.3.4",
State: workerState,
}, 5*time.Minute)
Expect(err).NotTo(HaveOccurred())
err = worker.Prune()
Expect(err).To(errMatch)
},
Entry("running", "running", Equal(ErrCannotPruneRunningWorker)),
Entry("landing", "landing", BeNil()),
Entry("retiring", "retiring", BeNil()),
)
Context("when worker is stalled", func() {
var pruneErr error
BeforeEach(func() {
worker, err := workerFactory.SaveWorker(atc.Worker{
Name: "worker-to-prune",
GardenAddr: "1.2.3.4",
State: "running",
}, -5*time.Minute)
Expect(err).NotTo(HaveOccurred())
_, err = workerLifecycle.StallUnresponsiveWorkers()
Expect(err).NotTo(HaveOccurred())
pruneErr = worker.Prune()
})
It("does not return error", func() {
Expect(pruneErr).NotTo(HaveOccurred())
})
})
})
Context("when worker does not exist", func() {
BeforeEach(func() {
var err error
worker, err = workerFactory.SaveWorker(atcWorker, 5*time.Minute)
Expect(err).NotTo(HaveOccurred())
err = worker.Delete()
Expect(err).NotTo(HaveOccurred())
})
It("raises ErrWorkerNotPresent", func() {
err := worker.Prune()
Expect(err).To(Equal(ErrWorkerNotPresent))
})
})
})
Describe("FindContainer/CreateContainer", func() {
var (
containerMetadata ContainerMetadata
containerOwner ContainerOwner
foundCreatingContainer CreatingContainer
foundCreatedContainer CreatedContainer
worker Worker
)
expiries := ContainerOwnerExpiries{
Min: 5 * time.Minute,
Max: 1 * time.Hour,
}
BeforeEach(func() {
containerMetadata = ContainerMetadata{
Type: "check",
}
var err error
worker, err = workerFactory.SaveWorker(atcWorker, 5*time.Minute)
Expect(err).NotTo(HaveOccurred())
atcWorker2 := atcWorker
atcWorker2.Name = "some-name2"
atcWorker2.GardenAddr = "some-garden-addr-other"
otherWorker, err = workerFactory.SaveWorker(atcWorker2, 5*time.Minute)
Expect(err).NotTo(HaveOccurred())
resourceConfig, err := resourceConfigFactory.FindOrCreateResourceConfig(
"some-resource-type",
atc.Source{"some": "source"},
atc.VersionedResourceTypes{},
)
Expect(err).ToNot(HaveOccurred())
containerOwner = NewResourceConfigCheckSessionContainerOwner(resourceConfig, expiries)
})
JustBeforeEach(func() {
var err error
foundCreatingContainer, foundCreatedContainer, err = worker.FindContainer(containerOwner)
Expect(err).ToNot(HaveOccurred())
})
Context("when there is a creating container", func() {
var creatingContainer CreatingContainer
BeforeEach(func() {
var err error
creatingContainer, err = worker.CreateContainer(containerOwner, containerMetadata)
Expect(err).ToNot(HaveOccurred())
})
It("returns it", func() {
Expect(foundCreatedContainer).To(BeNil())
Expect(foundCreatingContainer).ToNot(BeNil())
})
Context("when finding on another worker", func() {
BeforeEach(func() {
worker = otherWorker
})
It("does not find it", func() {
Expect(foundCreatingContainer).To(BeNil())
Expect(foundCreatedContainer).To(BeNil())
})
})
Context("when there is a created container", func() {
BeforeEach(func() {
_, err := creatingContainer.Created()
Expect(err).ToNot(HaveOccurred())
})
It("returns it", func() {
Expect(foundCreatedContainer).ToNot(BeNil())
Expect(foundCreatingContainer).To(BeNil())
})
Context("when finding on another worker", func() {
BeforeEach(func() {
worker = otherWorker
})
It("does not find it", func() {
Expect(foundCreatingContainer).To(BeNil())
Expect(foundCreatedContainer).To(BeNil())
})
})
})
Context("when the creating container is failed and gced", func() {
BeforeEach(func() {
var err error
_, err = creatingContainer.Failed()
Expect(err).ToNot(HaveOccurred())
containerRepository := NewContainerRepository(dbConn)
containersDestroyed, err := containerRepository.DestroyFailedContainers()
Expect(containersDestroyed).To(Equal(1))
Expect(err).ToNot(HaveOccurred())
var checkSessions int
err = dbConn.QueryRow("SELECT COUNT(*) FROM resource_config_check_sessions").Scan(&checkSessions)
Expect(err).ToNot(HaveOccurred())
Expect(checkSessions).To(Equal(1))
})
Context("and we create a new container", func() {
BeforeEach(func() {
_, err := worker.CreateContainer(containerOwner, containerMetadata)
Expect(err).ToNot(HaveOccurred())
})
It("does not duplicate the resource config check session", func() {
var checkSessions int
err := dbConn.QueryRow("SELECT COUNT(*) FROM resource_config_check_sessions").Scan(&checkSessions)
Expect(err).ToNot(HaveOccurred())
Expect(checkSessions).To(Equal(1))
})
})
})
})
Context("when there is no container", func() {
It("returns nil", func() {
Expect(foundCreatedContainer).To(BeNil())
Expect(foundCreatingContainer).To(BeNil())
})
})
Context("when the container has a meta type", func() {
var container CreatingContainer
Context("when the meta type is check", func() {
BeforeEach(func() {
containerMetadata = ContainerMetadata{
Type: "check",
}
var err error
container, err = worker.CreateContainer(containerOwner, containerMetadata)
Expect(err).ToNot(HaveOccurred())
})
It("returns a container with empty team id", func() {
var teamID sql.NullString
err := dbConn.QueryRow(fmt.Sprintf("SELECT team_id FROM containers WHERE id='%d'", container.ID())).Scan(&teamID)
Expect(err).ToNot(HaveOccurred())
Expect(teamID.Valid).To(BeFalse())
})
})
Context("when the meta type is not check", func() {
BeforeEach(func() {
containerMetadata = ContainerMetadata{
Type: "get",
}
oneOffBuild, err := defaultTeam.CreateOneOffBuild()
Expect(err).ToNot(HaveOccurred())
container, err = worker.CreateContainer(NewBuildStepContainerOwner(oneOffBuild.ID(), atc.PlanID("1"), 1), containerMetadata)
Expect(err).ToNot(HaveOccurred())
})
It("returns a container with a team id", func() {
var teamID sql.NullString
err := dbConn.QueryRow(fmt.Sprintf("SELECT team_id FROM containers WHERE id='%d'", container.ID())).Scan(&teamID)
Expect(err).ToNot(HaveOccurred())
Expect(teamID.Valid).To(BeTrue())
})
})
})
})
})