github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/engine/build_step_delegate.go (about)

     1  package engine
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  	"time"
    10  
    11  	"code.cloudfoundry.org/clock"
    12  	"code.cloudfoundry.org/lager"
    13  	"github.com/pf-qiu/concourse/v6/atc"
    14  	"github.com/pf-qiu/concourse/v6/atc/db"
    15  	"github.com/pf-qiu/concourse/v6/atc/event"
    16  	"github.com/pf-qiu/concourse/v6/atc/exec"
    17  	"github.com/pf-qiu/concourse/v6/atc/exec/build"
    18  	"github.com/pf-qiu/concourse/v6/atc/policy"
    19  	"github.com/pf-qiu/concourse/v6/atc/worker"
    20  	"github.com/pf-qiu/concourse/v6/tracing"
    21  	"go.opentelemetry.io/otel/api/trace"
    22  )
    23  
    24  type buildStepDelegate struct {
    25  	build         db.Build
    26  	planID        atc.PlanID
    27  	clock         clock.Clock
    28  	state         exec.RunState
    29  	stderr        io.Writer
    30  	stdout        io.Writer
    31  	policyChecker policy.Checker
    32  }
    33  
    34  func NewBuildStepDelegate(
    35  	build db.Build,
    36  	planID atc.PlanID,
    37  	state exec.RunState,
    38  	clock clock.Clock,
    39  	policyChecker policy.Checker,
    40  ) *buildStepDelegate {
    41  	return &buildStepDelegate{
    42  		build:         build,
    43  		planID:        planID,
    44  		clock:         clock,
    45  		state:         state,
    46  		stdout:        nil,
    47  		stderr:        nil,
    48  		policyChecker: policyChecker,
    49  	}
    50  }
    51  
    52  func (delegate *buildStepDelegate) StartSpan(
    53  	ctx context.Context,
    54  	component string,
    55  	extraAttrs tracing.Attrs,
    56  ) (context.Context, trace.Span) {
    57  	attrs := delegate.build.TracingAttrs()
    58  	for k, v := range extraAttrs {
    59  		attrs[k] = v
    60  	}
    61  
    62  	return tracing.StartSpan(ctx, component, attrs)
    63  }
    64  
    65  type credVarsIterator struct {
    66  	line string
    67  }
    68  
    69  func (it *credVarsIterator) YieldCred(name, value string) {
    70  	for _, lineValue := range strings.Split(value, "\n") {
    71  		lineValue = strings.TrimSpace(lineValue)
    72  		// Don't consider a single char as a secret.
    73  		if len(lineValue) > 1 {
    74  			it.line = strings.Replace(it.line, lineValue, "((redacted))", -1)
    75  		}
    76  	}
    77  }
    78  
    79  func (delegate *buildStepDelegate) Stdout() io.Writer {
    80  	if delegate.stdout != nil {
    81  		return delegate.stdout
    82  	}
    83  	if delegate.state.RedactionEnabled() {
    84  		delegate.stdout = newDBEventWriterWithSecretRedaction(
    85  			delegate.build,
    86  			event.Origin{
    87  				Source: event.OriginSourceStdout,
    88  				ID:     event.OriginID(delegate.planID),
    89  			},
    90  			delegate.clock,
    91  			delegate.buildOutputFilter,
    92  		)
    93  	} else {
    94  		delegate.stdout = newDBEventWriter(
    95  			delegate.build,
    96  			event.Origin{
    97  				Source: event.OriginSourceStdout,
    98  				ID:     event.OriginID(delegate.planID),
    99  			},
   100  			delegate.clock,
   101  		)
   102  	}
   103  	return delegate.stdout
   104  }
   105  
   106  func (delegate *buildStepDelegate) Stderr() io.Writer {
   107  	if delegate.stderr != nil {
   108  		return delegate.stderr
   109  	}
   110  	if delegate.state.RedactionEnabled() {
   111  		delegate.stderr = newDBEventWriterWithSecretRedaction(
   112  			delegate.build,
   113  			event.Origin{
   114  				Source: event.OriginSourceStderr,
   115  				ID:     event.OriginID(delegate.planID),
   116  			},
   117  			delegate.clock,
   118  			delegate.buildOutputFilter,
   119  		)
   120  	} else {
   121  		delegate.stderr = newDBEventWriter(
   122  			delegate.build,
   123  			event.Origin{
   124  				Source: event.OriginSourceStderr,
   125  				ID:     event.OriginID(delegate.planID),
   126  			},
   127  			delegate.clock,
   128  		)
   129  	}
   130  	return delegate.stderr
   131  }
   132  
   133  func (delegate *buildStepDelegate) Initializing(logger lager.Logger) {
   134  	err := delegate.build.SaveEvent(event.Initialize{
   135  		Origin: event.Origin{
   136  			ID: event.OriginID(delegate.planID),
   137  		},
   138  		Time: time.Now().Unix(),
   139  	})
   140  	if err != nil {
   141  		logger.Error("failed-to-save-initialize-event", err)
   142  		return
   143  	}
   144  
   145  	logger.Info("initializing")
   146  }
   147  
   148  func (delegate *buildStepDelegate) Starting(logger lager.Logger) {
   149  	err := delegate.build.SaveEvent(event.Start{
   150  		Origin: event.Origin{
   151  			ID: event.OriginID(delegate.planID),
   152  		},
   153  		Time: time.Now().Unix(),
   154  	})
   155  	if err != nil {
   156  		logger.Error("failed-to-save-start-event", err)
   157  		return
   158  	}
   159  
   160  	logger.Debug("starting")
   161  }
   162  
   163  func (delegate *buildStepDelegate) Finished(logger lager.Logger, succeeded bool) {
   164  	// PR#4398: close to flush stdout and stderr
   165  	delegate.Stdout().(io.Closer).Close()
   166  	delegate.Stderr().(io.Closer).Close()
   167  
   168  	err := delegate.build.SaveEvent(event.Finish{
   169  		Origin: event.Origin{
   170  			ID: event.OriginID(delegate.planID),
   171  		},
   172  		Time:      time.Now().Unix(),
   173  		Succeeded: succeeded,
   174  	})
   175  	if err != nil {
   176  		logger.Error("failed-to-save-finish-event", err)
   177  		return
   178  	}
   179  
   180  	logger.Info("finished")
   181  }
   182  
   183  func (delegate *buildStepDelegate) SelectedWorker(logger lager.Logger, workerName string) {
   184  	err := delegate.build.SaveEvent(event.SelectedWorker{
   185  		Time: time.Now().Unix(),
   186  		Origin: event.Origin{
   187  			ID: event.OriginID(delegate.planID),
   188  		},
   189  		WorkerName: workerName,
   190  	})
   191  	if err != nil {
   192  		logger.Error("failed-to-save-selected-worker-event", err)
   193  		return
   194  	}
   195  }
   196  
   197  func (delegate *buildStepDelegate) Errored(logger lager.Logger, message string) {
   198  	err := delegate.build.SaveEvent(event.Error{
   199  		Message: message,
   200  		Origin: event.Origin{
   201  			ID: event.OriginID(delegate.planID),
   202  		},
   203  		Time: delegate.clock.Now().Unix(),
   204  	})
   205  	if err != nil {
   206  		logger.Error("failed-to-save-error-event", err)
   207  	}
   208  }
   209  
   210  // Name of the artifact fetched when using image_resource. Note that this only
   211  // exists within a local scope, so it doesn't pollute the build state.
   212  const defaultImageName = "image"
   213  
   214  func (delegate *buildStepDelegate) FetchImage(
   215  	ctx context.Context,
   216  	image atc.ImageResource,
   217  	types atc.VersionedResourceTypes,
   218  	privileged bool,
   219  ) (worker.ImageSpec, error) {
   220  	err := delegate.checkImagePolicy(image, privileged)
   221  	if err != nil {
   222  		return worker.ImageSpec{}, err
   223  	}
   224  
   225  	fetchState := delegate.state.NewLocalScope()
   226  
   227  	imageName := defaultImageName
   228  	if image.Name != "" {
   229  		imageName = image.Name
   230  	}
   231  
   232  	version := image.Version
   233  	if version == nil {
   234  		checkID := delegate.planID + "/image-check"
   235  
   236  		checkPlan := atc.Plan{
   237  			ID: checkID,
   238  			Check: &atc.CheckPlan{
   239  				Name:   imageName,
   240  				Type:   image.Type,
   241  				Source: image.Source,
   242  
   243  				VersionedResourceTypes: types,
   244  
   245  				Tags: image.Tags,
   246  			},
   247  		}
   248  
   249  		err := delegate.build.SaveEvent(event.ImageCheck{
   250  			Time: delegate.clock.Now().Unix(),
   251  			Origin: event.Origin{
   252  				ID: event.OriginID(delegate.planID),
   253  			},
   254  			PublicPlan: checkPlan.Public(),
   255  		})
   256  		if err != nil {
   257  			return worker.ImageSpec{}, fmt.Errorf("save image check event: %w", err)
   258  		}
   259  
   260  		ok, err := fetchState.Run(ctx, checkPlan)
   261  		if err != nil {
   262  			return worker.ImageSpec{}, err
   263  		}
   264  
   265  		if !ok {
   266  			return worker.ImageSpec{}, fmt.Errorf("image check failed")
   267  		}
   268  
   269  		if !fetchState.Result(checkID, &version) {
   270  			return worker.ImageSpec{}, fmt.Errorf("check did not return a version")
   271  		}
   272  	}
   273  
   274  	getID := delegate.planID + "/image-get"
   275  
   276  	getPlan := atc.Plan{
   277  		ID: getID,
   278  		Get: &atc.GetPlan{
   279  			Name:    imageName,
   280  			Type:    image.Type,
   281  			Source:  image.Source,
   282  			Version: &version,
   283  			Params:  image.Params,
   284  
   285  			VersionedResourceTypes: types,
   286  
   287  			Tags: image.Tags,
   288  		},
   289  	}
   290  
   291  	err = delegate.build.SaveEvent(event.ImageGet{
   292  		Time: delegate.clock.Now().Unix(),
   293  		Origin: event.Origin{
   294  			ID: event.OriginID(delegate.planID),
   295  		},
   296  		PublicPlan: getPlan.Public(),
   297  	})
   298  	if err != nil {
   299  		return worker.ImageSpec{}, fmt.Errorf("save image get event: %w", err)
   300  	}
   301  
   302  	ok, err := fetchState.Run(ctx, getPlan)
   303  	if err != nil {
   304  		return worker.ImageSpec{}, err
   305  	}
   306  
   307  	if !ok {
   308  		return worker.ImageSpec{}, fmt.Errorf("image fetching failed")
   309  	}
   310  
   311  	var cache db.UsedResourceCache
   312  	if !fetchState.Result(getID, &cache) {
   313  		return worker.ImageSpec{}, fmt.Errorf("get did not return a cache")
   314  	}
   315  
   316  	err = delegate.build.SaveImageResourceVersion(cache)
   317  	if err != nil {
   318  		return worker.ImageSpec{}, fmt.Errorf("save image version: %w", err)
   319  	}
   320  
   321  	art, found := fetchState.ArtifactRepository().ArtifactFor(build.ArtifactName(imageName))
   322  	if !found {
   323  		return worker.ImageSpec{}, fmt.Errorf("fetched artifact not found")
   324  	}
   325  
   326  	return worker.ImageSpec{
   327  		ImageArtifact: art,
   328  		Privileged:    privileged,
   329  	}, nil
   330  }
   331  
   332  func (delegate *buildStepDelegate) checkImagePolicy(image atc.ImageResource, privileged bool) error {
   333  	if !delegate.policyChecker.ShouldCheckAction(policy.ActionUseImage) {
   334  		return nil
   335  	}
   336  
   337  	redactedSource, err := delegate.redactImageSource(image.Source)
   338  	if err != nil {
   339  		return fmt.Errorf("redact source: %w", err)
   340  	}
   341  
   342  	result, err := delegate.policyChecker.Check(policy.PolicyCheckInput{
   343  		Action:   policy.ActionUseImage,
   344  		Team:     delegate.build.TeamName(),
   345  		Pipeline: delegate.build.PipelineName(),
   346  		Data: map[string]interface{}{
   347  			"image_type":   image.Type,
   348  			"image_source": redactedSource,
   349  			"privileged":   privileged,
   350  		},
   351  	})
   352  	if err != nil {
   353  		return fmt.Errorf("perform check: %w", err)
   354  	}
   355  
   356  	if !result.Allowed {
   357  		return policy.PolicyCheckNotPass{
   358  			Reasons: result.Reasons,
   359  		}
   360  	}
   361  
   362  	return nil
   363  }
   364  
   365  func (delegate *buildStepDelegate) buildOutputFilter(str string) string {
   366  	it := &credVarsIterator{line: str}
   367  	delegate.state.IterateInterpolatedCreds(it)
   368  	return it.line
   369  }
   370  
   371  func (delegate *buildStepDelegate) redactImageSource(source atc.Source) (atc.Source, error) {
   372  	b, err := json.Marshal(&source)
   373  	if err != nil {
   374  		return source, err
   375  	}
   376  	s := delegate.buildOutputFilter(string(b))
   377  	newSource := atc.Source{}
   378  	err = json.Unmarshal([]byte(s), &newSource)
   379  	if err != nil {
   380  		return source, err
   381  	}
   382  	return newSource, nil
   383  }