370 lines
11 KiB
Go
370 lines
11 KiB
Go
package db_test
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/concourse/concourse/atc"
|
|
"github.com/concourse/concourse/atc/db"
|
|
"github.com/concourse/concourse/atc/db/algorithm"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("ResourceCacheLifecycle", func() {
|
|
|
|
var resourceCacheLifecycle db.ResourceCacheLifecycle
|
|
|
|
BeforeEach(func() {
|
|
resourceCacheLifecycle = db.NewResourceCacheLifecycle(dbConn)
|
|
})
|
|
|
|
Describe("CleanUpInvalidCaches", func() {
|
|
Context("the resource cache is used by a build", func() {
|
|
resourceCacheForOneOffBuild := func() (db.UsedResourceCache, db.Build) {
|
|
build, err := defaultTeam.CreateOneOffBuild()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
return createResourceCacheWithUser(db.ForBuild(build.ID())), build
|
|
}
|
|
|
|
resourceCacheForJobBuild := func() (db.UsedResourceCache, db.Build) {
|
|
build, err := defaultJob.CreateBuild()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
return createResourceCacheWithUser(db.ForBuild(build.ID())), build
|
|
}
|
|
|
|
Context("when its a one off build", func() {
|
|
It("doesn't delete the resource cache", func() {
|
|
_, _ = resourceCacheForOneOffBuild()
|
|
|
|
err := resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
})
|
|
|
|
Context("when the build goes away", func() {
|
|
BeforeEach(func() {
|
|
_, build := resourceCacheForOneOffBuild()
|
|
|
|
_, err := build.Delete()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
|
|
err = resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("deletes the resource cache", func() {
|
|
Expect(countResourceCaches()).To(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("when the cache is for a saved image resource version for a finished build", func() {
|
|
setBuildStatus := func(a db.BuildStatus) {
|
|
resourceCache, build := resourceCacheForOneOffBuild()
|
|
|
|
err := build.SaveImageResourceVersion(resourceCache)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = build.SetInterceptible(false)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = build.Finish(a)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = resourceCacheLifecycle.CleanUsesForFinishedBuilds(logger)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
}
|
|
|
|
Context("when the build has succeeded", func() {
|
|
It("does not remove the image resource cache", func() {
|
|
setBuildStatus(db.BuildStatusSucceeded)
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
|
|
err := resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("when build has not succeeded", func() {
|
|
It("does not removes the image resource cache", func() {
|
|
setBuildStatus(db.BuildStatusFailed)
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
|
|
err := resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when its a build of a job in a pipeline", func() {
|
|
Context("when the cache is for a saved image resource version for a finished build", func() {
|
|
setBuildStatus := func(a db.BuildStatus) (db.UsedResourceCache, db.Build) {
|
|
resourceCache, build := resourceCacheForJobBuild()
|
|
Expect(build.JobID()).ToNot(BeZero())
|
|
|
|
err := build.SaveImageResourceVersion(resourceCache)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = build.SetInterceptible(false)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = build.Finish(a)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = resourceCacheLifecycle.CleanUsesForFinishedBuilds(logger)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
return resourceCache, build
|
|
}
|
|
|
|
Context("when the build has succeeded", func() {
|
|
It("does not remove the resource cache for the most recent build", func() {
|
|
setBuildStatus(db.BuildStatusSucceeded)
|
|
Expect(countResourceCaches()).To(Equal(1))
|
|
|
|
err := resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).To(Equal(1))
|
|
})
|
|
|
|
It("removes resource caches for previous finished builds", func() {
|
|
resourceCache1, _ := setBuildStatus(db.BuildStatusFailed)
|
|
resourceCache2, _ := setBuildStatus(db.BuildStatusSucceeded)
|
|
Expect(resourceCache1.ID()).ToNot(Equal(resourceCache2.ID()))
|
|
|
|
Expect(countResourceCaches()).To(Equal(2))
|
|
|
|
err := resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).To(Equal(1))
|
|
})
|
|
})
|
|
|
|
Context("when build has not succeeded", func() {
|
|
It("does not remove the image resource cache", func() {
|
|
setBuildStatus(db.BuildStatusFailed)
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
|
|
err := resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("the resource cache is used by a container", func() {
|
|
var (
|
|
container db.CreatingContainer
|
|
containerOwner db.ContainerOwner
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
resourceConfig, err := resourceConfigFactory.FindOrCreateResourceConfig(
|
|
"some-base-resource-type",
|
|
atc.Source{
|
|
"some": "source",
|
|
},
|
|
atc.VersionedResourceTypes{},
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
containerOwner = db.NewResourceConfigCheckSessionContainerOwner(resourceConfig, db.ContainerOwnerExpiries{})
|
|
|
|
container, err = defaultWorker.CreateContainer(containerOwner, db.ContainerMetadata{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
_ = createResourceCacheWithUser(db.ForContainer(container.ID()))
|
|
})
|
|
|
|
Context("and the container still exists", func() {
|
|
BeforeEach(func() {
|
|
err := resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("doesn't delete the resource cache", func() {
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("and the container has been deleted", func() {
|
|
BeforeEach(func() {
|
|
createdContainer, err := container.Created()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
destroyingContainer, err := createdContainer.Destroying()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
_, err = destroyingContainer.Destroy()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("deletes the resource cache", func() {
|
|
Expect(countResourceCaches()).To(BeZero())
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("when the cache is for a custom resource type", func() {
|
|
It("does not remove the cache if the type is still configured", func() {
|
|
_, err := resourceConfigFactory.FindOrCreateResourceConfig(
|
|
"some-type",
|
|
atc.Source{
|
|
"some": "source",
|
|
},
|
|
atc.VersionedResourceTypes{
|
|
atc.VersionedResourceType{
|
|
ResourceType: atc.ResourceType{
|
|
Name: "some-type",
|
|
Type: "some-base-resource-type",
|
|
Source: atc.Source{
|
|
"some": "source",
|
|
},
|
|
},
|
|
Version: atc.Version{"showme": "whatyougot"},
|
|
},
|
|
},
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
err = resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
})
|
|
|
|
It("removes the cache if the type is no longer configured", func() {
|
|
_, err := resourceConfigFactory.FindOrCreateResourceConfig(
|
|
"some-type",
|
|
atc.Source{
|
|
"some": "source",
|
|
},
|
|
atc.VersionedResourceTypes{
|
|
atc.VersionedResourceType{
|
|
ResourceType: atc.ResourceType{
|
|
Name: "some-type",
|
|
Type: "some-base-resource-type",
|
|
Source: atc.Source{
|
|
"some": "source",
|
|
},
|
|
},
|
|
Version: atc.Version{"showme": "whatyougot"},
|
|
},
|
|
},
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = resourceConfigCheckSessionLifecycle.CleanInactiveResourceConfigCheckSessions()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = resourceConfigFactory.CleanUnreferencedConfigs()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
|
|
err = resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).To(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("when the cache is for a resource version used as an input for the next build of a job", func() {
|
|
It("does not remove the cache", func() {
|
|
err := defaultPipeline.Unpause()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
resourceConfigScope, err := defaultResource.SetResourceConfig(
|
|
atc.Source{"some": "source"},
|
|
atc.VersionedResourceTypes{},
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
containerOwner := db.NewResourceConfigCheckSessionContainerOwner(resourceConfigScope.ResourceConfig(), db.ContainerOwnerExpiries{})
|
|
|
|
container, err := defaultWorker.CreateContainer(containerOwner, db.ContainerMetadata{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
_ = createResourceCacheWithUser(db.ForContainer(container.ID()))
|
|
|
|
err = resourceConfigScope.SaveVersions([]atc.Version{{"some": "version"}})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
resourceConfigVersion, found, err := resourceConfigScope.FindVersion(atc.Version{"some": "version"})
|
|
Expect(found).To(BeTrue())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = defaultJob.SaveNextInputMapping(algorithm.InputMapping{
|
|
"some-resource": algorithm.InputVersion{
|
|
VersionID: resourceConfigVersion.ID(),
|
|
ResourceID: defaultResource.ID(),
|
|
},
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
createdContainer, err := container.Created()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
destroyingContainer, err := createdContainer.Destroying()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
_, err = destroyingContainer.Destroy()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
|
|
err = resourceCacheLifecycle.CleanUpInvalidCaches(logger.Session("resource-cache-lifecycle"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
Expect(countResourceCaches()).ToNot(BeZero())
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
func countResourceCaches() int {
|
|
var result int
|
|
err := psql.Select("count(*)").
|
|
From("resource_caches").
|
|
RunWith(dbConn).
|
|
QueryRow().
|
|
Scan(&result)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
func createResourceCacheWithUser(resourceCacheUser db.ResourceCacheUser) db.UsedResourceCache {
|
|
usedResourceCache, err := resourceCacheFactory.FindOrCreateResourceCache(
|
|
resourceCacheUser,
|
|
"some-base-resource-type",
|
|
atc.Version{"some": "version"},
|
|
atc.Source{
|
|
"some": "source",
|
|
},
|
|
atc.Params{"some": fmt.Sprintf("param-%d", time.Now().UnixNano())},
|
|
atc.VersionedResourceTypes{},
|
|
)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
return usedResourceCache
|
|
}
|