1package exec
2
3import (
4	"context"
5	"fmt"
6	"io"
7
8	"code.cloudfoundry.org/lager"
9	"code.cloudfoundry.org/lager/lagerctx"
10	"github.com/concourse/concourse/atc"
11	"github.com/concourse/concourse/atc/creds"
12	"github.com/concourse/concourse/atc/db"
13	"github.com/concourse/concourse/atc/exec/build"
14	"github.com/concourse/concourse/atc/resource"
15	"github.com/concourse/concourse/atc/runtime"
16	"github.com/concourse/concourse/atc/worker"
17	"github.com/concourse/concourse/tracing"
18	"github.com/concourse/concourse/vars"
19)
20
21type ErrPipelineNotFound struct {
22	PipelineName string
23}
24
25func (e ErrPipelineNotFound) Error() string {
26	return fmt.Sprintf("pipeline '%s' not found", e.PipelineName)
27}
28
29type ErrResourceNotFound struct {
30	ResourceName string
31}
32
33func (e ErrResourceNotFound) Error() string {
34	return fmt.Sprintf("resource '%s' not found", e.ResourceName)
35}
36
37//go:generate counterfeiter . GetDelegate
38
39type GetDelegate interface {
40	ImageVersionDetermined(db.UsedResourceCache) error
41	RedactImageSource(source atc.Source) (atc.Source, error)
42
43	Stdout() io.Writer
44	Stderr() io.Writer
45
46	Variables() *vars.BuildVariables
47
48	Initializing(lager.Logger)
49	Starting(lager.Logger)
50	Finished(lager.Logger, ExitStatus, runtime.VersionResult)
51	SelectedWorker(lager.Logger, string)
52	Errored(lager.Logger, string)
53
54	UpdateVersion(lager.Logger, atc.GetPlan, runtime.VersionResult)
55}
56
57// GetStep will fetch a version of a resource on a worker that supports the
58// resource type.
59type GetStep struct {
60	planID               atc.PlanID
61	plan                 atc.GetPlan
62	metadata             StepMetadata
63	containerMetadata    db.ContainerMetadata
64	resourceFactory      resource.ResourceFactory
65	resourceCacheFactory db.ResourceCacheFactory
66	strategy             worker.ContainerPlacementStrategy
67	workerClient         worker.Client
68	delegate             GetDelegate
69	succeeded            bool
70}
71
72func NewGetStep(
73	planID atc.PlanID,
74	plan atc.GetPlan,
75	metadata StepMetadata,
76	containerMetadata db.ContainerMetadata,
77	resourceFactory resource.ResourceFactory,
78	resourceCacheFactory db.ResourceCacheFactory,
79	strategy worker.ContainerPlacementStrategy,
80	delegate GetDelegate,
81	client worker.Client,
82) Step {
83	return &GetStep{
84		planID:               planID,
85		plan:                 plan,
86		metadata:             metadata,
87		containerMetadata:    containerMetadata,
88		resourceFactory:      resourceFactory,
89		resourceCacheFactory: resourceCacheFactory,
90		strategy:             strategy,
91		delegate:             delegate,
92		workerClient:         client,
93	}
94}
95func (step *GetStep) Run(ctx context.Context, state RunState) error {
96	ctx, span := tracing.StartSpan(ctx, "get", tracing.Attrs{
97		"team":     step.metadata.TeamName,
98		"pipeline": step.metadata.PipelineName,
99		"job":      step.metadata.JobName,
100		"build":    step.metadata.BuildName,
101		"resource": step.plan.Resource,
102		"name":     step.plan.Name,
103	})
104
105	err := step.run(ctx, state)
106	tracing.End(span, err)
107
108	return err
109}
110
111func (step *GetStep) run(ctx context.Context, state RunState) error {
112	logger := lagerctx.FromContext(ctx)
113	logger = logger.Session("get-step", lager.Data{
114		"step-name": step.plan.Name,
115		"job-id":    step.metadata.JobID,
116	})
117
118	step.delegate.Initializing(logger)
119
120	variables := step.delegate.Variables()
121
122	source, err := creds.NewSource(variables, step.plan.Source).Evaluate()
123	if err != nil {
124		return err
125	}
126
127	params, err := creds.NewParams(variables, step.plan.Params).Evaluate()
128	if err != nil {
129		return err
130	}
131
132	resourceTypes, err := creds.NewVersionedResourceTypes(variables, step.plan.VersionedResourceTypes).Evaluate()
133	if err != nil {
134		return err
135	}
136
137	version, err := NewVersionSourceFromPlan(&step.plan).Version(state)
138	if err != nil {
139		return err
140	}
141
142	containerSpec := worker.ContainerSpec{
143		ImageSpec: worker.ImageSpec{
144			ResourceType: step.plan.Type,
145		},
146		TeamID: step.metadata.TeamID,
147		Env:    step.metadata.Env(),
148	}
149	tracing.Inject(ctx, &containerSpec)
150
151	workerSpec := worker.WorkerSpec{
152		ResourceType:  step.plan.Type,
153		Tags:          step.plan.Tags,
154		TeamID:        step.metadata.TeamID,
155		ResourceTypes: resourceTypes,
156	}
157
158	imageSpec := worker.ImageFetcherSpec{
159		ResourceTypes: resourceTypes,
160		Delegate:      step.delegate,
161	}
162
163	resourceCache, err := step.resourceCacheFactory.FindOrCreateResourceCache(
164		db.ForBuild(step.metadata.BuildID),
165		step.plan.Type,
166		version,
167		source,
168		params,
169		resourceTypes,
170	)
171	if err != nil {
172		logger.Error("failed-to-create-resource-cache", err)
173		return err
174	}
175
176	processSpec := runtime.ProcessSpec{
177		Path:         "/opt/resource/in",
178		Args:         []string{resource.ResourcesDir("get")},
179		StdoutWriter: step.delegate.Stdout(),
180		StderrWriter: step.delegate.Stderr(),
181	}
182
183	resourceToGet := step.resourceFactory.NewResource(
184		source,
185		params,
186		version,
187	)
188
189	containerOwner := db.NewBuildStepContainerOwner(step.metadata.BuildID, step.planID, step.metadata.TeamID)
190
191	getResult, err := step.workerClient.RunGetStep(
192		ctx,
193		logger,
194		containerOwner,
195		containerSpec,
196		workerSpec,
197		step.strategy,
198		step.containerMetadata,
199		imageSpec,
200		processSpec,
201		step.delegate,
202		resourceCache,
203		resourceToGet,
204	)
205	if err != nil {
206		return err
207	}
208
209	if getResult.ExitStatus == 0 {
210		state.ArtifactRepository().RegisterArtifact(
211			build.ArtifactName(step.plan.Name),
212			getResult.GetArtifact,
213		)
214
215		if step.plan.Resource != "" {
216			step.delegate.UpdateVersion(logger, step.plan, getResult.VersionResult)
217		}
218
219		step.succeeded = true
220	}
221
222	step.delegate.Finished(
223		logger,
224		ExitStatus(getResult.ExitStatus),
225		getResult.VersionResult,
226	)
227
228	return nil
229}
230
231// Succeeded returns true if the resource was successfully fetched.
232func (step *GetStep) Succeeded() bool {
233	return step.succeeded
234}
235