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  })