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  }