github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/gc/resource_cache_collector_test.go (about) 1 package gc_test 2 3 import ( 4 "context" 5 "fmt" 6 7 sq "github.com/Masterminds/squirrel" 8 "github.com/pf-qiu/concourse/v6/atc" 9 "github.com/pf-qiu/concourse/v6/atc/db" 10 "github.com/pf-qiu/concourse/v6/atc/db/dbtest" 11 "github.com/pf-qiu/concourse/v6/atc/gc" 12 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 ) 16 17 var _ = Describe("ResourceCacheCollector", func() { 18 var collector GcCollector 19 var buildCollector GcCollector 20 21 BeforeEach(func() { 22 collector = gc.NewResourceCacheCollector(resourceCacheLifecycle) 23 buildCollector = gc.NewBuildCollector(buildFactory) 24 }) 25 26 Describe("Run", func() { 27 Describe("resource caches", func() { 28 var resourceCacheUseCollector GcCollector 29 30 var oneOffBuild db.Build 31 var jobBuild db.Build 32 33 var oneOffCache db.UsedResourceCache 34 var jobCache db.UsedResourceCache 35 36 var scenario *dbtest.Scenario 37 38 BeforeEach(func() { 39 resourceCacheUseCollector = gc.NewResourceCacheUseCollector(resourceCacheLifecycle) 40 41 scenario = dbtest.Setup( 42 builder.WithPipeline(atc.Config{ 43 Resources: atc.ResourceConfigs{ 44 { 45 Name: "some-resource", 46 Type: "some-base-type", 47 Source: atc.Source{"some": "source"}, 48 }, 49 }, 50 Jobs: atc.JobConfigs{ 51 { 52 Name: "some-job", 53 }, 54 { 55 Name: "some-other-job", 56 }, 57 }, 58 }), 59 builder.WithResourceVersions("some-resource"), 60 ) 61 62 oneOffBuild, err = scenario.Team.CreateOneOffBuild() 63 Expect(err).ToNot(HaveOccurred()) 64 65 oneOffCache, err = resourceCacheFactory.FindOrCreateResourceCache( 66 db.ForBuild(oneOffBuild.ID()), 67 "some-base-type", 68 atc.Version{"some": "version"}, 69 atc.Source{ 70 "some": "source", 71 }, 72 nil, 73 atc.VersionedResourceTypes{}, 74 ) 75 Expect(err).NotTo(HaveOccurred()) 76 77 jobBuild, err = scenario.Job("some-job").CreateBuild() 78 Expect(err).ToNot(HaveOccurred()) 79 80 jobCache, err = resourceCacheFactory.FindOrCreateResourceCache( 81 db.ForBuild(jobBuild.ID()), 82 "some-base-type", 83 atc.Version{"some": "version"}, 84 atc.Source{ 85 "some": "source", 86 }, 87 nil, 88 atc.VersionedResourceTypes{}, 89 ) 90 Expect(err).NotTo(HaveOccurred()) 91 }) 92 93 resourceCacheExists := func(resourceCache db.UsedResourceCache) bool { 94 var count int 95 err = psql.Select("COUNT(*)"). 96 From("resource_caches"). 97 Where(sq.Eq{"id": resourceCache.ID()}). 98 RunWith(dbConn). 99 QueryRow(). 100 Scan(&count) 101 Expect(err).NotTo(HaveOccurred()) 102 return count == 1 103 } 104 105 JustBeforeEach(func() { 106 Expect(buildCollector.Run(context.TODO())).To(Succeed()) 107 Expect(resourceCacheUseCollector.Run(context.TODO())).To(Succeed()) 108 Expect(collector.Run(context.TODO())).To(Succeed()) 109 }) 110 111 Context("when the resource cache is still in use", func() { 112 It("does not delete the cache", func() { 113 Expect(collector.Run(context.TODO())).To(Succeed()) 114 Expect(resourceCacheExists(oneOffCache)).To(BeTrue()) 115 Expect(resourceCacheExists(jobCache)).To(BeTrue()) 116 }) 117 }) 118 119 Context("when the cache is no longer in use", func() { 120 BeforeEach(func() { 121 Expect(oneOffBuild.Finish(db.BuildStatusSucceeded)).To(Succeed()) 122 Expect(jobBuild.Finish(db.BuildStatusSucceeded)).To(Succeed()) 123 }) 124 125 Context("when the cache is an input to a job", func() { 126 BeforeEach(func() { 127 var versionMD5 string 128 version := `{"some":"version"}` 129 err = psql.Insert("resource_config_versions"). 130 Columns("version", "version_md5", "metadata", "resource_config_scope_id"). 131 Values(version, sq.Expr(fmt.Sprintf("md5('%s')", version)), `null`, jobCache.ResourceConfig().ID()). 132 Suffix("RETURNING version_md5"). 133 RunWith(dbConn).QueryRow().Scan(&versionMD5) 134 Expect(err).NotTo(HaveOccurred()) 135 136 Expect(scenario.Job("some-job").SaveNextInputMapping(db.InputMapping{ 137 "whatever": db.InputResult{ 138 Input: &db.AlgorithmInput{ 139 AlgorithmVersion: db.AlgorithmVersion{ 140 Version: db.ResourceVersion(versionMD5), 141 ResourceID: scenario.Resource("some-resource").ID(), 142 }, 143 }, 144 }, 145 }, true)).To(Succeed()) 146 }) 147 148 Context("when pipeline is paused", func() { 149 BeforeEach(func() { 150 err := scenario.Pipeline.Pause() 151 Expect(err).NotTo(HaveOccurred()) 152 }) 153 154 It("removes the cache", func() { 155 Expect(resourceCacheExists(jobCache)).To(BeFalse()) 156 }) 157 }) 158 159 Context("when pipeline is not paused", func() { 160 It("leaves it alone", func() { 161 Expect(resourceCacheExists(jobCache)).To(BeTrue()) 162 }) 163 }) 164 }) 165 166 Context("when the cache is an image resource version for a job build", func() { 167 BeforeEach(func() { 168 err := jobBuild.SaveImageResourceVersion(jobCache) 169 Expect(err).NotTo(HaveOccurred()) 170 }) 171 172 It("leaves it alone", func() { 173 Expect(resourceCacheExists(jobCache)).To(BeTrue()) 174 }) 175 176 Context("when another build of the same job exists with a different image cache", func() { 177 var secondJobBuild db.Build 178 var secondJobCache db.UsedResourceCache 179 180 BeforeEach(func() { 181 secondJobBuild, err = scenario.Job("some-job").CreateBuild() 182 Expect(err).ToNot(HaveOccurred()) 183 184 secondJobCache, err = resourceCacheFactory.FindOrCreateResourceCache( 185 db.ForBuild(secondJobBuild.ID()), 186 "some-base-type", 187 atc.Version{"some": "new-version"}, 188 atc.Source{ 189 "some": "source", 190 }, 191 nil, 192 atc.VersionedResourceTypes{}, 193 ) 194 Expect(err).NotTo(HaveOccurred()) 195 196 Expect(secondJobBuild.SaveImageResourceVersion(secondJobCache)).To(Succeed()) 197 }) 198 199 Context("when the second build succeeds", func() { 200 BeforeEach(func() { 201 Expect(secondJobBuild.Finish(db.BuildStatusSucceeded)).To(Succeed()) 202 }) 203 204 It("keeps the new cache and removes the old one", func() { 205 Expect(resourceCacheExists(jobCache)).To(BeFalse()) 206 Expect(resourceCacheExists(secondJobCache)).To(BeTrue()) 207 }) 208 }) 209 210 Context("when the second build fails", func() { 211 BeforeEach(func() { 212 Expect(secondJobBuild.Finish(db.BuildStatusFailed)).To(Succeed()) 213 }) 214 215 It("keeps the new cache and the old one", func() { 216 Expect(resourceCacheExists(jobCache)).To(BeTrue()) 217 Expect(resourceCacheExists(secondJobCache)).To(BeTrue()) 218 }) 219 }) 220 }) 221 222 Context("when another build of a different job exists with a different image cache", func() { 223 var secondJobBuild db.Build 224 var secondJobCache db.UsedResourceCache 225 226 BeforeEach(func() { 227 secondJobBuild, err = scenario.Job("some-other-job").CreateBuild() 228 Expect(err).ToNot(HaveOccurred()) 229 230 secondJobCache, err = resourceCacheFactory.FindOrCreateResourceCache( 231 db.ForBuild(secondJobBuild.ID()), 232 "some-base-type", 233 atc.Version{"some": "new-version"}, 234 atc.Source{ 235 "some": "source", 236 }, 237 nil, 238 atc.VersionedResourceTypes{}, 239 ) 240 Expect(err).NotTo(HaveOccurred()) 241 242 Expect(secondJobBuild.SaveImageResourceVersion(secondJobCache)).To(Succeed()) 243 }) 244 245 Context("when the second build succeeds", func() { 246 BeforeEach(func() { 247 Expect(secondJobBuild.Finish(db.BuildStatusSucceeded)).To(Succeed()) 248 }) 249 250 It("keeps the new cache and the old one", func() { 251 Expect(resourceCacheExists(jobCache)).To(BeTrue()) 252 Expect(resourceCacheExists(secondJobCache)).To(BeTrue()) 253 }) 254 }) 255 256 Context("when the second build fails", func() { 257 BeforeEach(func() { 258 Expect(secondJobBuild.Finish(db.BuildStatusFailed)).To(Succeed()) 259 }) 260 261 It("keeps the new cache and the old one", func() { 262 Expect(resourceCacheExists(jobCache)).To(BeTrue()) 263 Expect(resourceCacheExists(secondJobCache)).To(BeTrue()) 264 }) 265 }) 266 }) 267 }) 268 269 Context("when the cache is an image resource version for a one-off build", func() { 270 BeforeEach(func() { 271 err := oneOffBuild.SaveImageResourceVersion(oneOffCache) 272 Expect(err).NotTo(HaveOccurred()) 273 }) 274 275 Context("when the build finished recently", func() { 276 It("leaves it alone", func() { 277 Expect(resourceCacheExists(oneOffCache)).To(BeTrue()) 278 }) 279 }) 280 281 Context("when the build finished a day ago", func() { 282 BeforeEach(func() { 283 _, err := psql.Update("builds"). 284 Set("end_time", sq.Expr("NOW() - '25 hours'::interval")). 285 Where(sq.Eq{"id": oneOffBuild.ID()}). 286 RunWith(dbConn). 287 Exec() 288 Expect(err).ToNot(HaveOccurred()) 289 }) 290 291 It("removes the cache", func() { 292 Expect(resourceCacheExists(oneOffCache)).To(BeFalse()) 293 }) 294 }) 295 }) 296 }) 297 }) 298 }) 299 })