github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/artifact_source_test.go (about) 1 package worker_test 2 3 import ( 4 "archive/tar" 5 "compress/gzip" 6 "context" 7 "errors" 8 "io" 9 "io/ioutil" 10 "time" 11 12 "code.cloudfoundry.org/lager" 13 "github.com/concourse/baggageclaim" 14 "github.com/pf-qiu/concourse/v6/atc/compression" 15 "github.com/pf-qiu/concourse/v6/atc/db" 16 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 17 "github.com/pf-qiu/concourse/v6/atc/runtime" 18 "github.com/pf-qiu/concourse/v6/atc/runtime/runtimefakes" 19 "github.com/pf-qiu/concourse/v6/atc/worker" 20 "github.com/pf-qiu/concourse/v6/atc/worker/workerfakes" 21 "github.com/onsi/gomega/gbytes" 22 23 . "github.com/onsi/ginkgo" 24 . "github.com/onsi/gomega" 25 ) 26 27 var _ = Describe("StreamableArtifactSource", func() { 28 var ( 29 fakeDestination *workerfakes.FakeArtifactDestination 30 fakeVolume *workerfakes.FakeVolume 31 fakeArtifact *runtimefakes.FakeArtifact 32 33 enabledP2pStreaming bool 34 p2pStreamingTimeout time.Duration 35 36 artifactSource worker.StreamableArtifactSource 37 comp compression.Compression 38 testLogger lager.Logger 39 40 disaster error 41 ) 42 43 BeforeEach(func() { 44 fakeArtifact = new(runtimefakes.FakeArtifact) 45 fakeVolume = new(workerfakes.FakeVolume) 46 fakeDestination = new(workerfakes.FakeArtifactDestination) 47 comp = compression.NewGzipCompression() 48 49 enabledP2pStreaming = false 50 p2pStreamingTimeout = 15 * time.Minute 51 52 testLogger = lager.NewLogger("test") 53 disaster = errors.New("disaster") 54 }) 55 56 JustBeforeEach(func() { 57 artifactSource = worker.NewStreamableArtifactSource(fakeArtifact, fakeVolume, comp, enabledP2pStreaming, p2pStreamingTimeout) 58 }) 59 60 Context("StreamTo", func() { 61 var streamToErr error 62 63 JustBeforeEach(func() { 64 streamToErr = artifactSource.StreamTo(context.TODO(), fakeDestination) 65 }) 66 67 Context("via atc", func() { 68 var outStream *gbytes.Buffer 69 70 BeforeEach(func() { 71 outStream = gbytes.NewBuffer() 72 fakeVolume.StreamOutReturns(outStream, nil) 73 }) 74 75 Context("when ArtifactSource can successfully stream to ArtifactDestination", func() { 76 77 It("calls StreamOut and StreamIn with the correct params", func() { 78 Expect(fakeVolume.StreamOutCallCount()).To(Equal(1)) 79 80 _, actualPath, encoding := fakeVolume.StreamOutArgsForCall(0) 81 Expect(actualPath).To(Equal(".")) 82 Expect(encoding).To(Equal(baggageclaim.GzipEncoding)) 83 84 _, actualPath, encoding, actualStreamedOutBits := fakeDestination.StreamInArgsForCall(0) 85 Expect(actualPath).To(Equal(".")) 86 Expect(actualStreamedOutBits).To(Equal(outStream)) 87 Expect(encoding).To(Equal(baggageclaim.GzipEncoding)) 88 }) 89 90 It("does not return an err", func() { 91 Expect(streamToErr).ToNot(HaveOccurred()) 92 }) 93 }) 94 95 Context("when streaming out of source fails ", func() { 96 BeforeEach(func() { 97 fakeVolume.StreamOutReturns(nil, disaster) 98 }) 99 It("returns the err", func() { 100 Expect(streamToErr).To(Equal(disaster)) 101 }) 102 }) 103 104 Context("when streaming in to destination fails ", func() { 105 BeforeEach(func() { 106 fakeDestination.StreamInReturns(disaster) 107 }) 108 It("returns the err", func() { 109 Expect(streamToErr).To(Equal(disaster)) 110 }) 111 It("closes the streamOut io.reader", func() { 112 Expect(outStream.Closed()).To(BeTrue()) 113 }) 114 }) 115 }) 116 117 Context("p2p", func() { 118 BeforeEach(func() { 119 enabledP2pStreaming = true 120 }) 121 122 Context("GetStreamInP2pUrl fails", func() { 123 BeforeEach(func() { 124 fakeDestination.GetStreamInP2pUrlReturns("", disaster) 125 }) 126 127 It("does return an err", func() { 128 Expect(streamToErr).To(HaveOccurred()) 129 Expect(streamToErr).To(Equal(disaster)) 130 }) 131 132 It("should not call StreamP2pOut", func() { 133 Expect(fakeDestination.GetStreamInP2pUrlCallCount()).To(Equal(1)) 134 135 _, actualPath := fakeDestination.GetStreamInP2pUrlArgsForCall(0) 136 Expect(actualPath).To(Equal(".")) 137 138 Expect(fakeVolume.StreamP2pOutCallCount()).To(Equal(0)) 139 }) 140 }) 141 142 Context("GetStreamInP2pUrl succeeds", func() { 143 BeforeEach(func() { 144 fakeDestination.GetStreamInP2pUrlReturns("some-url", nil) 145 }) 146 147 It("calls GetStreamInP2pUrl and StreamP2pOut with the correct params", func() { 148 Expect(fakeDestination.GetStreamInP2pUrlCallCount()).To(Equal(1)) 149 150 _, actualPath := fakeDestination.GetStreamInP2pUrlArgsForCall(0) 151 Expect(actualPath).To(Equal(".")) 152 153 Expect(fakeVolume.StreamP2pOutCallCount()).To(Equal(1)) 154 155 _, actualPath, actualStreamUrl, actualEncoding := fakeVolume.StreamP2pOutArgsForCall(0) 156 Expect(actualPath).To(Equal(".")) 157 Expect(actualStreamUrl).To(Equal("some-url")) 158 Expect(actualEncoding).To(Equal(baggageclaim.GzipEncoding)) 159 }) 160 161 Context("StreamP2pOut fails", func() { 162 BeforeEach(func() { 163 fakeVolume.StreamP2pOutReturns(disaster) 164 }) 165 166 It("does return an err", func() { 167 Expect(streamToErr).To(HaveOccurred()) 168 Expect(streamToErr).To(Equal(disaster)) 169 }) 170 }) 171 172 Context("StreamP2pOut succeeds", func() { 173 It("does not return an err", func() { 174 Expect(streamToErr).ToNot(HaveOccurred()) 175 }) 176 }) 177 }) 178 }) 179 }) 180 181 Context("StreamFile", func() { 182 var ( 183 streamFileErr error 184 streamFileReader io.ReadCloser 185 ) 186 187 JustBeforeEach(func() { 188 streamFileReader, streamFileErr = artifactSource.StreamFile(context.TODO(), "some-file") 189 }) 190 191 Context("when ArtifactSource can successfully stream a file out", func() { 192 var ( 193 fileContent = "file-content" 194 tgzBuffer *gbytes.Buffer 195 ) 196 197 BeforeEach(func() { 198 tgzBuffer = gbytes.NewBuffer() 199 fakeVolume.StreamOutReturns(tgzBuffer, nil) 200 gzipWriter := gzip.NewWriter(tgzBuffer) 201 defer gzipWriter.Close() 202 203 tarWriter := tar.NewWriter(gzipWriter) 204 defer tarWriter.Close() 205 206 err := tarWriter.WriteHeader(&tar.Header{ 207 Name: "some-file", 208 Mode: 0644, 209 Size: int64(len(fileContent)), 210 }) 211 Expect(err).NotTo(HaveOccurred()) 212 213 _, err = tarWriter.Write([]byte(fileContent)) 214 Expect(err).NotTo(HaveOccurred()) 215 }) 216 217 It("streams out the given path", func() { 218 Expect(streamFileErr).NotTo(HaveOccurred()) 219 220 Expect(ioutil.ReadAll(streamFileReader)).To(Equal([]byte(fileContent))) 221 _, path, encoding := fakeVolume.StreamOutArgsForCall(0) 222 Expect(path).To(Equal("some-file")) 223 Expect(encoding).To(Equal(baggageclaim.GzipEncoding)) 224 }) 225 226 It("closes the stream from the volume", func() { 227 Expect(streamFileErr).NotTo(HaveOccurred()) 228 229 Expect(tgzBuffer.Closed()).To(BeFalse()) 230 231 err := streamFileReader.Close() 232 Expect(err).NotTo(HaveOccurred()) 233 234 Expect(tgzBuffer.Closed()).To(BeTrue()) 235 }) 236 }) 237 238 Context("when ArtifactSource fails to stream a file out", func() { 239 240 Context("when streaming out of source fails ", func() { 241 BeforeEach(func() { 242 fakeVolume.StreamOutReturns(nil, disaster) 243 }) 244 245 It("returns the error", func() { 246 Expect(streamFileErr).To(Equal(disaster)) 247 }) 248 }) 249 }) 250 }) 251 252 Context("ExistsOn", func() { 253 var ( 254 fakeWorker *workerfakes.FakeWorker 255 actualVolume worker.Volume 256 actualFound bool 257 actualErr error 258 ) 259 260 BeforeEach(func() { 261 fakeWorker = new(workerfakes.FakeWorker) 262 fakeWorker.LookupVolumeReturns(fakeVolume, true, disaster) 263 fakeArtifact.IDReturns("some-id") 264 }) 265 266 JustBeforeEach(func() { 267 actualVolume, actualFound, actualErr = artifactSource.ExistsOn(testLogger, fakeWorker) 268 }) 269 270 Context("when the volume belongs to the worker passed in", func() { 271 BeforeEach(func() { 272 fakeWorker.NameReturns("some-foo-worker-name") 273 fakeVolume.WorkerNameReturns("some-foo-worker-name") 274 }) 275 It("returns the volume", func() { 276 Expect(actualFound).To(BeTrue()) 277 Expect(actualVolume).To(Equal(fakeVolume)) 278 Expect(actualErr).ToNot(HaveOccurred()) 279 }) 280 }) 281 Context("when the volume doesn't belong to the worker passed in", func() { 282 BeforeEach(func() { 283 fakeWorker.NameReturns("some-foo-worker-name") 284 fakeVolume.WorkerNameReturns("some-other-foo-worker-name") 285 }) 286 Context("when the volume has a resource cache", func() { 287 var fakeResourceCache db.UsedResourceCache 288 289 BeforeEach(func() { 290 fakeResourceCache = new(dbfakes.FakeUsedResourceCache) 291 fakeWorker.FindResourceCacheForVolumeReturns(fakeResourceCache, true, nil) 292 293 }) 294 295 It("queries the worker's local volume for the resourceCache", func() { 296 _, actualResourceCache := fakeWorker.FindVolumeForResourceCacheArgsForCall(0) 297 Expect(actualResourceCache).To(Equal(fakeResourceCache)) 298 }) 299 300 Context("when the resource cache has a local volume on the worker", func() { 301 var localFakeVolume worker.Volume 302 BeforeEach(func() { 303 localFakeVolume = new(workerfakes.FakeVolume) 304 fakeWorker.FindVolumeForResourceCacheReturns(localFakeVolume, true, nil) 305 }) 306 It("returns worker's local volume for the resourceCache", func() { 307 Expect(actualFound).To(BeTrue()) 308 Expect(actualVolume).To(Equal(localFakeVolume)) 309 Expect(actualErr).ToNot(HaveOccurred()) 310 }) 311 }) 312 313 }) 314 315 Context("when the volume does NOT have a resource cache", func() { 316 BeforeEach(func() { 317 fakeWorker.FindResourceCacheForVolumeReturns(nil, false, nil) 318 319 }) 320 It("returns not found", func() { 321 Expect(actualFound).To(BeFalse()) 322 Expect(actualErr).ToNot(HaveOccurred()) 323 }) 324 }) 325 }) 326 }) 327 328 }) 329 330 var _ = Describe("CacheArtifactSource", func() { 331 Context("ExistsOn", func() { 332 var ( 333 fakeWorker *workerfakes.FakeWorker 334 actualVolume worker.Volume 335 actualFound bool 336 actualErr error 337 disaster error 338 fakeVolume *workerfakes.FakeVolume 339 cacheArtifactSource worker.ArtifactSource 340 cacheArtifact runtime.CacheArtifact 341 testLogger lager.Logger 342 ) 343 344 BeforeEach(func() { 345 fakeWorker = new(workerfakes.FakeWorker) 346 fakeVolume = new(workerfakes.FakeVolume) 347 disaster = errors.New("disaster") 348 349 fakeWorker.FindVolumeForTaskCacheReturns(fakeVolume, true, disaster) 350 351 testLogger = lager.NewLogger("cacheArtifactSource") 352 353 cacheArtifact = runtime.CacheArtifact{ 354 TeamID: 5, 355 JobID: 18, 356 StepName: "some-step-name", 357 Path: "some/path/foo", 358 } 359 cacheArtifactSource = worker.NewCacheArtifactSource(cacheArtifact) 360 361 }) 362 363 JustBeforeEach(func() { 364 actualVolume, actualFound, actualErr = cacheArtifactSource.ExistsOn(testLogger, fakeWorker) 365 }) 366 367 It("calls Worker.FindVolumeForTaskCache with the the correct params", func() { 368 _, actualTeamID, actualJobID, actualStepName, actualPath := fakeWorker.FindVolumeForTaskCacheArgsForCall(0) 369 Expect(actualTeamID).To(Equal(cacheArtifact.TeamID)) 370 Expect(actualJobID).To(Equal(cacheArtifact.JobID)) 371 Expect(actualStepName).To(Equal(cacheArtifact.StepName)) 372 Expect(actualPath).To(Equal(cacheArtifact.Path)) 373 }) 374 375 It("returns the response of Worker.FindVolumeForTaskCache", func() { 376 Expect(actualVolume).To(Equal(fakeVolume)) 377 Expect(actualFound).To(BeTrue()) 378 Expect(actualErr).To(Equal(disaster)) 379 380 }) 381 }) 382 })