Skip to content

Commit e34b869

Browse files
committed
Get input output with symlinks
Add a function to return inputs and outputs. Note that symlinks point foo to bar will be represented as foo->bar.
1 parent bcc51f0 commit e34b869

File tree

3 files changed

+174
-4
lines changed

3 files changed

+174
-4
lines changed

go/pkg/fakes/server.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
2929
bsgrpc "google.golang.org/genproto/googleapis/bytestream"
3030
bspb "google.golang.org/genproto/googleapis/bytestream"
31-
anypb "google.golang.org/protobuf/types/known/anypb"
31+
"google.golang.org/protobuf/types/known/anypb"
3232
dpb "google.golang.org/protobuf/types/known/durationpb"
3333
tspb "google.golang.org/protobuf/types/known/timestamppb"
3434
)
@@ -304,6 +304,26 @@ func (f *InputFile) apply(ac *repb.ActionResult, s *Server, execRoot string) err
304304
return nil
305305
}
306306

307+
// InputSymlink to be made available to the fake action.
308+
type InputSymlink struct {
309+
Path string // newname
310+
Content string
311+
Target string // oldname
312+
}
313+
314+
// Apply puts the target file in the fake CAS and create a symlink in OS.
315+
func (ins *InputSymlink) apply(ac *repb.ActionResult, s *Server, execRoot string) error {
316+
inf := InputFile{Path: ins.Target, Contents: ins.Content}
317+
if err := inf.apply(ac, s, execRoot); err != nil {
318+
return err
319+
}
320+
// create a symlink from old name (target) to new name (path)
321+
if err := os.Symlink(filepath.Join(execRoot, ins.Target), filepath.Join(execRoot, ins.Path)); err != nil {
322+
return fmt.Errorf("error creating symlink: %w", err)
323+
}
324+
return nil
325+
}
326+
307327
// OutputFile is to be added as an output of the fake action.
308328
type OutputFile struct {
309329
Path string

go/pkg/tool/tool.go

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ func (c *Client) getOutputs(ctx context.Context, actionRes *repb.ActionResult) (
770770
return res.String(), nil
771771
}
772772

773-
func (c *Client) getInputTree(ctx context.Context, root *repb.Digest) (string, []string, error) {
773+
func (c *Client) getInputTree(ctx context.Context, root *repb.Digest) (string, map[string]string, error) {
774774
var res bytes.Buffer
775775

776776
dg, err := digest.NewFromProto(root)
@@ -800,7 +800,7 @@ func (c *Client) getInputTree(ctx context.Context, root *repb.Digest) (string, [
800800
return res.String(), paths, nil
801801
}
802802

803-
func (c *Client) flattenTree(ctx context.Context, t *repb.Tree) (string, []string, error) {
803+
func (c *Client) flattenTree(ctx context.Context, t *repb.Tree) (string, map[string]string, error) {
804804
var res bytes.Buffer
805805
outputs, err := c.GrpcClient.FlattenTree(t, "")
806806
if err != nil {
@@ -816,8 +816,10 @@ func (c *Client) flattenTree(ctx context.Context, t *repb.Tree) (string, []strin
816816
paths = append(paths, path)
817817
}
818818
sort.Strings(paths)
819+
pathDgs := make(map[string]string, len(paths))
819820
for _, path := range paths {
820821
output := outputs[path]
822+
dg := output.Digest
821823
var np string
822824
if output.NodeProperties != nil {
823825
np = fmt.Sprintf(" [Node properties: %v]", prototext.MarshalOptions{Multiline: false}.Format(output.NodeProperties))
@@ -826,11 +828,16 @@ func (c *Client) flattenTree(ctx context.Context, t *repb.Tree) (string, []strin
826828
res.WriteString(fmt.Sprintf("%v: [Directory digest: %v]%s\n", path, output.Digest, np))
827829
} else if output.SymlinkTarget != "" {
828830
res.WriteString(fmt.Sprintf("%v: [Symlink digest: %v, Symlink Target: %v]%s\n", path, output.Digest, output.SymlinkTarget, np))
831+
path = path + "->" + output.SymlinkTarget
832+
if o, ok := outputs[output.SymlinkTarget]; ok {
833+
dg = o.Digest
834+
}
829835
} else {
830836
res.WriteString(fmt.Sprintf("%v: [File digest: %v]%s\n", path, output.Digest, np))
831837
}
838+
pathDgs[path] = fmt.Sprintf("%v", dg)
832839
}
833-
return res.String(), paths, nil
840+
return res.String(), pathDgs, nil
834841
}
835842

836843
func (c *Client) getActionResult(ctx context.Context, actionDigest string) (*repb.ActionResult, error) {
@@ -848,3 +855,91 @@ func (c *Client) getActionResult(ctx context.Context, actionDigest string) (*rep
848855
}
849856
return resPb, nil
850857
}
858+
859+
// IO is a collection input root Digest, Inputs, and Outputs for an action.
860+
type IO struct {
861+
RootDg string
862+
Inputs *Inputs
863+
Outputs *Outputs
864+
}
865+
866+
// Inputs are input Paths (with digests) of an action. For a symlink, the key is
867+
// `<path>-><target>`, the value is the dg of the target file.
868+
type Inputs struct {
869+
Paths map[string]string
870+
}
871+
872+
// Outputs are output Files/Dirs (with digests) of an action. For a symlink, the
873+
// key is `<path>-><target>`, the value is the dg of the target file.
874+
type Outputs struct {
875+
Files map[string]string
876+
Dirs map[string]string
877+
FileSymlinks map[string]string
878+
DirSymlinks map[string]string
879+
}
880+
881+
// GetIO returns the Inputs and Outputs of an action Digest.
882+
func (c *Client) GetIO(ctx context.Context, actionDigest string) (*IO, error) {
883+
acDg, err := digest.NewFromString(actionDigest)
884+
if err != nil {
885+
return nil, fmt.Errorf("error creating action digest: %w", err)
886+
}
887+
actionProto := &repb.Action{}
888+
// Get all the Inputs.
889+
if _, err := c.GrpcClient.ReadProto(ctx, acDg, actionProto); err != nil {
890+
return nil, fmt.Errorf("error reading from proto: %w", err)
891+
}
892+
rootDg, err := digest.NewFromProto(actionProto.GetInputRootDigest())
893+
if err != nil {
894+
return nil, fmt.Errorf("error getting input root digest: %w", err)
895+
}
896+
_, inputs, err := c.getInputTree(ctx, actionProto.GetInputRootDigest())
897+
if err != nil {
898+
return nil, err
899+
}
900+
// Get all the Outputs.
901+
resPb, err := c.getActionResult(ctx, actionDigest)
902+
if err != nil {
903+
return nil, err
904+
}
905+
906+
files := map[string]string{}
907+
dirs := map[string]string{}
908+
fileSymlinks := map[string]string{}
909+
dirSymlinks := map[string]string{}
910+
for _, f := range resPb.GetOutputFiles() {
911+
if f != nil {
912+
dg, err := digest.NewFromProto(f.GetDigest())
913+
if err != nil {
914+
log.Errorf("error creating Digest from proto %v: %w", f.GetDigest(), err)
915+
}
916+
files[f.GetPath()] = fmt.Sprintf("%v", dg)
917+
}
918+
}
919+
for _, d := range resPb.GetOutputDirectories() {
920+
if d != nil {
921+
dg, err := digest.NewFromProto(d.GetTreeDigest())
922+
if err != nil {
923+
log.Errorf("error creating Digest from proto %v: %w", d.GetTreeDigest(), err)
924+
}
925+
dirs[d.GetPath()] = fmt.Sprintf("%v", dg)
926+
}
927+
}
928+
for _, fs := range resPb.GetOutputFileSymlinks() {
929+
if fs != nil {
930+
fileSymlinks[fs.GetPath()+"->"+fs.GetTarget()] = files[fs.GetTarget()]
931+
}
932+
}
933+
for _, ds := range resPb.GetOutputDirectorySymlinks() {
934+
if ds != nil {
935+
dirSymlinks[ds.GetPath()+"->"+ds.GetTarget()] = dirs[ds.GetTarget()]
936+
}
937+
}
938+
939+
return &IO{
940+
RootDg: fmt.Sprintf("%v", rootDg),
941+
Inputs: &Inputs{inputs},
942+
Outputs: &Outputs{Files: files, Dirs: dirs, FileSymlinks: fileSymlinks, DirSymlinks: dirSymlinks},
943+
}, nil
944+
945+
}

go/pkg/tool/tool_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,58 @@ func TestTool_UploadBlob(t *testing.T) {
444444
t.Fatalf("Expected 1 write for blob '%v', got %v", dg.String(), cas.BlobWrites(dg))
445445
}
446446
}
447+
448+
func TestTool_GetIO(t *testing.T) {
449+
e, cleanup := fakes.NewTestEnv(t)
450+
defer cleanup()
451+
cmd := &command.Command{
452+
Args: []string{"tool"},
453+
ExecRoot: e.ExecRoot,
454+
InputSpec: &command.InputSpec{
455+
Inputs: []string{"foo.c", "bar.c"},
456+
SymlinkBehavior: command.PreserveSymlink,
457+
},
458+
}
459+
opt := command.DefaultExecutionOptions()
460+
_, acDg, _, _ := e.Set(
461+
cmd,
462+
opt,
463+
&command.Result{Status: command.CacheHitResultStatus},
464+
&fakes.InputFile{Path: "foo.c", Contents: "foo"},
465+
&fakes.InputSymlink{Path: "bar.c", Content: "bar", Target: "previous_dir/old_target_file"},
466+
&fakes.OutputFile{Path: "a/b/out", Contents: "foo"},
467+
&fakes.OutputSymlink{Path: "a/b/sl", Target: "a/b/out"},
468+
)
469+
toolClient := &Client{GrpcClient: e.Client.GrpcClient}
470+
tmpDir := t.TempDir()
471+
io, err := toolClient.GetIO(context.Background(), acDg.String())
472+
if err != nil {
473+
t.Fatalf("DownloadActionResult(%v,%v) failed: %v", acDg.String(), tmpDir, err)
474+
}
475+
476+
getInputRootDg := io.RootDg
477+
wantInputRootDg := "ea520b417ef2887249b4ea2d24ff64f5d7d6c419eb1a0ce2067c39ece5c37b56/206"
478+
if diff := cmp.Diff(wantInputRootDg, getInputRootDg); diff != "" {
479+
t.Errorf("GetIO returned diff in Inputs' list: (-want +got)\n%s", diff)
480+
}
481+
getInputs := io.Inputs.Paths
482+
wantInputs := map[string]string{
483+
"foo.c": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae/3",
484+
"previous_dir/old_target_file": "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9/3",
485+
"bar.c->previous_dir/old_target_file": "fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9/3",
486+
}
487+
if diff := cmp.Diff(wantInputs, getInputs); diff != "" {
488+
t.Errorf("GetIO returned diff in Inputs' list: (-want +got)\n%s", diff)
489+
}
490+
491+
getOutputFiles := io.Outputs.Files
492+
wantOutputFiles := map[string]string{"a/b/out": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae/3"}
493+
if diff := cmp.Diff(wantOutputFiles, getOutputFiles); diff != "" {
494+
t.Errorf("GetIO returned diff in Outputs' list: (-want +got)\n%s", diff)
495+
}
496+
getOutputFileSyms := io.Outputs.FileSymlinks
497+
wantOutputFileSyms := map[string]string{"a/b/sl->a/b/out": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae/3"}
498+
if diff := cmp.Diff(wantOutputFileSyms, getOutputFileSyms); diff != "" {
499+
t.Errorf("GetIO returned diff in Outputs' list: (-want +got)\n%s", diff)
500+
}
501+
}

0 commit comments

Comments
 (0)