github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/resource_config_factory_test.go (about) 1 package db_test 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/pf-qiu/concourse/v6/atc" 8 "github.com/pf-qiu/concourse/v6/atc/db" 9 . "github.com/onsi/ginkgo" 10 . "github.com/onsi/gomega" 11 ) 12 13 var _ = Describe("ResourceConfigFactory", func() { 14 var build db.Build 15 16 BeforeEach(func() { 17 var err error 18 job, found, err := defaultPipeline.Job("some-job") 19 Expect(err).NotTo(HaveOccurred()) 20 Expect(found).To(BeTrue()) 21 22 build, err = job.CreateBuild() 23 Expect(err).NotTo(HaveOccurred()) 24 }) 25 26 Context("when a config is created", func() { 27 var resourceConfig db.ResourceConfig 28 29 BeforeEach(func() { 30 var err error 31 resourceConfig, err = resourceConfigFactory.FindOrCreateResourceConfig( 32 "some-base-resource-type", 33 atc.Source{"some": "unique-source"}, 34 atc.VersionedResourceTypes{}, 35 ) 36 Expect(err).ToNot(HaveOccurred()) 37 }) 38 39 It("has a recent 'last referenced' value", func() { 40 Expect(resourceConfig.LastReferenced()).To(BeTemporally("~", time.Now(), time.Minute)) 41 }) 42 43 Context("and created again", func() { 44 var sameConfig db.ResourceConfig 45 46 BeforeEach(func() { 47 var err error 48 sameConfig, err = resourceConfigFactory.FindOrCreateResourceConfig( 49 "some-base-resource-type", 50 atc.Source{"some": "unique-source"}, 51 atc.VersionedResourceTypes{}, 52 ) 53 Expect(err).ToNot(HaveOccurred()) 54 }) 55 56 It("returns the same config, but with a newer 'last referenced' time", func() { 57 Expect(sameConfig.ID()).To(Equal(resourceConfig.ID())) 58 Expect(sameConfig.LastReferenced()).To(BeTemporally(">", resourceConfig.LastReferenced())) 59 }) 60 }) 61 62 Context("when cleaning up with no grace period", func() { 63 It("removes the config immediately", func() { 64 Expect(resourceConfigFactory.CleanUnreferencedConfigs(0)).To(Succeed()) 65 66 recreated, err := resourceConfigFactory.FindOrCreateResourceConfig( 67 "some-base-resource-type", 68 atc.Source{"some": "unique-source"}, 69 atc.VersionedResourceTypes{}, 70 ) 71 Expect(err).ToNot(HaveOccurred()) 72 Expect(recreated.ID()).ToNot(Equal(resourceConfig.ID())) 73 }) 74 }) 75 76 Context("when cleaning up with a grace period", func() { 77 It("spares the config", func() { 78 Expect(resourceConfigFactory.CleanUnreferencedConfigs(time.Hour)).To(Succeed()) 79 80 recreated, err := resourceConfigFactory.FindOrCreateResourceConfig( 81 "some-base-resource-type", 82 atc.Source{"some": "unique-source"}, 83 atc.VersionedResourceTypes{}, 84 ) 85 Expect(err).ToNot(HaveOccurred()) 86 Expect(recreated.ID()).To(Equal(resourceConfig.ID())) 87 }) 88 }) 89 }) 90 91 Context("when the resource config is concurrently created", func() { 92 BeforeEach(func() { 93 Expect(build.Finish(db.BuildStatusSucceeded)).To(Succeed()) 94 Expect(build.SetInterceptible(false)).To(Succeed()) 95 }) 96 97 It("consistently is able to be created", func() { 98 // enable concurrent use of database. this is set to 1 by default to 99 // ensure methods don't require more than one in a single connection, 100 // which can cause deadlocking as the pool is limited. 101 dbConn.SetMaxOpenConns(2) 102 103 done := make(chan struct{}) 104 105 wg := new(sync.WaitGroup) 106 wg.Add(1) 107 go func() { 108 defer GinkgoRecover() 109 defer wg.Done() 110 111 for { 112 select { 113 case <-done: 114 return 115 default: 116 _, err := resourceConfigFactory.FindOrCreateResourceConfig("some-base-resource-type", atc.Source{"some": "unique-source"}, atc.VersionedResourceTypes{}) 117 Expect(err).ToNot(HaveOccurred()) 118 } 119 } 120 }() 121 122 wg.Add(1) 123 go func() { 124 defer GinkgoRecover() 125 defer close(done) 126 defer wg.Done() 127 128 for i := 0; i < 100; i++ { 129 _, err := resourceConfigFactory.FindOrCreateResourceConfig("some-base-resource-type", atc.Source{"some": "unique-source"}, atc.VersionedResourceTypes{}) 130 Expect(err).ToNot(HaveOccurred()) 131 } 132 }() 133 134 wg.Wait() 135 }) 136 }) 137 Context("when the resource config is concurrently deleted and created", func() { 138 BeforeEach(func() { 139 Expect(build.Finish(db.BuildStatusSucceeded)).To(Succeed()) 140 Expect(build.SetInterceptible(false)).To(Succeed()) 141 }) 142 143 It("consistently is able to be used", func() { 144 // enable concurrent use of database. this is set to 1 by default to 145 // ensure methods don't require more than one in a single connection, 146 // which can cause deadlocking as the pool is limited. 147 dbConn.SetMaxOpenConns(2) 148 149 done := make(chan struct{}) 150 151 wg := new(sync.WaitGroup) 152 wg.Add(1) 153 go func() { 154 defer GinkgoRecover() 155 defer wg.Done() 156 157 for { 158 select { 159 case <-done: 160 return 161 default: 162 Expect(resourceConfigFactory.CleanUnreferencedConfigs(0)).To(Succeed()) 163 } 164 } 165 }() 166 167 wg.Add(1) 168 go func() { 169 defer GinkgoRecover() 170 defer close(done) 171 defer wg.Done() 172 173 for i := 0; i < 100; i++ { 174 _, err := resourceConfigFactory.FindOrCreateResourceConfig("some-base-resource-type", atc.Source{"some": "unique-source"}, atc.VersionedResourceTypes{}) 175 Expect(err).ToNot(HaveOccurred()) 176 } 177 }() 178 179 wg.Wait() 180 }) 181 }) 182 183 Describe("FindResourceConfigByID", func() { 184 var ( 185 resourceConfigID int 186 resourceConfig db.ResourceConfig 187 createdResourceConfig db.ResourceConfig 188 found bool 189 err error 190 ) 191 192 JustBeforeEach(func() { 193 resourceConfig, found, err = resourceConfigFactory.FindResourceConfigByID(resourceConfigID) 194 Expect(err).ToNot(HaveOccurred()) 195 }) 196 197 Context("when the resource config does exist", func() { 198 Context("when the resource config uses a base resource type", func() { 199 BeforeEach(func() { 200 setupTx, err := dbConn.Begin() 201 Expect(err).ToNot(HaveOccurred()) 202 203 brt := db.BaseResourceType{ 204 Name: "base-resource-type-name", 205 } 206 207 _, err = brt.FindOrCreate(setupTx, false) 208 Expect(err).NotTo(HaveOccurred()) 209 Expect(setupTx.Commit()).To(Succeed()) 210 211 createdResourceConfig, err = resourceConfigFactory.FindOrCreateResourceConfig("base-resource-type-name", atc.Source{}, atc.VersionedResourceTypes{}) 212 Expect(err).ToNot(HaveOccurred()) 213 Expect(createdResourceConfig).ToNot(BeNil()) 214 215 resourceConfigID = createdResourceConfig.ID() 216 }) 217 218 It("should find the resource config using the resource's config id", func() { 219 Expect(found).To(BeTrue()) 220 Expect(resourceConfig).ToNot(BeNil()) 221 Expect(resourceConfig.ID()).To(Equal(resourceConfigID)) 222 Expect(resourceConfig.CreatedByBaseResourceType()).To(Equal(createdResourceConfig.CreatedByBaseResourceType())) 223 }) 224 }) 225 226 Context("when the resource config uses a custom resource type", func() { 227 BeforeEach(func() { 228 pipelineResourceTypes, err := defaultPipeline.ResourceTypes() 229 Expect(err).ToNot(HaveOccurred()) 230 231 createdResourceConfig, err = resourceConfigFactory.FindOrCreateResourceConfig("some-type", atc.Source{}, pipelineResourceTypes.Deserialize()) 232 Expect(err).ToNot(HaveOccurred()) 233 Expect(createdResourceConfig).ToNot(BeNil()) 234 235 resourceConfigID = createdResourceConfig.ID() 236 }) 237 238 It("should find the resource config using the resource's config id", func() { 239 Expect(found).To(BeTrue()) 240 Expect(resourceConfig).ToNot(BeNil()) 241 Expect(resourceConfig.ID()).To(Equal(resourceConfigID)) 242 Expect(resourceConfig.CreatedByResourceCache().ID()).To(Equal(createdResourceConfig.CreatedByResourceCache().ID())) 243 Expect(resourceConfig.CreatedByResourceCache().ResourceConfig().ID()).To(Equal(createdResourceConfig.CreatedByResourceCache().ResourceConfig().ID())) 244 }) 245 }) 246 }) 247 248 Context("when the resource config id does not exist", func() { 249 BeforeEach(func() { 250 resourceConfigID = 123 251 }) 252 253 It("should not find the resource config", func() { 254 Expect(found).To(BeFalse()) 255 }) 256 }) 257 }) 258 })