Skip to content

Commit 042d985

Browse files
authored
Make download_action command write a script to execute the action locally (#515)
This will be useful if the developer just wants to run the action within the docker container on their machine after downloading the action inputs
1 parent 67d0b18 commit 042d985

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

go/pkg/tool/tool.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"bytes"
88
"context"
99
"fmt"
10+
"io/ioutil"
1011
"os"
1112
"path/filepath"
1213
"sort"
@@ -404,6 +405,10 @@ func (c *Client) DownloadAction(ctx context.Context, actionDigest, outputPath st
404405
return err
405406
}
406407

408+
if err := c.writeExecScript(ctx, commandProto, filepath.Join(outputPath, "run_locally.sh")); err != nil {
409+
return err
410+
}
411+
407412
log.Infof("Fetching input tree from input root digest.. %v", actionProto.GetInputRootDigest())
408413
rootPath := filepath.Join(outputPath, "input")
409414
os.RemoveAll(rootPath)
@@ -439,6 +444,55 @@ func (c *Client) DownloadAction(ctx context.Context, actionDigest, outputPath st
439444
return nil
440445
}
441446

447+
func (c *Client) writeExecScript(ctx context.Context, cmd *repb.Command, filename string) error {
448+
if cmd == nil {
449+
return fmt.Errorf("invalid comment (nil)")
450+
}
451+
452+
var runActionScript bytes.Buffer
453+
runActionFilename := filepath.Join(filepath.Dir(filename), "run_command.sh")
454+
wd := cmd.WorkingDirectory
455+
execCmd := strings.Join(cmd.GetArguments(), " ")
456+
runActionScript.WriteString(fmt.Sprintf("#!/bin/bash\n\n"))
457+
runActionScript.WriteString(fmt.Sprintf("# This script is meant to be called by %v.\n", filename))
458+
if wd != "" {
459+
runActionScript.WriteString(fmt.Sprintf("cd %v\n", wd))
460+
}
461+
for _, od := range cmd.GetOutputDirectories() {
462+
runActionScript.WriteString(fmt.Sprintf("mkdir -p %v\n", od))
463+
}
464+
for _, of := range cmd.GetOutputFiles() {
465+
runActionScript.WriteString(fmt.Sprintf("mkdir -p %v\n", filepath.Dir(of)))
466+
}
467+
for _, e := range cmd.GetEnvironmentVariables() {
468+
runActionScript.WriteString(fmt.Sprintf("export %v=%v\n", e.GetName(), e.GetValue()))
469+
}
470+
runActionScript.WriteString(fmt.Sprintf("%v\n", execCmd))
471+
runActionScript.WriteString(fmt.Sprintf("bash\n"))
472+
if err := ioutil.WriteFile(runActionFilename, runActionScript.Bytes(), 0755); err != nil {
473+
return err
474+
}
475+
476+
var container string
477+
for _, property := range cmd.Platform.GetProperties() {
478+
if property.Name == "container-image" {
479+
container = strings.TrimPrefix(property.Value, "docker://")
480+
}
481+
}
482+
if container == "" {
483+
return fmt.Errorf("container-image platform property missing from command proto: %v", cmd)
484+
}
485+
var execScript bytes.Buffer
486+
dockerCmd := fmt.Sprintf("docker run -i -t -w /b/f/w -v `pwd`/input:/b/f/w -v `pwd`/run_command.sh:/b/f/w/run_command.sh %v ./run_command.sh\n", container)
487+
execScript.WriteString(fmt.Sprintf("#!/bin/bash\n\n"))
488+
execScript.WriteString(fmt.Sprintf("# This script can be used to run the action locally on\n"))
489+
execScript.WriteString(fmt.Sprintf("# this machine.\n"))
490+
execScript.WriteString(fmt.Sprintf("echo \"WARNING: The results from executing the action through this script may differ from results from RBE.\"\n"))
491+
execScript.WriteString(fmt.Sprintf("set -x\n"))
492+
execScript.WriteString(dockerCmd)
493+
return ioutil.WriteFile(filename, execScript.Bytes(), 0755)
494+
}
495+
442496
func (c *Client) prepProtos(ctx context.Context, actionRoot string) (string, error) {
443497
cmdTxt, err := os.ReadFile(filepath.Join(actionRoot, "cmd.textproto"))
444498
if err != nil {

go/pkg/tool/tool_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ func TestTool_DownloadAction(t *testing.T) {
148148
ExecRoot: e.ExecRoot,
149149
InputSpec: &command.InputSpec{Inputs: []string{"i1", "a/b/i2"}, InputNodeProperties: map[string]*cpb.NodeProperties{"i1": fooProperties}},
150150
OutputFiles: []string{"a/b/out"},
151+
Platform: map[string]string{
152+
"container-image": "foo",
153+
},
151154
}
152155
_, acDg, _, _ := e.Set(cmd, command.DefaultExecutionOptions(), &command.Result{Status: command.SuccessResultStatus}, &fakes.InputFile{Path: "i1", Contents: "i1"}, &fakes.InputFile{Path: "a/b/i2", Contents: "i2"})
153156

@@ -161,6 +164,14 @@ func TestTool_DownloadAction(t *testing.T) {
161164
reCmdPb := &repb.Command{
162165
Arguments: []string{"foo", "bar", "baz"},
163166
OutputFiles: []string{"a/b/out"},
167+
Platform: &repb.Platform{
168+
Properties: []*repb.Platform_Property{
169+
&repb.Platform_Property{
170+
Name: "container-image",
171+
Value: "foo",
172+
},
173+
},
174+
},
164175
}
165176
acPb := &repb.Action{
166177
CommandDigest: digest.TestNewFromMessage(reCmdPb).ToProto(),
@@ -212,6 +223,9 @@ func TestTool_ExecuteAction(t *testing.T) {
212223
ExecRoot: e.ExecRoot,
213224
InputSpec: &command.InputSpec{Inputs: []string{"i1", "i2"}, InputNodeProperties: map[string]*cpb.NodeProperties{"i1": fooProperties}},
214225
OutputFiles: []string{"a/b/out"},
226+
Platform: map[string]string{
227+
"container-image": "foo",
228+
},
215229
}
216230
opt := &command.ExecutionOptions{AcceptCached: false, DownloadOutputs: true, DownloadOutErr: true}
217231
_, acDg, _, _ := e.Set(cmd, opt, &command.Result{Status: command.SuccessResultStatus}, &fakes.OutputFile{Path: "a/b/out", Contents: "out"},

0 commit comments

Comments
 (0)