github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/choose_task_worker_test.go (about) 1 package worker_test 2 3 import ( 4 "bytes" 5 "context" 6 "time" 7 8 "code.cloudfoundry.org/garden" 9 "github.com/pf-qiu/concourse/v6/atc/metric" 10 11 "code.cloudfoundry.org/lager/lagertest" 12 13 "github.com/pf-qiu/concourse/v6/atc" 14 "github.com/pf-qiu/concourse/v6/atc/compression/compressionfakes" 15 "github.com/pf-qiu/concourse/v6/atc/db" 16 "github.com/pf-qiu/concourse/v6/atc/db/lock/lockfakes" 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 22 . "github.com/onsi/ginkgo" 23 . "github.com/onsi/gomega" 24 ) 25 26 var _ = Describe("RunTaskStep", func() { 27 var ( 28 subject worker.Client 29 taskResult worker.TaskResult 30 err error 31 32 outputBuffer *bytes.Buffer 33 ctx context.Context 34 35 fakeWorker *workerfakes.FakeWorker 36 fakePool *workerfakes.FakePool 37 fakeTaskProcessSpec runtime.ProcessSpec 38 fakeLock *lockfakes.FakeLock 39 fakeProvider *workerfakes.FakeWorkerProvider 40 fakeCompression *compressionfakes.FakeCompression 41 fakeContainerOwner db.ContainerOwner 42 fakeContainerSpec worker.ContainerSpec 43 fakeWorkerSpec worker.WorkerSpec 44 fakeStrategy *workerfakes.FakeContainerPlacementStrategy 45 fakeMetadata db.ContainerMetadata 46 fakeEventDelegate *runtimefakes.FakeStartingEventDelegate 47 fakeLockFactory *lockfakes.FakeLockFactory 48 ) 49 50 Context("assign task when", func() { 51 BeforeEach(func() { 52 logger = lagertest.NewTestLogger("test") 53 outputBuffer = new(bytes.Buffer) 54 ctx, _ = context.WithCancel(context.Background()) 55 56 fakePool = new(workerfakes.FakePool) 57 fakeProvider = new(workerfakes.FakeWorkerProvider) 58 fakeCompression = new(compressionfakes.FakeCompression) 59 fakeContainerOwner = containerOwnerDummy() 60 fakeContainerSpec = workerContainerDummy() 61 fakeWorkerSpec = workerSpecDummy() 62 fakeStrategy = new(workerfakes.FakeContainerPlacementStrategy) 63 fakeMetadata = containerMetadataDummy() 64 fakeTaskProcessSpec = processSpecDummy(outputBuffer) 65 fakeEventDelegate = new(runtimefakes.FakeStartingEventDelegate) 66 fakeLockFactory = new(lockfakes.FakeLockFactory) 67 fakeWorker = fakeWorkerStub() 68 fakeLock = new(lockfakes.FakeLock) 69 70 fakeStrategy.ModifiesActiveTasksReturns(true) 71 fakeLockFactory.AcquireReturns(fakeLock, true, nil) 72 }) 73 74 JustBeforeEach(func() { 75 workerInterval := 250 * time.Millisecond 76 workerStatusInterval := 500 * time.Millisecond 77 78 subject = worker.NewClient( 79 fakePool, 80 fakeProvider, 81 fakeCompression, 82 workerInterval, 83 workerStatusInterval, 84 false, 85 15*time.Minute) 86 }) 87 88 Context("worker is available", func() { 89 BeforeEach(func() { 90 fakePool.ContainerInWorkerReturns(false, nil) 91 fakePool.FindOrChooseWorkerForContainerReturns(fakeWorker, nil) 92 }) 93 94 JustBeforeEach(func() { 95 taskResult, err = subject.RunTaskStep(ctx, 96 logger, 97 fakeContainerOwner, 98 fakeContainerSpec, 99 fakeWorkerSpec, 100 fakeStrategy, 101 fakeMetadata, 102 fakeTaskProcessSpec, 103 fakeEventDelegate, 104 fakeLockFactory) 105 }) 106 107 It("returns result of container process", func() { 108 Expect(err).To(BeNil()) 109 Expect(taskResult).To(Not(BeNil())) 110 Expect(taskResult.ExitStatus).To(BeZero()) 111 }) 112 113 It("releases lock acquired", func() { 114 Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount())) 115 }) 116 117 It("increases the active task count", func() { 118 Expect(fakeWorker.IncreaseActiveTasksCallCount()).To(Equal(1)) 119 Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount())) 120 }) 121 122 Context("when the container is already present on the worker", func() { 123 BeforeEach(func() { 124 fakePool.ContainerInWorkerReturns(true, nil) 125 }) 126 127 It("does not increase the active task count", func() { 128 Expect(fakeWorker.IncreaseActiveTasksCallCount()).To(Equal(0)) 129 Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount())) 130 }) 131 132 }) 133 }) 134 135 Context("waiting for worker to be available", func() { 136 BeforeEach(func() { 137 fakePool.FindOrChooseWorkerForContainerReturnsOnCall(0, nil, nil) 138 fakePool.FindOrChooseWorkerForContainerReturnsOnCall(1, nil, nil) 139 fakePool.FindOrChooseWorkerForContainerReturnsOnCall(2, nil, nil) 140 fakePool.FindOrChooseWorkerForContainerReturnsOnCall(3, fakeWorker, nil) 141 }) 142 143 JustBeforeEach(func() { 144 taskResult, err = subject.RunTaskStep(ctx, 145 logger, 146 fakeContainerOwner, 147 fakeContainerSpec, 148 fakeWorkerSpec, 149 fakeStrategy, 150 fakeMetadata, 151 fakeTaskProcessSpec, 152 fakeEventDelegate, 153 fakeLockFactory) 154 }) 155 156 It("returns result of container process", func() { 157 Expect(err).To(BeNil()) 158 Expect(taskResult).To(Not(BeNil())) 159 Expect(taskResult.ExitStatus).To(BeZero()) 160 }) 161 162 It("releases lock properly", func() { 163 Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount())) 164 }) 165 166 It("task waiting metrics is gauged", func() { 167 labels := metric.TasksWaitingLabels{ 168 TeamId: "123", 169 WorkerTags: "step_tags", 170 Platform: "some-platform", 171 } 172 173 Expect(metric.Metrics.TasksWaiting).To(HaveKey(labels)) 174 175 // Verify that when one task is waiting the gauge is increased... 176 Eventually(metric.Metrics.TasksWaiting[labels].Max(), 2*time.Second).Should(Equal(float64(1))) 177 // and then decreased. 178 Eventually(metric.Metrics.TasksWaiting[labels].Max(), 2*time.Second).Should(Equal(float64(0))) 179 }) 180 181 It("writes status to output writer", func() { 182 output := outputBuffer.String() 183 Expect(output).To(ContainSubstring("All workers are busy at the moment, please stand-by.\n")) 184 Expect(output).To(ContainSubstring("Found a free worker after waiting")) 185 }) 186 }) 187 }) 188 }) 189 190 func processSpecDummy(outputBuffer *bytes.Buffer) runtime.ProcessSpec { 191 return runtime.ProcessSpec{ 192 Path: "/some/path", 193 Args: []string{"some", "args"}, 194 Dir: "/some/dir", 195 StdoutWriter: outputBuffer, 196 StderrWriter: new(bytes.Buffer), 197 } 198 } 199 200 func containerMetadataDummy() db.ContainerMetadata { 201 return db.ContainerMetadata{ 202 WorkingDirectory: "some-artifact-root", 203 Type: db.ContainerTypeTask, 204 StepName: "some-step", 205 } 206 } 207 208 func workerContainerDummy() worker.ContainerSpec { 209 cpu := uint64(1024) 210 memory := uint64(1024) 211 212 return worker.ContainerSpec{ 213 TeamID: 123, 214 ImageSpec: worker.ImageSpec{ 215 ImageArtifactSource: new(workerfakes.FakeStreamableArtifactSource), 216 Privileged: false, 217 }, 218 Limits: worker.ContainerLimits{ 219 CPU: &cpu, 220 Memory: &memory, 221 }, 222 Dir: "some-artifact-root", 223 Env: []string{"SECURE=secret-task-param"}, 224 ArtifactByPath: map[string]runtime.Artifact{}, 225 Inputs: []worker.InputSource{}, 226 Outputs: worker.OutputPaths{}, 227 } 228 } 229 230 func workerSpecDummy() worker.WorkerSpec { 231 return worker.WorkerSpec{ 232 TeamID: 123, 233 Platform: "some-platform", 234 Tags: []string{"step", "tags"}, 235 } 236 } 237 238 func containerOwnerDummy() db.ContainerOwner { 239 return db.NewBuildStepContainerOwner( 240 1234, 241 atc.PlanID("42"), 242 123, 243 ) 244 } 245 246 func fakeWorkerStub() *workerfakes.FakeWorker { 247 fakeContainer := new(workerfakes.FakeContainer) 248 fakeContainer.PropertiesReturns(garden.Properties{"concourse:exit-status": "0"}, nil) 249 250 fakeWorker := new(workerfakes.FakeWorker) 251 fakeWorker.NameReturns("some-worker") 252 fakeWorker.SatisfiesReturns(true) 253 fakeWorker.FindOrCreateContainerReturns(fakeContainer, nil) 254 255 fakeWorker.IncreaseActiveTasksStub = func() error { 256 fakeWorker.ActiveTasksReturns(1, nil) 257 return nil 258 } 259 fakeWorker.DecreaseActiveTasksStub = func() error { 260 fakeWorker.ActiveTasksReturns(0, nil) 261 return nil 262 } 263 return fakeWorker 264 }