github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/exec/put_step.go (about) 1 package exec 2 3 import ( 4 "context" 5 "io" 6 7 "code.cloudfoundry.org/lager" 8 "code.cloudfoundry.org/lager/lagerctx" 9 "github.com/pf-qiu/concourse/v6/atc" 10 "github.com/pf-qiu/concourse/v6/atc/creds" 11 "github.com/pf-qiu/concourse/v6/atc/db" 12 "github.com/pf-qiu/concourse/v6/atc/resource" 13 "github.com/pf-qiu/concourse/v6/atc/runtime" 14 "github.com/pf-qiu/concourse/v6/atc/worker" 15 "github.com/pf-qiu/concourse/v6/tracing" 16 "go.opentelemetry.io/otel/api/trace" 17 ) 18 19 //go:generate counterfeiter . PutDelegateFactory 20 21 type PutDelegateFactory interface { 22 PutDelegate(state RunState) PutDelegate 23 } 24 25 //go:generate counterfeiter . PutDelegate 26 27 type PutDelegate interface { 28 StartSpan(context.Context, string, tracing.Attrs) (context.Context, trace.Span) 29 30 FetchImage(context.Context, atc.ImageResource, atc.VersionedResourceTypes, bool) (worker.ImageSpec, error) 31 32 Stdout() io.Writer 33 Stderr() io.Writer 34 35 Initializing(lager.Logger) 36 Starting(lager.Logger) 37 Finished(lager.Logger, ExitStatus, runtime.VersionResult) 38 SelectedWorker(lager.Logger, string) 39 Errored(lager.Logger, string) 40 41 SaveOutput(lager.Logger, atc.PutPlan, atc.Source, atc.VersionedResourceTypes, runtime.VersionResult) 42 } 43 44 // PutStep produces a resource version using preconfigured params and any data 45 // available in the worker.ArtifactRepository. 46 type PutStep struct { 47 planID atc.PlanID 48 plan atc.PutPlan 49 metadata StepMetadata 50 containerMetadata db.ContainerMetadata 51 resourceFactory resource.ResourceFactory 52 resourceConfigFactory db.ResourceConfigFactory 53 strategy worker.ContainerPlacementStrategy 54 workerClient worker.Client 55 delegateFactory PutDelegateFactory 56 succeeded bool 57 } 58 59 func NewPutStep( 60 planID atc.PlanID, 61 plan atc.PutPlan, 62 metadata StepMetadata, 63 containerMetadata db.ContainerMetadata, 64 resourceFactory resource.ResourceFactory, 65 resourceConfigFactory db.ResourceConfigFactory, 66 strategy worker.ContainerPlacementStrategy, 67 workerClient worker.Client, 68 delegateFactory PutDelegateFactory, 69 ) Step { 70 return &PutStep{ 71 planID: planID, 72 plan: plan, 73 metadata: metadata, 74 containerMetadata: containerMetadata, 75 resourceFactory: resourceFactory, 76 resourceConfigFactory: resourceConfigFactory, 77 workerClient: workerClient, 78 strategy: strategy, 79 delegateFactory: delegateFactory, 80 } 81 } 82 83 // Run chooses a worker that supports the step's resource type and creates a 84 // container. 85 // 86 // All worker.ArtifactSources present in the worker.ArtifactRepository are then brought into 87 // the container, using volumes if possible, and streaming content over if not. 88 // 89 // The resource's put script is then invoked. If the context is canceled, the 90 // script will be interrupted. 91 func (step *PutStep) Run(ctx context.Context, state RunState) (bool, error) { 92 delegate := step.delegateFactory.PutDelegate(state) 93 ctx, span := delegate.StartSpan(ctx, "put", tracing.Attrs{ 94 "name": step.plan.Name, 95 "resource": step.plan.Resource, 96 }) 97 98 ok, err := step.run(ctx, state, delegate) 99 tracing.End(span, err) 100 101 return ok, err 102 } 103 104 func (step *PutStep) run(ctx context.Context, state RunState, delegate PutDelegate) (bool, error) { 105 logger := lagerctx.FromContext(ctx) 106 logger = logger.Session("put-step", lager.Data{ 107 "step-name": step.plan.Name, 108 "job-id": step.metadata.JobID, 109 }) 110 111 delegate.Initializing(logger) 112 113 source, err := creds.NewSource(state, step.plan.Source).Evaluate() 114 if err != nil { 115 return false, err 116 } 117 118 params, err := creds.NewParams(state, step.plan.Params).Evaluate() 119 if err != nil { 120 return false, err 121 } 122 123 resourceTypes, err := creds.NewVersionedResourceTypes(state, step.plan.VersionedResourceTypes).Evaluate() 124 if err != nil { 125 return false, err 126 } 127 128 var putInputs PutInputs 129 if step.plan.Inputs == nil { 130 // Put step defaults to all inputs if not specified 131 putInputs = NewAllInputs() 132 } else if step.plan.Inputs.All { 133 putInputs = NewAllInputs() 134 } else if step.plan.Inputs.Detect { 135 putInputs = NewDetectInputs(step.plan.Params) 136 } else { 137 // Covers both cases where inputs are specified and when there are no 138 // inputs specified and "all" field is given a false boolean, which will 139 // result in no inputs attached 140 putInputs = NewSpecificInputs(step.plan.Inputs.Specified) 141 } 142 143 containerInputs, err := putInputs.FindAll(state.ArtifactRepository()) 144 if err != nil { 145 return false, err 146 } 147 148 workerSpec := worker.WorkerSpec{ 149 Tags: step.plan.Tags, 150 TeamID: step.metadata.TeamID, 151 ResourceType: step.plan.VersionedResourceTypes.Base(step.plan.Type), 152 } 153 154 var imageSpec worker.ImageSpec 155 resourceType, found := step.plan.VersionedResourceTypes.Lookup(step.plan.Type) 156 if found { 157 image := atc.ImageResource{ 158 Name: resourceType.Name, 159 Type: resourceType.Type, 160 Source: resourceType.Source, 161 Params: resourceType.Params, 162 Version: resourceType.Version, 163 Tags: resourceType.Tags, 164 } 165 if len(image.Tags) == 0 { 166 image.Tags = step.plan.Tags 167 } 168 169 types := step.plan.VersionedResourceTypes.Without(step.plan.Type) 170 171 var err error 172 imageSpec, err = delegate.FetchImage(ctx, image, types, resourceType.Privileged) 173 if err != nil { 174 return false, err 175 } 176 } else { 177 imageSpec.ResourceType = step.plan.Type 178 } 179 180 containerSpec := worker.ContainerSpec{ 181 ImageSpec: imageSpec, 182 TeamID: step.metadata.TeamID, 183 184 Dir: step.containerMetadata.WorkingDirectory, 185 186 Env: step.metadata.Env(), 187 188 ArtifactByPath: containerInputs, 189 } 190 tracing.Inject(ctx, &containerSpec) 191 192 owner := db.NewBuildStepContainerOwner(step.metadata.BuildID, step.planID, step.metadata.TeamID) 193 194 containerSpec.BindMounts = []worker.BindMountSource{ 195 &worker.CertsVolumeMount{Logger: logger}, 196 } 197 198 processSpec := runtime.ProcessSpec{ 199 Path: "/opt/resource/out", 200 Args: []string{resource.ResourcesDir("put")}, 201 StdoutWriter: delegate.Stdout(), 202 StderrWriter: delegate.Stderr(), 203 } 204 205 resourceToPut := step.resourceFactory.NewResource(source, params, nil) 206 207 result, err := step.workerClient.RunPutStep( 208 ctx, 209 logger, 210 owner, 211 containerSpec, 212 workerSpec, 213 step.strategy, 214 step.containerMetadata, 215 processSpec, 216 delegate, 217 resourceToPut, 218 ) 219 if err != nil { 220 logger.Error("failed-to-put-resource", err) 221 return false, err 222 } 223 224 if result.ExitStatus != 0 { 225 delegate.Finished(logger, ExitStatus(result.ExitStatus), runtime.VersionResult{}) 226 return false, nil 227 } 228 229 versionResult := result.VersionResult 230 // step.plan.Resource maps to an actual resource that may have been used outside of a pipeline context. 231 // Hence, if it was used outside the pipeline context, we don't want to save the output. 232 if step.plan.Resource != "" { 233 delegate.SaveOutput(logger, step.plan, source, resourceTypes, versionResult) 234 } 235 236 state.StoreResult(step.planID, versionResult) 237 238 delegate.Finished(logger, 0, versionResult) 239 240 return true, nil 241 }