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