func()

in pkg/gcpbuildpack/builderoutput.go [71:126]


func (ctx *Context) saveErrorOutput(err error) {
	var be *buildererror.Error
	if !errors.As(err, &be) {
		be = buildererror.Errorf(buildererror.StatusInternal, "%s", err.Error())
	}
	outputDir := os.Getenv(builderOutputEnv)
	if outputDir == "" {
		return
	}

	if len(be.Message) > maxMessageBytes {
		be.Message = keepTail(be.Message)
	}

	be.BuildpackID, be.BuildpackVersion = ctx.BuildpackID(), ctx.BuildpackVersion()
	bo := builderoutput.BuilderOutput{Error: *be}
	bm := buildermetrics.GlobalBuilderMetrics()
	bmd := buildermetadata.GlobalBuilderMetadata()
	bo.Metrics = *bm
	bo.Metadata = *bmd
	data, err := bo.JSON()
	if err != nil {
		ctx.Warnf("Failed to marshal, skipping structured error output: %v", err)
		return
	}

	if err := os.MkdirAll(outputDir, 0755); err != nil {
		ctx.Warnf("Failed to create dir %s, skipping structured error output: %v", outputDir, err)
		return
	}

	// /bin/detect steps run in parallel, so they might compete over the output file. To eliminate
	// this competition, write to temp file, then `mv -f` to final location (last one in wins).
	tname := filepath.Join(outputDir, fmt.Sprintf("%s-%d", builderOutputFilename, rand.Int()))
	if err := ioutil.WriteFile(tname, data, 0644); err != nil {
		ctx.Warnf("Failed to write %s, skipping structured error output: %v", tname, err)
		return
	}
	fname := filepath.Join(outputDir, builderOutputFilename)
	if _, err := ctx.Exec([]string{"mv", "-f", tname, fname}); err != nil {
		ctx.Warnf("Failed to move %s to %s, skipping structured error output: %v", tname, fname, err)
		return
	}
	if expected := os.Getenv(expectedBuilderOutputEnv); expected != "" {
		// This logic is for acceptance tests. Ideally they would examine $BUILDER_OUTPUT themselves, but as
		// currently constructed that is difficult. So instead they delegate the task of checking whether
		// $BUILDER_OUTPUT contains a certain expected error-message pattern to this code.
		r, err := regexp.Compile(expected)
		if err == nil {
			ctx.Logf("Expected pattern included in error output: %t", r.MatchString(be.Message))
		} else {
			ctx.Warnf("Bad regexp %q: %v", expectedBuilderOutputEnv, err)
		}
	}
	return
}