936 lines
32 KiB
Go
936 lines
32 KiB
Go
package radar_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"code.cloudfoundry.org/clock/fakeclock"
|
|
"code.cloudfoundry.org/lager"
|
|
"code.cloudfoundry.org/lager/lagertest"
|
|
"github.com/cloudfoundry/bosh-cli/director/template"
|
|
"github.com/concourse/concourse/atc"
|
|
"github.com/concourse/concourse/atc/creds"
|
|
"github.com/concourse/concourse/atc/db"
|
|
"github.com/concourse/concourse/atc/db/dbfakes"
|
|
"github.com/concourse/concourse/atc/db/lock"
|
|
"github.com/concourse/concourse/atc/db/lock/lockfakes"
|
|
. "github.com/concourse/concourse/atc/radar"
|
|
"github.com/concourse/concourse/atc/resource"
|
|
"github.com/concourse/concourse/atc/worker"
|
|
"github.com/concourse/concourse/atc/worker/workerfakes"
|
|
|
|
rfakes "github.com/concourse/concourse/atc/resource/resourcefakes"
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("ResourceTypeScanner", func() {
|
|
var (
|
|
epoch time.Time
|
|
|
|
fakeContainer *workerfakes.FakeContainer
|
|
fakeWorker *workerfakes.FakeWorker
|
|
fakePool *workerfakes.FakePool
|
|
fakeStrategy *workerfakes.FakeContainerPlacementStrategy
|
|
fakeResourceFactory *rfakes.FakeResourceFactory
|
|
fakeResourceConfigFactory *dbfakes.FakeResourceConfigFactory
|
|
fakeDBPipeline *dbfakes.FakePipeline
|
|
fakeResourceConfig *dbfakes.FakeResourceConfig
|
|
fakeResourceConfigScope *dbfakes.FakeResourceConfigScope
|
|
fakeClock *fakeclock.FakeClock
|
|
interval time.Duration
|
|
variables creds.Variables
|
|
|
|
fakeResourceType *dbfakes.FakeResourceType
|
|
interpolatedResourceTypes atc.VersionedResourceTypes
|
|
|
|
scanner Scanner
|
|
|
|
fakeLock *lockfakes.FakeLock
|
|
teamID = 123
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
fakeLock = &lockfakes.FakeLock{}
|
|
interval = 1 * time.Minute
|
|
variables = template.StaticVariables{
|
|
"source-params": "some-secret-sauce",
|
|
}
|
|
|
|
interpolatedResourceTypes = atc.VersionedResourceTypes{
|
|
{
|
|
ResourceType: atc.ResourceType{
|
|
Name: "some-custom-resource",
|
|
Type: "registry-image",
|
|
Source: atc.Source{"custom": "some-secret-sauce"},
|
|
Tags: atc.Tags{"some-tag"},
|
|
},
|
|
Version: atc.Version{"custom": "version"},
|
|
},
|
|
}
|
|
|
|
fakeClock = fakeclock.NewFakeClock(epoch)
|
|
|
|
fakeContainer = new(workerfakes.FakeContainer)
|
|
fakeStrategy = new(workerfakes.FakeContainerPlacementStrategy)
|
|
fakePool = new(workerfakes.FakePool)
|
|
fakeWorker = new(workerfakes.FakeWorker)
|
|
fakeResourceFactory = new(rfakes.FakeResourceFactory)
|
|
fakeResourceConfigFactory = new(dbfakes.FakeResourceConfigFactory)
|
|
fakeResourceType = new(dbfakes.FakeResourceType)
|
|
fakeDBPipeline = new(dbfakes.FakePipeline)
|
|
fakeResourceConfig = new(dbfakes.FakeResourceConfig)
|
|
fakeResourceConfig.IDReturns(123)
|
|
fakeResourceConfigScope = new(dbfakes.FakeResourceConfigScope)
|
|
fakeResourceConfigScope.IDReturns(123)
|
|
fakeResourceConfigScope.ResourceConfigReturns(fakeResourceConfig)
|
|
|
|
fakeResourceType.IDReturns(39)
|
|
fakeResourceType.NameReturns("some-custom-resource")
|
|
fakeResourceType.TypeReturns("registry-image")
|
|
fakeResourceType.SourceReturns(atc.Source{"custom": "((source-params))"})
|
|
fakeResourceType.VersionReturns(atc.Version{"custom": "version"})
|
|
fakeResourceType.TagsReturns(atc.Tags{"some-tag"})
|
|
fakeResourceType.SetResourceConfigReturns(fakeResourceConfigScope, nil)
|
|
|
|
fakeDBPipeline.IDReturns(42)
|
|
fakeDBPipeline.NameReturns("some-pipeline")
|
|
fakeDBPipeline.TeamIDReturns(teamID)
|
|
fakeDBPipeline.ReloadReturns(true, nil)
|
|
fakeDBPipeline.ResourceTypesReturns([]db.ResourceType{fakeResourceType}, nil)
|
|
fakeDBPipeline.ResourceTypeByIDReturns(fakeResourceType, true, nil)
|
|
|
|
scanner = NewResourceTypeScanner(
|
|
fakeClock,
|
|
fakePool,
|
|
fakeResourceFactory,
|
|
fakeResourceConfigFactory,
|
|
interval,
|
|
fakeDBPipeline,
|
|
"https://www.example.com",
|
|
variables,
|
|
fakeStrategy,
|
|
)
|
|
})
|
|
|
|
Describe("Run", func() {
|
|
var (
|
|
fakeResource *rfakes.FakeResource
|
|
actualInterval time.Duration
|
|
runErr error
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
fakeWorker.NameReturns("some-worker")
|
|
fakePool.FindOrChooseWorkerForContainerReturns(fakeWorker, nil)
|
|
|
|
fakeContainer.HandleReturns("some-handle")
|
|
fakeWorker.FindOrCreateContainerReturns(fakeContainer, nil)
|
|
|
|
fakeResource = new(rfakes.FakeResource)
|
|
fakeResourceFactory.NewResourceForContainerReturns(fakeResource)
|
|
})
|
|
|
|
JustBeforeEach(func() {
|
|
actualInterval, runErr = scanner.Run(lagertest.NewTestLogger("test"), fakeResourceType.ID())
|
|
})
|
|
|
|
Context("when the lock cannot be acquired", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigScope.AcquireResourceCheckingLockReturns(nil, false, nil)
|
|
})
|
|
|
|
It("does not check", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(Equal(0))
|
|
})
|
|
|
|
It("returns the configured interval", func() {
|
|
Expect(runErr).To(Equal(ErrFailedToAcquireLock))
|
|
Expect(actualInterval).To(Equal(interval))
|
|
})
|
|
})
|
|
|
|
Context("when the lock can be acquired", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigScope.AcquireResourceCheckingLockReturns(fakeLock, true, nil)
|
|
})
|
|
|
|
Context("when the last checked was not updated", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigScope.UpdateLastCheckStartTimeReturns(false, nil)
|
|
})
|
|
|
|
It("does not check", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(Equal(0))
|
|
})
|
|
|
|
It("returns the configured interval", func() {
|
|
Expect(runErr).To(Equal(ErrFailedToAcquireLock))
|
|
Expect(actualInterval).To(Equal(interval))
|
|
})
|
|
})
|
|
|
|
Context("when the last checked was updated", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigScope.UpdateLastCheckStartTimeReturns(true, nil)
|
|
})
|
|
|
|
It("checks immediately", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(Equal(1))
|
|
})
|
|
|
|
It("constructs the resource of the correct type", func() {
|
|
Expect(fakeResourceType.SetResourceConfigCallCount()).To(Equal(1))
|
|
resourceSource, resourceTypes := fakeResourceType.SetResourceConfigArgsForCall(0)
|
|
Expect(resourceSource).To(Equal(atc.Source{"custom": "some-secret-sauce"}))
|
|
Expect(resourceTypes).To(Equal(atc.VersionedResourceTypes{}))
|
|
|
|
_, _, owner, containerSpec, metadata, workerSpec, _ := fakePool.FindOrChooseWorkerForContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.Tags).To(Equal([]string{"some-tag"}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(metadata).To(Equal(db.ContainerMetadata{
|
|
Type: db.ContainerTypeCheck,
|
|
}))
|
|
Expect(workerSpec).To(Equal(worker.WorkerSpec{
|
|
ResourceType: "registry-image",
|
|
Tags: []string{"some-tag"},
|
|
ResourceTypes: atc.VersionedResourceTypes{},
|
|
TeamID: 123,
|
|
}))
|
|
|
|
Expect(fakeWorker.FindOrCreateContainerCallCount()).To(Equal(1))
|
|
_, _, _, owner, containerSpec, resourceTypes = fakeWorker.FindOrCreateContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.Tags).To(Equal([]string{"some-tag"}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(resourceTypes).To(Equal(atc.VersionedResourceTypes{}))
|
|
})
|
|
|
|
Context("when the resource type overrides a base resource type", func() {
|
|
BeforeEach(func() {
|
|
otherResourceType := fakeResourceType
|
|
|
|
fakeResourceType = new(dbfakes.FakeResourceType)
|
|
fakeResourceType.IDReturns(40)
|
|
fakeResourceType.NameReturns("registry-image")
|
|
fakeResourceType.TypeReturns("registry-image")
|
|
fakeResourceType.SourceReturns(atc.Source{"custom": "((source-params))"})
|
|
fakeResourceType.VersionReturns(atc.Version{"custom": "image-version"})
|
|
fakeResourceType.SetResourceConfigReturns(fakeResourceConfigScope, nil)
|
|
|
|
fakeDBPipeline.ResourceTypesReturns([]db.ResourceType{
|
|
fakeResourceType,
|
|
otherResourceType,
|
|
}, nil)
|
|
fakeDBPipeline.ResourceTypeByIDReturns(fakeResourceType, true, nil)
|
|
})
|
|
|
|
It("constructs the resource of the correct type", func() {
|
|
Expect(fakeResourceType.SetResourceConfigCallCount()).To(Equal(1))
|
|
resourceSource, resourceTypes := fakeResourceType.SetResourceConfigArgsForCall(0)
|
|
Expect(resourceSource).To(Equal(atc.Source{"custom": "some-secret-sauce"}))
|
|
Expect(resourceTypes).To(Equal(interpolatedResourceTypes))
|
|
|
|
Expect(fakeResourceType.SetCheckSetupErrorCallCount()).To(Equal(1))
|
|
err := fakeResourceType.SetCheckSetupErrorArgsForCall(0)
|
|
Expect(err).To(BeNil())
|
|
|
|
_, _, owner, containerSpec, metadata, workerSpec, _ := fakePool.FindOrChooseWorkerForContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(metadata).To(Equal(db.ContainerMetadata{
|
|
Type: db.ContainerTypeCheck,
|
|
}))
|
|
Expect(workerSpec).To(Equal(worker.WorkerSpec{
|
|
ResourceType: "registry-image",
|
|
ResourceTypes: interpolatedResourceTypes,
|
|
TeamID: 123,
|
|
}))
|
|
|
|
Expect(fakeWorker.FindOrCreateContainerCallCount()).To(Equal(1))
|
|
_, _, _, owner, containerSpec, resourceTypes = fakeWorker.FindOrCreateContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(resourceTypes).To(Equal(interpolatedResourceTypes))
|
|
})
|
|
})
|
|
|
|
Context("when the resource type config has a specified check interval", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceType.CheckEveryReturns("10ms")
|
|
fakeDBPipeline.ResourceTypeByIDReturns(fakeResourceType, true, nil)
|
|
})
|
|
|
|
It("leases for the configured interval", func() {
|
|
Expect(fakeResourceConfigScope.AcquireResourceCheckingLockCallCount()).To(Equal(1))
|
|
Expect(fakeResourceConfigScope.UpdateLastCheckStartTimeCallCount()).To(Equal(1))
|
|
|
|
leaseInterval, immediate := fakeResourceConfigScope.UpdateLastCheckStartTimeArgsForCall(0)
|
|
Expect(leaseInterval).To(Equal(10 * time.Millisecond))
|
|
Expect(immediate).To(BeFalse())
|
|
|
|
Eventually(fakeLock.ReleaseCallCount()).Should(Equal(1))
|
|
})
|
|
|
|
It("returns configured interval", func() {
|
|
Expect(actualInterval).To(Equal(10 * time.Millisecond))
|
|
})
|
|
|
|
Context("when the interval cannot be parsed", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceType.CheckEveryReturns("bad-value")
|
|
fakeDBPipeline.ResourceTypeByIDReturns(fakeResourceType, true, nil)
|
|
})
|
|
|
|
It("sets the check error", func() {
|
|
Expect(fakeResourceType.SetCheckSetupErrorCallCount()).To(Equal(1))
|
|
|
|
resourceErr := fakeResourceType.SetCheckSetupErrorArgsForCall(0)
|
|
Expect(resourceErr).To(MatchError("time: invalid duration bad-value"))
|
|
})
|
|
|
|
It("returns an error", func() {
|
|
Expect(runErr).To(HaveOccurred())
|
|
})
|
|
})
|
|
})
|
|
|
|
It("grabs a periodic resource checking lock before checking, breaks lock after done", func() {
|
|
Expect(fakeResourceConfigScope.AcquireResourceCheckingLockCallCount()).To(Equal(1))
|
|
Expect(fakeResourceConfigScope.UpdateLastCheckStartTimeCallCount()).To(Equal(1))
|
|
|
|
leaseInterval, immediate := fakeResourceConfigScope.UpdateLastCheckStartTimeArgsForCall(0)
|
|
Expect(leaseInterval).To(Equal(interval))
|
|
Expect(immediate).To(BeFalse())
|
|
|
|
Eventually(fakeLock.ReleaseCallCount()).Should(Equal(1))
|
|
})
|
|
|
|
Context("when there is no current version", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceType.VersionReturns(nil)
|
|
})
|
|
|
|
It("checks from nil", func() {
|
|
_, _, version := fakeResource.CheckArgsForCall(0)
|
|
Expect(version).To(BeNil())
|
|
})
|
|
})
|
|
|
|
Context("when there is a current version", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigVersion := new(dbfakes.FakeResourceConfigVersion)
|
|
fakeResourceConfigVersion.IDReturns(1)
|
|
fakeResourceConfigVersion.VersionReturns(db.Version{"version": "42"})
|
|
|
|
fakeResourceConfigScope.LatestVersionReturns(fakeResourceConfigVersion, true, nil)
|
|
})
|
|
|
|
It("checks with it", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(Equal(1))
|
|
_, _, version := fakeResource.CheckArgsForCall(0)
|
|
Expect(version).To(Equal(atc.Version{"version": "42"}))
|
|
})
|
|
})
|
|
|
|
Context("when the check returns versions", func() {
|
|
var checkedFrom chan atc.Version
|
|
|
|
var nextVersions []atc.Version
|
|
|
|
BeforeEach(func() {
|
|
checkedFrom = make(chan atc.Version, 100)
|
|
|
|
nextVersions = []atc.Version{
|
|
{"version": "1"},
|
|
{"version": "2"},
|
|
{"version": "3"},
|
|
}
|
|
|
|
checkResults := map[int][]atc.Version{
|
|
0: nextVersions,
|
|
}
|
|
|
|
check := 0
|
|
fakeResource.CheckStub = func(ctx context.Context, source atc.Source, from atc.Version) ([]atc.Version, error) {
|
|
defer GinkgoRecover()
|
|
|
|
Expect(source).To(Equal(atc.Source{"custom": "some-secret-sauce"}))
|
|
|
|
checkedFrom <- from
|
|
result := checkResults[check]
|
|
check++
|
|
|
|
return result, nil
|
|
}
|
|
})
|
|
|
|
It("saves all resource type versions", func() {
|
|
Eventually(fakeResourceConfigScope.SaveVersionsCallCount).Should(Equal(1))
|
|
|
|
version := fakeResourceConfigScope.SaveVersionsArgsForCall(0)
|
|
Expect(version).To(Equal(nextVersions))
|
|
})
|
|
})
|
|
|
|
Context("when checking fails", func() {
|
|
disaster := errors.New("nope")
|
|
|
|
BeforeEach(func() {
|
|
fakeResource.CheckReturns(nil, disaster)
|
|
})
|
|
|
|
It("exits with the failure", func() {
|
|
Expect(runErr).To(HaveOccurred())
|
|
Expect(runErr).To(Equal(disaster))
|
|
})
|
|
|
|
It("sets the resource's check error", func() {
|
|
Expect(fakeResourceConfigScope.SetCheckErrorCallCount()).To(Equal(1))
|
|
|
|
err := fakeResourceConfigScope.SetCheckErrorArgsForCall(0)
|
|
Expect(err).To(Equal(disaster))
|
|
})
|
|
})
|
|
|
|
Context("when the pipeline is paused", func() {
|
|
BeforeEach(func() {
|
|
fakeDBPipeline.CheckPausedReturns(true, nil)
|
|
})
|
|
|
|
It("does not check", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(BeZero())
|
|
})
|
|
|
|
It("returns the default interval", func() {
|
|
Expect(actualInterval).To(Equal(interval))
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(runErr).NotTo(HaveOccurred())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("Scan", func() {
|
|
var (
|
|
fakeResource *rfakes.FakeResource
|
|
runErr error
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
fakeWorker.NameReturns("some-worker")
|
|
fakePool.FindOrChooseWorkerForContainerReturns(fakeWorker, nil)
|
|
|
|
fakeContainer.HandleReturns("some-handle")
|
|
fakeWorker.FindOrCreateContainerReturns(fakeContainer, nil)
|
|
|
|
fakeResource = new(rfakes.FakeResource)
|
|
fakeResourceFactory.NewResourceForContainerReturns(fakeResource)
|
|
})
|
|
|
|
JustBeforeEach(func() {
|
|
runErr = scanner.Scan(lagertest.NewTestLogger("test"), fakeResourceType.ID())
|
|
})
|
|
|
|
Context("when the lock can be acquired and last checked is updated", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigScope.AcquireResourceCheckingLockReturns(fakeLock, true, nil)
|
|
fakeResourceConfigScope.UpdateLastCheckStartTimeReturns(true, nil)
|
|
})
|
|
|
|
It("checks immediately", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(Equal(1))
|
|
})
|
|
|
|
It("constructs the resource of the correct type", func() {
|
|
Expect(fakeResourceType.SetResourceConfigCallCount()).To(Equal(1))
|
|
resourceSource, resourceTypes := fakeResourceType.SetResourceConfigArgsForCall(0)
|
|
Expect(resourceSource).To(Equal(atc.Source{"custom": "some-secret-sauce"}))
|
|
Expect(resourceTypes).To(Equal(atc.VersionedResourceTypes{}))
|
|
|
|
Expect(fakeResourceType.SetCheckSetupErrorCallCount()).To(Equal(1))
|
|
err := fakeResourceType.SetCheckSetupErrorArgsForCall(0)
|
|
Expect(err).To(BeNil())
|
|
|
|
_, _, owner, containerSpec, metadata, workerSpec, _ := fakePool.FindOrChooseWorkerForContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.Tags).To(Equal([]string{"some-tag"}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(metadata).To(Equal(db.ContainerMetadata{
|
|
Type: db.ContainerTypeCheck,
|
|
}))
|
|
Expect(workerSpec).To(Equal(worker.WorkerSpec{
|
|
ResourceType: "registry-image",
|
|
Tags: []string{"some-tag"},
|
|
ResourceTypes: atc.VersionedResourceTypes{},
|
|
TeamID: 123,
|
|
}))
|
|
|
|
Expect(fakeWorker.FindOrCreateContainerCallCount()).To(Equal(1))
|
|
_, _, _, owner, containerSpec, resourceTypes = fakeWorker.FindOrCreateContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.Tags).To(Equal([]string{"some-tag"}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(resourceTypes).To(Equal(atc.VersionedResourceTypes{}))
|
|
})
|
|
|
|
Context("when the resource type depends on another custom type", func() {
|
|
var otherResourceType *dbfakes.FakeResourceType
|
|
|
|
BeforeEach(func() {
|
|
otherResourceType = new(dbfakes.FakeResourceType)
|
|
otherResourceType.IDReturns(39)
|
|
otherResourceType.NameReturns("custom-resource-parent")
|
|
otherResourceType.TypeReturns("registry-image")
|
|
|
|
fakeResourceType = new(dbfakes.FakeResourceType)
|
|
fakeResourceType.IDReturns(40)
|
|
fakeResourceType.NameReturns("custom-resource-child")
|
|
fakeResourceType.TypeReturns("custom-resource-parent")
|
|
fakeResourceType.SetResourceConfigReturns(fakeResourceConfigScope, nil)
|
|
|
|
// testing recursion is fun!
|
|
fakeDBPipeline.ResourceTypesReturnsOnCall(0, []db.ResourceType{
|
|
fakeResourceType,
|
|
otherResourceType,
|
|
}, nil)
|
|
fakeDBPipeline.ResourceTypeByIDReturnsOnCall(0, fakeResourceType, true, nil)
|
|
})
|
|
|
|
Context("when the custom type does not yet have a version", func() {
|
|
BeforeEach(func() {
|
|
otherResourceType.VersionReturns(nil)
|
|
})
|
|
|
|
It("checks for versions of the parent resource type", func() {
|
|
By("calling .scan() for the resource type name")
|
|
Expect(fakeDBPipeline.ResourceTypeByIDCallCount()).To(Equal(2))
|
|
Expect(fakeDBPipeline.ResourceTypeByIDArgsForCall(1)).To(Equal(39))
|
|
})
|
|
|
|
Context("when the check for the parent succeeds", func() {
|
|
It("reloads the resource types from the database", func() {
|
|
Expect(runErr).ToNot(HaveOccurred())
|
|
Expect(fakeDBPipeline.ResourceTypesCallCount()).To(Equal(4))
|
|
})
|
|
})
|
|
|
|
Context("somethinng fails in the parent resource scan", func() {
|
|
var parentResourceTypeErr = errors.New("jma says no recursion in production")
|
|
BeforeEach(func() {
|
|
fakeDBPipeline.ResourceTypeByIDReturnsOnCall(1, otherResourceType, true, parentResourceTypeErr)
|
|
})
|
|
|
|
It("returns the error from scanning the parent", func() {
|
|
Expect(runErr).To(Equal(parentResourceTypeErr))
|
|
})
|
|
|
|
It("saves the error to check_error on resource type row in db", func() {
|
|
Expect(fakeResourceType.SetCheckSetupErrorCallCount()).To(Equal(1))
|
|
|
|
err := fakeResourceType.SetCheckSetupErrorArgsForCall(0)
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(Equal("jma says no recursion in production"))
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when the custom type has a version already", func() {
|
|
BeforeEach(func() {
|
|
otherResourceType.VersionReturns(atc.Version{"custom": "image-version"})
|
|
})
|
|
|
|
It("does not check for versions of the parent resource type", func() {
|
|
Expect(fakeDBPipeline.ResourceTypeByIDCallCount()).To(Equal(1))
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when the resource type overrides a base resource type", func() {
|
|
BeforeEach(func() {
|
|
otherResourceType := fakeResourceType
|
|
|
|
fakeResourceType = new(dbfakes.FakeResourceType)
|
|
fakeResourceType.IDReturns(40)
|
|
fakeResourceType.NameReturns("registry-image")
|
|
fakeResourceType.TypeReturns("registry-image")
|
|
fakeResourceType.SourceReturns(atc.Source{"custom": "((source-params))"})
|
|
fakeResourceType.SetResourceConfigReturns(fakeResourceConfigScope, nil)
|
|
|
|
fakeDBPipeline.ResourceTypesReturns([]db.ResourceType{
|
|
fakeResourceType,
|
|
otherResourceType,
|
|
}, nil)
|
|
fakeDBPipeline.ResourceTypeByIDReturns(fakeResourceType, true, nil)
|
|
})
|
|
|
|
It("constructs the resource of the correct type", func() {
|
|
Expect(fakeResourceType.SetResourceConfigCallCount()).To(Equal(1))
|
|
resourceSource, resourceTypes := fakeResourceType.SetResourceConfigArgsForCall(0)
|
|
Expect(resourceSource).To(Equal(atc.Source{"custom": "some-secret-sauce"}))
|
|
Expect(resourceTypes).To(Equal(interpolatedResourceTypes))
|
|
|
|
_, _, owner, containerSpec, metadata, workerSpec, _ := fakePool.FindOrChooseWorkerForContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(workerSpec).To(Equal(worker.WorkerSpec{
|
|
ResourceType: "registry-image",
|
|
ResourceTypes: interpolatedResourceTypes,
|
|
TeamID: 123,
|
|
}))
|
|
Expect(metadata).To(Equal(db.ContainerMetadata{
|
|
Type: db.ContainerTypeCheck,
|
|
}))
|
|
|
|
Expect(fakeWorker.FindOrCreateContainerCallCount()).To(Equal(1))
|
|
_, _, _, owner, containerSpec, resourceTypes = fakeWorker.FindOrCreateContainerArgsForCall(0)
|
|
Expect(owner).To(Equal(db.NewResourceConfigCheckSessionContainerOwner(fakeResourceConfig, ContainerExpiries)))
|
|
Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{
|
|
ResourceType: "registry-image",
|
|
}))
|
|
Expect(containerSpec.TeamID).To(Equal(123))
|
|
Expect(resourceTypes).To(Equal(interpolatedResourceTypes))
|
|
})
|
|
})
|
|
|
|
It("grabs an immediate resource checking lock before checking, breaks lock after done", func() {
|
|
Expect(fakeResourceConfigScope.AcquireResourceCheckingLockCallCount()).To(Equal(1))
|
|
Expect(fakeResourceConfigScope.UpdateLastCheckStartTimeCallCount()).To(Equal(1))
|
|
|
|
leaseInterval, immediate := fakeResourceConfigScope.UpdateLastCheckStartTimeArgsForCall(0)
|
|
Expect(leaseInterval).To(Equal(interval))
|
|
Expect(immediate).To(BeTrue())
|
|
|
|
Eventually(fakeLock.ReleaseCallCount()).Should(Equal(1))
|
|
})
|
|
|
|
Context("when setting the resource config on the resource type fails", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceType.SetResourceConfigReturns(nil, errors.New("catastrophe"))
|
|
})
|
|
|
|
It("sets the check error and returns the error", func() {
|
|
Expect(runErr).To(HaveOccurred())
|
|
Expect(fakeResourceType.SetCheckSetupErrorCallCount()).To(Equal(1))
|
|
|
|
chkErr := fakeResourceType.SetCheckSetupErrorArgsForCall(0)
|
|
Expect(chkErr).To(MatchError("catastrophe"))
|
|
})
|
|
})
|
|
|
|
Context("when creating the container fails", func() {
|
|
BeforeEach(func() {
|
|
fakeWorker.FindOrCreateContainerReturns(nil, errors.New("catastrophe"))
|
|
})
|
|
|
|
It("sets the check error and returns the error", func() {
|
|
Expect(runErr).To(HaveOccurred())
|
|
Expect(fakeResourceConfigScope.SetCheckErrorCallCount()).To(Equal(1))
|
|
|
|
chkErr := fakeResourceConfigScope.SetCheckErrorArgsForCall(0)
|
|
Expect(chkErr).To(MatchError("catastrophe"))
|
|
})
|
|
})
|
|
|
|
Context("when finding or choosing the worker fails", func() {
|
|
BeforeEach(func() {
|
|
fakePool.FindOrChooseWorkerForContainerReturns(nil, errors.New("catastrophe"))
|
|
})
|
|
|
|
It("sets the check error and returns the error", func() {
|
|
Expect(runErr).To(HaveOccurred())
|
|
Expect(fakeResourceConfigScope.SetCheckErrorCallCount()).To(Equal(1))
|
|
|
|
chkErr := fakeResourceConfigScope.SetCheckErrorArgsForCall(0)
|
|
Expect(chkErr).To(MatchError("catastrophe"))
|
|
})
|
|
})
|
|
|
|
Context("when there is no current version", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceType.VersionReturns(nil)
|
|
})
|
|
|
|
It("checks from nil", func() {
|
|
_, _, version := fakeResource.CheckArgsForCall(0)
|
|
Expect(version).To(BeNil())
|
|
})
|
|
})
|
|
|
|
Context("when there is a current version", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigVersion := new(dbfakes.FakeResourceConfigVersion)
|
|
fakeResourceConfigVersion.IDReturns(1)
|
|
fakeResourceConfigVersion.VersionReturns(db.Version{"version": "42"})
|
|
|
|
fakeResourceConfigScope.LatestVersionReturns(fakeResourceConfigVersion, true, nil)
|
|
})
|
|
|
|
It("checks with it", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(Equal(1))
|
|
_, _, version := fakeResource.CheckArgsForCall(0)
|
|
Expect(version).To(Equal(atc.Version{"version": "42"}))
|
|
})
|
|
})
|
|
|
|
Context("when the check returns versions", func() {
|
|
var checkedFrom chan atc.Version
|
|
|
|
var nextVersions []atc.Version
|
|
|
|
BeforeEach(func() {
|
|
checkedFrom = make(chan atc.Version, 100)
|
|
|
|
nextVersions = []atc.Version{
|
|
{"version": "1"},
|
|
{"version": "2"},
|
|
{"version": "3"},
|
|
}
|
|
|
|
checkResults := map[int][]atc.Version{
|
|
0: nextVersions,
|
|
}
|
|
|
|
check := 0
|
|
fakeResource.CheckStub = func(ctx context.Context, source atc.Source, from atc.Version) ([]atc.Version, error) {
|
|
defer GinkgoRecover()
|
|
|
|
Expect(source).To(Equal(atc.Source{"custom": "some-secret-sauce"}))
|
|
|
|
checkedFrom <- from
|
|
result := checkResults[check]
|
|
check++
|
|
|
|
return result, nil
|
|
}
|
|
})
|
|
|
|
It("saves all resource type versions", func() {
|
|
Eventually(fakeResourceConfigScope.SaveVersionsCallCount).Should(Equal(1))
|
|
|
|
version := fakeResourceConfigScope.SaveVersionsArgsForCall(0)
|
|
Expect(version).To(Equal(nextVersions))
|
|
})
|
|
})
|
|
|
|
Context("when checking fails", func() {
|
|
disaster := errors.New("nope")
|
|
|
|
BeforeEach(func() {
|
|
fakeResource.CheckReturns(nil, disaster)
|
|
})
|
|
|
|
It("exits with the failure", func() {
|
|
Expect(runErr).To(HaveOccurred())
|
|
Expect(runErr).To(Equal(disaster))
|
|
})
|
|
})
|
|
|
|
Context("when the lock is not immediately available", func() {
|
|
BeforeEach(func() {
|
|
results := make(chan bool, 4)
|
|
results <- false
|
|
results <- false
|
|
results <- true
|
|
results <- true
|
|
close(results)
|
|
|
|
fakeResourceConfigScope.AcquireResourceCheckingLockStub = func(logger lager.Logger) (lock.Lock, bool, error) {
|
|
if <-results {
|
|
return fakeLock, true, nil
|
|
} else {
|
|
// allow the sleep to continue
|
|
go fakeClock.WaitForWatcherAndIncrement(time.Second)
|
|
return nil, false, nil
|
|
}
|
|
}
|
|
})
|
|
|
|
It("retries every second until it is", func() {
|
|
Expect(fakeResourceConfigScope.AcquireResourceCheckingLockCallCount()).To(Equal(3))
|
|
Expect(fakeLock.ReleaseCallCount()).To(Equal(1))
|
|
})
|
|
})
|
|
|
|
Context("when last checked interval is not immediately updated", func() {
|
|
BeforeEach(func() {
|
|
results := make(chan bool, 4)
|
|
results <- false
|
|
results <- false
|
|
results <- true
|
|
results <- true
|
|
close(results)
|
|
|
|
fakeResourceConfigScope.UpdateLastCheckStartTimeStub = func(interval time.Duration, immediate bool) (bool, error) {
|
|
if <-results {
|
|
return true, nil
|
|
} else {
|
|
// allow the sleep to continue
|
|
go fakeClock.WaitForWatcherAndIncrement(time.Second)
|
|
return false, nil
|
|
}
|
|
}
|
|
})
|
|
|
|
It("retries every second until it is", func() {
|
|
Expect(fakeResourceConfigScope.AcquireResourceCheckingLockCallCount()).To(Equal(3))
|
|
Expect(fakeResourceConfigScope.UpdateLastCheckStartTimeCallCount()).To(Equal(3))
|
|
|
|
leaseInterval, immediate := fakeResourceConfigScope.UpdateLastCheckStartTimeArgsForCall(0)
|
|
Expect(leaseInterval).To(Equal(interval))
|
|
Expect(immediate).To(BeTrue())
|
|
|
|
leaseInterval, immediate = fakeResourceConfigScope.UpdateLastCheckStartTimeArgsForCall(1)
|
|
Expect(leaseInterval).To(Equal(interval))
|
|
Expect(immediate).To(BeTrue())
|
|
|
|
leaseInterval, immediate = fakeResourceConfigScope.UpdateLastCheckStartTimeArgsForCall(2)
|
|
Expect(leaseInterval).To(Equal(interval))
|
|
Expect(immediate).To(BeTrue())
|
|
|
|
Expect(fakeLock.ReleaseCallCount()).To(Equal(3))
|
|
})
|
|
})
|
|
|
|
It("clears the resource's check error", func() {
|
|
Expect(fakeResourceConfigScope.SetCheckErrorCallCount()).To(Equal(1))
|
|
|
|
err := fakeResourceConfigScope.SetCheckErrorArgsForCall(0)
|
|
Expect(err).To(BeNil())
|
|
})
|
|
|
|
Context("when the pipeline is paused", func() {
|
|
BeforeEach(func() {
|
|
fakeDBPipeline.CheckPausedReturns(true, nil)
|
|
})
|
|
|
|
It("does not check", func() {
|
|
Expect(fakeResource.CheckCallCount()).To(BeZero())
|
|
})
|
|
|
|
It("does not return an error", func() {
|
|
Expect(runErr).NotTo(HaveOccurred())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("ScanFromVersion", func() {
|
|
var (
|
|
fakeResource *rfakes.FakeResource
|
|
fromVersion atc.Version
|
|
|
|
scanErr error
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
fakeWorker.NameReturns("some-worker")
|
|
fakePool.FindOrChooseWorkerForContainerReturns(fakeWorker, nil)
|
|
|
|
fakeContainer.HandleReturns("some-handle")
|
|
fakeWorker.FindOrCreateContainerReturns(fakeContainer, nil)
|
|
|
|
fakeResource = new(rfakes.FakeResource)
|
|
fakeResourceFactory.NewResourceForContainerReturns(fakeResource)
|
|
fromVersion = nil
|
|
})
|
|
|
|
JustBeforeEach(func() {
|
|
scanErr = scanner.ScanFromVersion(lagertest.NewTestLogger("test"), 57, fromVersion)
|
|
})
|
|
|
|
Context("if the lock can be acquired", func() {
|
|
BeforeEach(func() {
|
|
fakeResourceConfigScope.AcquireResourceCheckingLockReturns(fakeLock, true, nil)
|
|
fakeResourceConfigScope.UpdateLastCheckStartTimeReturns(true, nil)
|
|
fakeResourceConfigVersion := new(dbfakes.FakeResourceConfigVersion)
|
|
fakeResourceConfigVersion.IDReturns(1)
|
|
fakeResourceConfigVersion.VersionReturns(db.Version{"custom": "version"})
|
|
|
|
fakeResourceConfigScope.LatestVersionReturns(fakeResourceConfigVersion, true, nil)
|
|
})
|
|
|
|
Context("when fromVersion is nil", func() {
|
|
It("checks from the current version", func() {
|
|
_, _, version := fakeResource.CheckArgsForCall(0)
|
|
Expect(version).To(Equal(atc.Version{"custom": "version"}))
|
|
})
|
|
})
|
|
|
|
Context("when fromVersion is specified", func() {
|
|
BeforeEach(func() {
|
|
fromVersion = atc.Version{
|
|
"version": "1",
|
|
}
|
|
})
|
|
|
|
It("checks from it", func() {
|
|
_, _, version := fakeResource.CheckArgsForCall(0)
|
|
Expect(version).To(Equal(atc.Version{"version": "1"}))
|
|
})
|
|
|
|
Context("when the check returns only the latest version", func() {
|
|
BeforeEach(func() {
|
|
fakeResource.CheckReturns([]atc.Version{fromVersion}, nil)
|
|
})
|
|
|
|
It("saves it", func() {
|
|
Expect(fakeResourceConfigScope.SaveVersionsCallCount()).To(Equal(1))
|
|
versions := fakeResourceConfigScope.SaveVersionsArgsForCall(0)
|
|
Expect(versions[0]).To(Equal(fromVersion))
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when checking fails with ErrResourceScriptFailed", func() {
|
|
scriptFail := resource.ErrResourceScriptFailed{}
|
|
|
|
BeforeEach(func() {
|
|
fakeResource.CheckReturns(nil, scriptFail)
|
|
})
|
|
|
|
It("returns the error", func() {
|
|
Expect(scanErr).To(Equal(scriptFail))
|
|
})
|
|
})
|
|
|
|
Context("when the resource is not in the database", func() {
|
|
BeforeEach(func() {
|
|
fakeDBPipeline.ResourceTypeByIDReturns(nil, false, nil)
|
|
})
|
|
|
|
It("returns an error", func() {
|
|
Expect(scanErr).To(HaveOccurred())
|
|
Expect(scanErr.Error()).To(ContainSubstring("resource type not found: 57"))
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|