func writeFileToCAS(bzFiler *bzsnapshot.BzFiler, path string) (*remoteexecution.Digest, error) {
	bytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("Error reading file %s as bytes: %s", path, err)
	}
	sha := fmt.Sprintf("%x", sha256.Sum256(bytes))
	digest := &remoteexecution.Digest{Hash: sha, SizeBytes: int64(len(bytes))}
	err = cas.MakeCASClient().ByteStreamWrite(bzFiler.CASResolver, digest, bytes, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 5))
	if err != nil {
		return nil, fmt.Errorf("Error writing data to CAS server: %s", err)
	}
	log.Infof("Wrote file to CAS: %s as %s", path, digest)
	return digest, nil
}
Write a file's bytes to the BzFiler's CAS, returning the Digest of the uploaded file.
This is distinct from using a Filer to Ingest data into the CAS,
which allows for Filer-implementation-specific behavior that could alter the bytes and expected digest.
(Our typical BzFiler use case is uploading Files that include relative path and other protobuf data)
package runners
import (
	"crypto/sha256"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"time"
	"github.com/cenkalti/backoff"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/any"
	log "github.com/sirupsen/logrus"
	remoteexecution "github.com/twitter/scoot/bazel/remoteexecution"
	google_rpc_code "google.golang.org/genproto/googleapis/rpc/code"
	google_rpc_errdetails "google.golang.org/genproto/googleapis/rpc/errdetails"
	google_rpc_status "google.golang.org/genproto/googleapis/rpc/status"
	"github.com/twitter/scoot/bazel"
	"github.com/twitter/scoot/bazel/cas"
	"github.com/twitter/scoot/bazel/execution/bazelapi"
	scootproto "github.com/twitter/scoot/common/proto"
	"github.com/twitter/scoot/runner"
	"github.com/twitter/scoot/runner/execer"
	"github.com/twitter/scoot/snapshot"
	bzsnapshot "github.com/twitter/scoot/snapshot/bazel"
)
func preProcessBazel(filer snapshot.Filer, cmd *runner.Command, rts *runTimes) (*bazelapi.ActionResult, []*remoteexecution.Digest, error) {
	notExist := []*remoteexecution.Digest{}
	bzFiler, ok := filer.(*bzsnapshot.BzFiler)
	if !ok {
		return nil, notExist, fmt.Errorf("Filer could not be asserted as type BzFiler. Type is: %s", reflect.TypeOf(filer))
	}
	if cmd.ExecuteRequest == nil {
		return nil, notExist, fmt.Errorf("Nil ExecuteRequest data in Command with RunType Bazel")
	}
	if !cmd.ExecuteRequest.GetRequest().GetSkipCacheLookup() {
		log.Info("Checking for existing results for command in ActionCache")
		rts.actionCacheCheckStart = stamp()
		ar, err := cas.MakeCASClient().GetCacheResult(bzFiler.CASResolver, cmd.ExecuteRequest.GetRequest().GetActionDigest(), backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 5))
		rts.actionCacheCheckEnd = stamp()
		if err != nil {
			if !cas.IsNotFoundError(err) {
				log.Errorf("Failed to check for cached result, will execute: %s", err)
			}
		} else if ar != nil {
			log.Info("Returning cached result for command")
			return &bazelapi.ActionResult{
				Result:       ar,
				ActionDigest: cmd.ExecuteRequest.GetRequest().GetActionDigest(),
				Cached:       true,
			}, notExist, nil
		}
	}
	missing, err := fetchBazelCommandData(bzFiler, cmd, rts)
	if err != nil {
		log.Errorf("Error fetching Bazel command: %v", err)
		return nil, missing, err
	}
	log.Infof("Worker running with updated command arguments: %q", cmd.Argv)
	return nil, notExist, nil
}
func fetchBazelCommandData(bzFiler *bzsnapshot.BzFiler, cmd *runner.Command, rts *runTimes) ([]*remoteexecution.Digest, error) {
	casCli := cas.MakeCASClient()
	notExist := []*remoteexecution.Digest{}
	log.Info("Fetching Bazel Action data from CAS server")
	rts.actionFetchStart = stamp()
	actionDigest := cmd.ExecuteRequest.GetRequest().GetActionDigest()
	actionBytes, err := casCli.ByteStreamRead(bzFiler.CASResolver, actionDigest, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 5))
	if err != nil {
		log.Errorf("Error reading Action data from CAS server: %s", err)
		notExist = append(notExist, actionDigest)
		return notExist, err
	}
	action := &remoteexecution.Action{}
	if err = proto.Unmarshal(actionBytes, action); err != nil {
		return notExist, fmt.Errorf("Failed to unmarshal bytes as remoteexecution.Action: %s", err)
	}
	rts.actionFetchEnd = stamp()
	d, err := time.ParseDuration(fmt.Sprintf("%dms", scootproto.GetMsFromDuration(action.GetTimeout())))
	if err != nil {
		return notExist, fmt.Errorf("Failed to parse Action Timeout as a Duration: %v", err)
	}
	log.Info("Fetching Bazel Command data from CAS server")
	rts.commandFetchStart = stamp()
	commandDigest := action.GetCommandDigest()
	commandBytes, err := casCli.ByteStreamRead(bzFiler.CASResolver, commandDigest, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 5))
	if err != nil {
		log.Errorf("Error reading Command data from CAS server: %s", err)
		notExist = append(notExist, commandDigest)
		return notExist, err
	}
	bzCommand := &remoteexecution.Command{}
	if err = proto.Unmarshal(commandBytes, bzCommand); err != nil {
		return notExist, fmt.Errorf("Failed to unmarshal bytes as remoteexecution.Command: %s", err)
	}
	rts.commandFetchEnd = stamp()
	cmd.SnapshotID = bazel.SnapshotIDFromDigest(action.GetInputRootDigest())
	cmd.Argv = bzCommand.GetArguments()
	for _, envVar := range bzCommand.GetEnvironmentVariables() {
		cmd.EnvVars[envVar.GetName()] = envVar.GetValue()
	}
	cmd.Timeout = d
	cmd.ExecuteRequest.Action = action
	cmd.ExecuteRequest.Command = bzCommand
	return notExist, nil
}
func setupJDKSymlink(path, filename string) error {
	jh, ok := os.LookupEnv("JAVA_HOME")
	if !ok {
		return fmt.Errorf("Unable to find $JAVA_HOME. Symlink not created")
	}
	return os.Symlink(jh, filepath.Join(path, filename))
}
func createOutputPaths(cmd *runner.Command, coDir string) error {
	for _, relPath := range cmd.ExecuteRequest.GetCommand().GetOutputFiles() {
		dir := filepath.Dir(filepath.Join(coDir, relPath))
		err := os.MkdirAll(dir, 0777)
		if err != nil {
			return fmt.Errorf("Failed to create parent output dir %s: %s", dir, err)
		}
	}
	for _, relPath := range cmd.ExecuteRequest.GetCommand().GetOutputDirectories() {
		dir := filepath.Dir(filepath.Join(coDir, relPath))
		err := os.MkdirAll(dir, 0777)
		if err != nil {
			return fmt.Errorf("Failed to create parent output dir %s: %s", dir, err)
		}
	}
	return nil
}
func postProcessBazel(filer snapshot.Filer,
	cmd *runner.Command,
	coDir string,
	stdout, stderr runner.Output,
	st execer.ProcessStatus,
	rts *runTimes,
	rID runner.RunnerID,
) (*bazelapi.ActionResult, error) {
	bzFiler, ok := filer.(*bzsnapshot.BzFiler)
	if !ok {
		return nil, fmt.Errorf("Filer could not be asserted as type BzFiler. Type is: %s", reflect.TypeOf(filer))
	}
	log.Info("Processing Bazel outputs to CAS")
	stdoutDigest, err := writeFileToCAS(bzFiler, stdout.AsFile())
	if err != nil {
		errstr := fmt.Sprintf("Error ingesting stdout to CAS: %s", err)
		log.Error(errstr)
		return nil, fmt.Errorf(errstr)
	}
	stderrDigest, err := writeFileToCAS(bzFiler, stderr.AsFile())
	if err != nil {
		errstr := fmt.Sprintf("Error ingesting stderr to CAS: %s", err)
		log.Error(errstr)
		return nil, fmt.Errorf(errstr)
	}
	outputFiles, err := ingestOutputFiles(bzFiler, cmd, coDir)
	if err != nil {
		errstr := fmt.Sprintf("Error ingesting OutputFiles: %s", err)
		log.Error(errstr)
		return nil, fmt.Errorf(errstr)
	}
	outputDirs, err := ingestOutputDirs(bzFiler, cmd, coDir)
	if err != nil {
		errstr := fmt.Sprintf("Error ingesting OutputDirs: %s", err)
		log.Error(errstr)
		return nil, fmt.Errorf(errstr)
	}
	rts.outputEnd = stamp()
	rts.invokeEnd = stamp()
	rts.queuedTime = scootproto.GetTimeFromTimestamp(cmd.ExecuteRequest.GetExecutionMetadata().GetQueuedTimestamp())
	metadata := &remoteexecution.ExecutedActionMetadata{
		Worker:                         rID.String(),
		QueuedTimestamp:                cmd.ExecuteRequest.GetExecutionMetadata().GetQueuedTimestamp(),
		WorkerStartTimestamp:           scootproto.GetTimestampFromTime(rts.invokeStart),
		WorkerCompletedTimestamp:       scootproto.GetTimestampFromTime(rts.invokeEnd),
		InputFetchStartTimestamp:       scootproto.GetTimestampFromTime(rts.inputStart),
		InputFetchCompletedTimestamp:   scootproto.GetTimestampFromTime(rts.inputEnd),
		ExecutionStartTimestamp:        scootproto.GetTimestampFromTime(rts.execStart),
		ExecutionCompletedTimestamp:    scootproto.GetTimestampFromTime(rts.execEnd),
		OutputUploadStartTimestamp:     scootproto.GetTimestampFromTime(rts.outputStart),
		OutputUploadCompletedTimestamp: scootproto.GetTimestampFromTime(rts.outputEnd),
	}
	ar := &remoteexecution.ActionResult{
		OutputFiles:       outputFiles,
		OutputDirectories: outputDirs,
		ExitCode:          int32(st.ExitCode),
		StdoutDigest:      stdoutDigest,
		StderrDigest:      stderrDigest,
		ExecutionMetadata: metadata,
	}
	ad := cmd.ExecuteRequest.GetRequest().GetActionDigest()
	if !cmd.ExecuteRequest.GetAction().GetDoNotCache() {
		log.Info("Updating results in ActionCache")
		_, err = cas.MakeCASClient().UpdateCacheResult(bzFiler.CASResolver, ad, ar, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 5))
		if err != nil {
			log.Errorf("Error updating result to ActionCache: %s", err)
		}
	}
	return &bazelapi.ActionResult{
		Result:       ar,
		ActionDigest: ad,
		Cached:       false,
	}, nil
}