1package worker_test
2
3import (
4	"context"
5	"errors"
6	"time"
7
8	"github.com/concourse/concourse/atc/db/dbfakes"
9	"github.com/concourse/concourse/atc/resource"
10	"github.com/concourse/concourse/atc/runtime"
11	"github.com/concourse/concourse/atc/worker"
12
13	"code.cloudfoundry.org/clock/fakeclock"
14	"code.cloudfoundry.org/lager"
15	"code.cloudfoundry.org/lager/lagertest"
16	"github.com/concourse/concourse/atc"
17	"github.com/concourse/concourse/atc/db"
18	"github.com/concourse/concourse/atc/db/lock"
19	"github.com/concourse/concourse/atc/db/lock/lockfakes"
20	"github.com/concourse/concourse/atc/resource/resourcefakes"
21	"github.com/concourse/concourse/atc/worker/workerfakes"
22	. "github.com/onsi/ginkgo"
23	. "github.com/onsi/gomega"
24)
25
26var _ = Describe("Fetcher", func() {
27	var (
28		fakeClock             *fakeclock.FakeClock
29		fakeLockFactory       *lockfakes.FakeLockFactory
30		fetcher               worker.Fetcher
31		ctx                   context.Context
32		cancel                func()
33		fakeBuildStepDelegate *workerfakes.FakeImageFetchingDelegate
34
35		fakeWorker             *workerfakes.FakeWorker
36		fakeVolume             *workerfakes.FakeVolume
37		fakeFetchSourceFactory *workerfakes.FakeFetchSourceFactory
38		fakeResource           *resourcefakes.FakeResource
39		fakeUsedResourceCache  *dbfakes.FakeUsedResourceCache
40
41		getResult worker.GetResult
42		fetchErr  error
43		teamID    = 123
44
45		volume worker.Volume
46	)
47
48	BeforeEach(func() {
49		fakeClock = fakeclock.NewFakeClock(time.Unix(0, 123))
50		fakeLockFactory = new(lockfakes.FakeLockFactory)
51		fakeFetchSourceFactory = new(workerfakes.FakeFetchSourceFactory)
52
53		fakeWorker = new(workerfakes.FakeWorker)
54		fakeWorker.NameReturns("some-worker")
55
56		fakeVolume = new(workerfakes.FakeVolume)
57		fakeVolume.HandleReturns("some-handle")
58		fakeResource = new(resourcefakes.FakeResource)
59		fakeUsedResourceCache = new(dbfakes.FakeUsedResourceCache)
60
61		fetcher = worker.NewFetcher(
62			fakeClock,
63			fakeLockFactory,
64			fakeFetchSourceFactory,
65		)
66
67		ctx, cancel = context.WithCancel(context.Background())
68
69		fakeBuildStepDelegate = new(workerfakes.FakeImageFetchingDelegate)
70	})
71
72	JustBeforeEach(func() {
73		getResult, volume, fetchErr = fetcher.Fetch(
74			ctx,
75			lagertest.NewTestLogger("test"),
76			db.ContainerMetadata{},
77			fakeWorker,
78			worker.ContainerSpec{
79				TeamID: teamID,
80			},
81			runtime.ProcessSpec{
82				Args: []string{resource.ResourcesDir("get")},
83			},
84			fakeResource,
85			db.NewBuildStepContainerOwner(0, "some-plan-id", 0),
86			worker.ImageFetcherSpec{
87				atc.VersionedResourceTypes{},
88				fakeBuildStepDelegate,
89			},
90			fakeUsedResourceCache,
91			"fake-lock-name",
92		)
93	})
94
95	Context("when getting source", func() {
96		var fakeFetchSource *workerfakes.FakeFetchSource
97
98		BeforeEach(func() {
99			fakeFetchSource = new(workerfakes.FakeFetchSource)
100			fakeFetchSourceFactory.NewFetchSourceReturns(fakeFetchSource)
101
102			fakeFetchSource.FindReturns(worker.GetResult{}, fakeVolume, false, nil)
103		})
104
105		Describe("failing to get a lock", func() {
106			Context("when did not get a lock", func() {
107				BeforeEach(func() {
108					fakeLock := new(lockfakes.FakeLock)
109					callCount := 0
110					fakeLockFactory.AcquireStub = func(lager.Logger, lock.LockID) (lock.Lock, bool, error) {
111						callCount++
112						fakeClock.Increment(worker.GetResourceLockInterval)
113						if callCount == 1 {
114							return nil, false, nil
115						}
116						return fakeLock, true, nil
117					}
118				})
119
120				It("retries until it gets the lock", func() {
121					Expect(fakeLockFactory.AcquireCallCount()).To(Equal(2))
122				})
123
124				It("creates fetch source after lock is acquired", func() {
125					Expect(fakeFetchSource.CreateCallCount()).To(Equal(1))
126				})
127			})
128
129			Context("when acquiring lock returns error", func() {
130				BeforeEach(func() {
131					fakeLock := new(lockfakes.FakeLock)
132					callCount := 0
133					fakeLockFactory.AcquireStub = func(lager.Logger, lock.LockID) (lock.Lock, bool, error) {
134						callCount++
135						fakeClock.Increment(worker.GetResourceLockInterval)
136						if callCount == 1 {
137							return nil, false, errors.New("disaster")
138						}
139						return fakeLock, true, nil
140					}
141				})
142
143				It("retries until it gets the lock", func() {
144					Expect(fakeLockFactory.AcquireCallCount()).To(Equal(2))
145				})
146
147				It("creates fetch source after lock is acquired", func() {
148					Expect(fakeFetchSource.CreateCallCount()).To(Equal(1))
149				})
150			})
151		})
152
153		Context("when getting lock succeeds", func() {
154			var fakeLock *lockfakes.FakeLock
155
156			BeforeEach(func() {
157				fakeLock = new(lockfakes.FakeLock)
158				fakeLockFactory.AcquireReturns(fakeLock, true, nil)
159				fakeFetchSource.CreateReturns(worker.GetResult{}, fakeVolume, nil)
160			})
161
162			It("acquires a lock with source lock name", func() {
163				Expect(fakeLockFactory.AcquireCallCount()).To(Equal(1))
164				_, lockID := fakeLockFactory.AcquireArgsForCall(0)
165				Expect(lockID).To(Equal(lock.NewTaskLockID("fake-lock-name")))
166			})
167
168			It("releases the lock", func() {
169				Expect(fakeLock.ReleaseCallCount()).To(Equal(1))
170			})
171
172			It("creates source", func() {
173				Expect(fakeFetchSource.CreateCallCount()).To(Equal(1))
174			})
175
176			It("returns the result and volume", func() {
177				Expect(getResult).To(Equal(worker.GetResult{}))
178				Expect(volume).ToNot(BeNil())
179			})
180		})
181
182		Context("when finding fails", func() {
183			var disaster error
184
185			BeforeEach(func() {
186				disaster = errors.New("fail")
187				fakeFetchSource.FindReturns(worker.GetResult{}, nil, false, disaster)
188			})
189
190			It("returns an error", func() {
191				Expect(fetchErr).To(HaveOccurred())
192				Expect(fetchErr).To(Equal(disaster))
193			})
194		})
195
196		Context("when cancelled", func() {
197			BeforeEach(func() {
198				cancel()
199			})
200
201			It("returns the context err", func() {
202				Expect(fetchErr).To(Equal(context.Canceled))
203			})
204		})
205	})
206})
207