github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/exec/get_step_test.go (about) 1 package exec_test 2 3 import ( 4 "context" 5 "errors" 6 7 "github.com/pf-qiu/concourse/v6/atc" 8 "github.com/pf-qiu/concourse/v6/atc/db" 9 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 10 "github.com/pf-qiu/concourse/v6/atc/exec" 11 "github.com/pf-qiu/concourse/v6/atc/exec/build" 12 "github.com/pf-qiu/concourse/v6/atc/exec/execfakes" 13 "github.com/pf-qiu/concourse/v6/atc/resource" 14 "github.com/pf-qiu/concourse/v6/atc/resource/resourcefakes" 15 "github.com/pf-qiu/concourse/v6/atc/runtime" 16 "github.com/pf-qiu/concourse/v6/atc/runtime/runtimefakes" 17 "github.com/pf-qiu/concourse/v6/atc/worker" 18 "github.com/pf-qiu/concourse/v6/atc/worker/workerfakes" 19 "github.com/pf-qiu/concourse/v6/tracing" 20 "github.com/pf-qiu/concourse/v6/vars" 21 "github.com/onsi/gomega/gbytes" 22 "go.opentelemetry.io/otel/api/trace" 23 "go.opentelemetry.io/otel/api/trace/tracetest" 24 25 . "github.com/onsi/ginkgo" 26 . "github.com/onsi/gomega" 27 ) 28 29 var _ = Describe("GetStep", func() { 30 var ( 31 ctx context.Context 32 cancel func() 33 stdoutBuf *gbytes.Buffer 34 stderrBuf *gbytes.Buffer 35 36 fakeClient *workerfakes.FakeClient 37 fakeWorker *workerfakes.FakeWorker 38 fakeStrategy *workerfakes.FakeContainerPlacementStrategy 39 40 fakeResourceFactory *resourcefakes.FakeResourceFactory 41 fakeResource *resourcefakes.FakeResource 42 fakeResourceCacheFactory *dbfakes.FakeResourceCacheFactory 43 fakeResourceCache *dbfakes.FakeUsedResourceCache 44 45 fakeDelegate *execfakes.FakeGetDelegate 46 fakeDelegateFactory *execfakes.FakeGetDelegateFactory 47 48 spanCtx context.Context 49 50 getPlan *atc.GetPlan 51 52 artifactRepository *build.Repository 53 fakeState *execfakes.FakeRunState 54 55 getStep exec.Step 56 getStepOk bool 57 getStepErr error 58 59 containerMetadata = db.ContainerMetadata{ 60 WorkingDirectory: resource.ResourcesDir("get"), 61 PipelineID: 4567, 62 Type: db.ContainerTypeGet, 63 StepName: "some-step", 64 } 65 66 stepMetadata = exec.StepMetadata{ 67 TeamID: 123, 68 TeamName: "some-team", 69 BuildID: 42, 70 BuildName: "some-build", 71 PipelineID: 4567, 72 PipelineName: "some-pipeline", 73 } 74 75 planID = "56" 76 ) 77 78 BeforeEach(func() { 79 ctx, cancel = context.WithCancel(context.Background()) 80 81 fakeClient = new(workerfakes.FakeClient) 82 fakeWorker = new(workerfakes.FakeWorker) 83 fakeWorker.NameReturns("some-worker") 84 fakeStrategy = new(workerfakes.FakeContainerPlacementStrategy) 85 86 fakeResourceFactory = new(resourcefakes.FakeResourceFactory) 87 fakeResource = new(resourcefakes.FakeResource) 88 fakeResourceCacheFactory = new(dbfakes.FakeResourceCacheFactory) 89 fakeResourceCache = new(dbfakes.FakeUsedResourceCache) 90 91 artifactRepository = build.NewRepository() 92 fakeState = new(execfakes.FakeRunState) 93 fakeState.ArtifactRepositoryReturns(artifactRepository) 94 fakeState.GetStub = vars.StaticVariables{ 95 "source-var": "super-secret-source", 96 "params-var": "super-secret-params", 97 }.Get 98 99 fakeDelegate = new(execfakes.FakeGetDelegate) 100 stdoutBuf = gbytes.NewBuffer() 101 stderrBuf = gbytes.NewBuffer() 102 fakeDelegate.StdoutReturns(stdoutBuf) 103 fakeDelegate.StderrReturns(stderrBuf) 104 spanCtx = context.Background() 105 fakeDelegate.StartSpanReturns(spanCtx, trace.NoopSpan{}) 106 107 fakeDelegateFactory = new(execfakes.FakeGetDelegateFactory) 108 fakeDelegateFactory.GetDelegateReturns(fakeDelegate) 109 110 getPlan = &atc.GetPlan{ 111 Name: "some-name", 112 Type: "some-base-type", 113 Source: atc.Source{"some": "((source-var))"}, 114 Params: atc.Params{"some": "((params-var))"}, 115 Version: &atc.Version{"some": "version"}, 116 VersionedResourceTypes: atc.VersionedResourceTypes{ 117 { 118 ResourceType: atc.ResourceType{ 119 Name: "some-custom-type", 120 Type: "another-custom-type", 121 Source: atc.Source{"some-custom": "((source-var))"}, 122 Params: atc.Params{"some-custom": "((params-var))"}, 123 }, 124 Version: atc.Version{"some-custom": "version"}, 125 }, 126 { 127 ResourceType: atc.ResourceType{ 128 Name: "another-custom-type", 129 Type: "registry-image", 130 Source: atc.Source{"another-custom": "((source-var))"}, 131 Privileged: true, 132 }, 133 Version: atc.Version{"another-custom": "version"}, 134 }, 135 }, 136 } 137 }) 138 139 AfterEach(func() { 140 cancel() 141 }) 142 143 JustBeforeEach(func() { 144 plan := atc.Plan{ 145 ID: atc.PlanID(planID), 146 Get: getPlan, 147 } 148 149 fakeResourceCacheFactory.FindOrCreateResourceCacheReturns(fakeResourceCache, nil) 150 fakeResourceFactory.NewResourceReturns(fakeResource) 151 152 getStep = exec.NewGetStep( 153 plan.ID, 154 *plan.Get, 155 stepMetadata, 156 containerMetadata, 157 fakeResourceFactory, 158 fakeResourceCacheFactory, 159 fakeStrategy, 160 fakeDelegateFactory, 161 fakeClient, 162 ) 163 164 getStepOk, getStepErr = getStep.Run(ctx, fakeState) 165 }) 166 167 It("propagates span context to the worker client", func() { 168 actualCtx, _, _, _, _, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 169 Expect(actualCtx).To(Equal(spanCtx)) 170 }) 171 172 It("constructs the resource cache correctly", func() { 173 _, typ, ver, source, params, types := fakeResourceCacheFactory.FindOrCreateResourceCacheArgsForCall(0) 174 Expect(typ).To(Equal("some-base-type")) 175 Expect(ver).To(Equal(atc.Version{"some": "version"})) 176 Expect(source).To(Equal(atc.Source{"some": "super-secret-source"})) 177 Expect(params).To(Equal(atc.Params{"some": "super-secret-params"})) 178 Expect(types).To(Equal(atc.VersionedResourceTypes{ 179 { 180 ResourceType: atc.ResourceType{ 181 Name: "some-custom-type", 182 Type: "another-custom-type", 183 Source: atc.Source{"some-custom": "super-secret-source"}, 184 185 // params don't need to be interpolated because it's used for 186 // fetching, not constructing the resource config 187 Params: atc.Params{"some-custom": "((params-var))"}, 188 }, 189 Version: atc.Version{"some-custom": "version"}, 190 }, 191 { 192 ResourceType: atc.ResourceType{ 193 Name: "another-custom-type", 194 Type: "registry-image", 195 Source: atc.Source{"another-custom": "super-secret-source"}, 196 Privileged: true, 197 }, 198 Version: atc.Version{"another-custom": "version"}, 199 }, 200 })) 201 }) 202 203 Context("when tracing is enabled", func() { 204 var buildSpan trace.Span 205 206 BeforeEach(func() { 207 tracing.ConfigureTraceProvider(tracetest.NewProvider()) 208 209 spanCtx, buildSpan = tracing.StartSpan(ctx, "build", nil) 210 fakeDelegate.StartSpanReturns(spanCtx, buildSpan) 211 }) 212 213 AfterEach(func() { 214 tracing.Configured = false 215 }) 216 217 It("propagates span context to the worker client", func() { 218 actualCtx, _, _, _, _, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 219 Expect(actualCtx).To(Equal(spanCtx)) 220 }) 221 222 It("populates the TRACEPARENT env var", func() { 223 _, _, _, actualContainerSpec, _, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 224 225 Expect(actualContainerSpec.Env).To(ContainElement(MatchRegexp(`TRACEPARENT=.+`))) 226 }) 227 }) 228 229 It("calls RunGetStep with the correct ContainerOwner", func() { 230 _, _, actualContainerOwner, _, _, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 231 Expect(actualContainerOwner).To(Equal(db.NewBuildStepContainerOwner( 232 stepMetadata.BuildID, 233 atc.PlanID(planID), 234 stepMetadata.TeamID, 235 ))) 236 }) 237 238 It("calls RunGetStep with the correct ContainerSpec", func() { 239 _, _, _, actualContainerSpec, _, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 240 Expect(actualContainerSpec).To(Equal( 241 worker.ContainerSpec{ 242 ImageSpec: worker.ImageSpec{ 243 ResourceType: "some-base-type", 244 }, 245 TeamID: stepMetadata.TeamID, 246 Env: stepMetadata.Env(), 247 }, 248 )) 249 }) 250 251 It("calls RunGetStep with the correct WorkerSpec", func() { 252 _, _, _, _, actualWorkerSpec, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 253 Expect(actualWorkerSpec).To(Equal( 254 worker.WorkerSpec{ 255 ResourceType: "some-base-type", 256 TeamID: stepMetadata.TeamID, 257 }, 258 )) 259 }) 260 261 Context("when the plan specifies tags", func() { 262 BeforeEach(func() { 263 getPlan.Tags = atc.Tags{"some", "tags"} 264 }) 265 266 It("sets them in the WorkerSpec", func() { 267 _, _, _, _, actualWorkerSpec, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 268 Expect(actualWorkerSpec.Tags).To(Equal([]string{"some", "tags"})) 269 }) 270 }) 271 272 Context("when using a custom resource type", func() { 273 var fakeImageSpec worker.ImageSpec 274 275 BeforeEach(func() { 276 getPlan.Type = "some-custom-type" 277 278 fakeImageSpec = worker.ImageSpec{ 279 ImageArtifact: new(runtimefakes.FakeArtifact), 280 } 281 282 fakeDelegate.FetchImageReturns(fakeImageSpec, nil) 283 }) 284 285 It("fetches the resource type image and uses it for the container", func() { 286 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 287 _, imageResource, types, privileged := fakeDelegate.FetchImageArgsForCall(0) 288 289 By("fetching the type image") 290 Expect(imageResource).To(Equal(atc.ImageResource{ 291 Name: "some-custom-type", 292 Type: "another-custom-type", 293 Source: atc.Source{"some-custom": "((source-var))"}, 294 Params: atc.Params{"some-custom": "((params-var))"}, 295 Version: atc.Version{"some-custom": "version"}, 296 })) 297 298 By("excluding the type from the FetchImage call") 299 Expect(types).To(Equal(atc.VersionedResourceTypes{ 300 { 301 ResourceType: atc.ResourceType{ 302 Name: "another-custom-type", 303 Type: "registry-image", 304 Source: atc.Source{"another-custom": "((source-var))"}, 305 Privileged: true, 306 }, 307 Version: atc.Version{"another-custom": "version"}, 308 }, 309 })) 310 311 By("not being privileged") 312 Expect(privileged).To(BeFalse()) 313 }) 314 315 Context("when the plan configures tags", func() { 316 BeforeEach(func() { 317 getPlan.Tags = atc.Tags{"plan", "tags"} 318 }) 319 320 It("fetches using the tags", func() { 321 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 322 _, imageResource, _, _ := fakeDelegate.FetchImageArgsForCall(0) 323 Expect(imageResource.Tags).To(Equal(atc.Tags{"plan", "tags"})) 324 }) 325 }) 326 327 Context("when the resource type configures tags", func() { 328 BeforeEach(func() { 329 taggedType, found := getPlan.VersionedResourceTypes.Lookup("some-custom-type") 330 Expect(found).To(BeTrue()) 331 332 taggedType.Tags = atc.Tags{"type", "tags"} 333 334 newTypes := getPlan.VersionedResourceTypes.Without("some-custom-type") 335 newTypes = append(newTypes, taggedType) 336 337 getPlan.VersionedResourceTypes = newTypes 338 }) 339 340 It("fetches using the type tags", func() { 341 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 342 _, imageResource, _, _ := fakeDelegate.FetchImageArgsForCall(0) 343 Expect(imageResource.Tags).To(Equal(atc.Tags{"type", "tags"})) 344 }) 345 346 Context("when the plan ALSO configures tags", func() { 347 BeforeEach(func() { 348 getPlan.Tags = atc.Tags{"plan", "tags"} 349 }) 350 351 It("fetches using only the type tags", func() { 352 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 353 _, imageResource, _, _ := fakeDelegate.FetchImageArgsForCall(0) 354 Expect(imageResource.Tags).To(Equal(atc.Tags{"type", "tags"})) 355 }) 356 }) 357 }) 358 359 It("sets the bottom-most type in the worker spec", func() { 360 _, _, _, _, actualWorkerSpec, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 361 Expect(actualWorkerSpec).To(Equal( 362 worker.WorkerSpec{ 363 TeamID: stepMetadata.TeamID, 364 ResourceType: "registry-image", 365 }, 366 )) 367 }) 368 369 It("calls RunGetStep with the correct ImageSpec", func() { 370 _, _, _, containerSpec, _, _, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 371 Expect(containerSpec.ImageSpec).To(Equal(fakeImageSpec)) 372 }) 373 374 Context("when the resource type is privileged", func() { 375 BeforeEach(func() { 376 getPlan.Type = "another-custom-type" 377 }) 378 379 It("fetches the image with privileged", func() { 380 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 381 _, _, _, privileged := fakeDelegate.FetchImageArgsForCall(0) 382 Expect(privileged).To(BeTrue()) 383 }) 384 }) 385 }) 386 387 It("calls RunGetStep with the correct ContainerPlacementStrategy", func() { 388 _, _, _, _, _, actualStrategy, _, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 389 Expect(actualStrategy).To(Equal(fakeStrategy)) 390 }) 391 392 It("calls RunGetStep with the correct ContainerMetadata", func() { 393 _, _, _, _, _, _, actualContainerMetadata, _, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 394 Expect(actualContainerMetadata).To(Equal( 395 db.ContainerMetadata{ 396 PipelineID: 4567, 397 Type: db.ContainerTypeGet, 398 StepName: "some-step", 399 WorkingDirectory: "/tmp/build/get", 400 }, 401 )) 402 }) 403 404 It("calls RunGetStep with the correct StartingEventDelegate", func() { 405 _, _, _, _, _, _, _, _, actualEventDelegate, _, _ := fakeClient.RunGetStepArgsForCall(0) 406 Expect(actualEventDelegate).To(Equal(fakeDelegate)) 407 }) 408 409 It("calls RunGetStep with the correct ProcessSpec", func() { 410 _, _, _, _, _, _, _, actualProcessSpec, _, _, _ := fakeClient.RunGetStepArgsForCall(0) 411 Expect(actualProcessSpec).To(Equal( 412 runtime.ProcessSpec{ 413 Path: "/opt/resource/in", 414 Args: []string{resource.ResourcesDir("get")}, 415 StdoutWriter: fakeDelegate.Stdout(), 416 StderrWriter: fakeDelegate.Stderr(), 417 }, 418 )) 419 }) 420 421 It("calls RunGetStep with the correct ResourceCache", func() { 422 _, _, _, _, _, _, _, _, _, actualResourceCache, _ := fakeClient.RunGetStepArgsForCall(0) 423 Expect(actualResourceCache).To(Equal(fakeResourceCache)) 424 }) 425 426 It("calls RunGetStep with the correct Resource", func() { 427 _, _, _, _, _, _, _, _, _, _, actualResource := fakeClient.RunGetStepArgsForCall(0) 428 Expect(actualResource).To(Equal(fakeResource)) 429 }) 430 431 Context("when Client.RunGetStep returns an err", func() { 432 var disaster error 433 BeforeEach(func() { 434 disaster = errors.New("disaster") 435 fakeClient.RunGetStepReturns(worker.GetResult{}, disaster) 436 }) 437 It("returns an err", func() { 438 Expect(fakeClient.RunGetStepCallCount()).To(Equal(1)) 439 Expect(getStepErr).To(HaveOccurred()) 440 Expect(getStepErr).To(Equal(disaster)) 441 }) 442 }) 443 444 Context("when Client.RunGetStep returns a Successful GetResult", func() { 445 BeforeEach(func() { 446 fakeClient.RunGetStepReturns( 447 worker.GetResult{ 448 ExitStatus: 0, 449 VersionResult: runtime.VersionResult{ 450 Version: atc.Version{"some": "version"}, 451 Metadata: []atc.MetadataField{{Name: "some", Value: "metadata"}}, 452 }, 453 GetArtifact: runtime.GetArtifact{VolumeHandle: "some-volume-handle"}, 454 }, nil) 455 }) 456 457 It("registers the resulting artifact in the RunState.ArtifactRepository", func() { 458 artifact, found := artifactRepository.ArtifactFor(build.ArtifactName(getPlan.Name)) 459 Expect(artifact).To(Equal(runtime.GetArtifact{VolumeHandle: "some-volume-handle"})) 460 Expect(found).To(BeTrue()) 461 }) 462 463 It("stores the resource cache as the step result", func() { 464 Expect(fakeState.StoreResultCallCount()).To(Equal(1)) 465 key, val := fakeState.StoreResultArgsForCall(0) 466 Expect(key).To(Equal(atc.PlanID(planID))) 467 Expect(val).To(Equal(fakeResourceCache)) 468 }) 469 470 It("marks the step as succeeded", func() { 471 Expect(getStepOk).To(BeTrue()) 472 }) 473 474 It("finishes the step via the delegate", func() { 475 Expect(fakeDelegate.FinishedCallCount()).To(Equal(1)) 476 _, status, info := fakeDelegate.FinishedArgsForCall(0) 477 Expect(status).To(Equal(exec.ExitStatus(0))) 478 Expect(info.Version).To(Equal(atc.Version{"some": "version"})) 479 Expect(info.Metadata).To(Equal([]atc.MetadataField{{Name: "some", Value: "metadata"}})) 480 }) 481 482 Context("when the plan has a resource", func() { 483 BeforeEach(func() { 484 getPlan.Resource = "some-pipeline-resource" 485 }) 486 487 It("saves a version for the resource", func() { 488 Expect(fakeDelegate.UpdateVersionCallCount()).To(Equal(1)) 489 _, actualPlan, actualVersionResult := fakeDelegate.UpdateVersionArgsForCall(0) 490 Expect(actualPlan.Resource).To(Equal("some-pipeline-resource")) 491 Expect(actualVersionResult.Version).To(Equal(atc.Version{"some": "version"})) 492 Expect(actualVersionResult.Metadata).To(Equal([]atc.MetadataField{{Name: "some", Value: "metadata"}})) 493 }) 494 }) 495 496 Context("when getting an anonymous resource", func() { 497 BeforeEach(func() { 498 getPlan.Resource = "" 499 }) 500 501 It("does not save the version", func() { 502 Expect(fakeDelegate.UpdateVersionCallCount()).To(Equal(0)) 503 }) 504 }) 505 506 It("does not return an err", func() { 507 Expect(getStepErr).ToNot(HaveOccurred()) 508 }) 509 }) 510 511 Context("when Client.RunGetStep returns a Failed GetResult", func() { 512 BeforeEach(func() { 513 fakeClient.RunGetStepReturns( 514 worker.GetResult{ 515 ExitStatus: 1, 516 VersionResult: runtime.VersionResult{}, 517 }, nil) 518 }) 519 520 It("does NOT mark the step as succeeded", func() { 521 Expect(getStepOk).To(BeFalse()) 522 }) 523 524 It("finishes the step via the delegate", func() { 525 Expect(fakeDelegate.FinishedCallCount()).To(Equal(1)) 526 _, actualExitStatus, actualVersionResult := fakeDelegate.FinishedArgsForCall(0) 527 Expect(actualExitStatus).ToNot(Equal(exec.ExitStatus(0))) 528 Expect(actualVersionResult).To(Equal(runtime.VersionResult{})) 529 }) 530 531 It("does not return an err", func() { 532 Expect(getStepErr).ToNot(HaveOccurred()) 533 }) 534 }) 535 })