Skip to content

Commit 9512a2e

Browse files
authored
Merge pull request #6262 from jsternberg/container-fs-requests
gateway: create interface for reading from container filesystem
2 parents 5068904 + 2999fdc commit 9512a2e

File tree

12 files changed

+819
-82
lines changed

12 files changed

+819
-82
lines changed

client/build.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,36 @@ func (g *gatewayClientForBuild) ReleaseContainer(ctx context.Context, in *gatewa
178178
return g.gateway.ReleaseContainer(ctx, in, opts...)
179179
}
180180

181+
func (g *gatewayClientForBuild) ReadFileContainer(ctx context.Context, in *gatewayapi.ReadFileRequest, opts ...grpc.CallOption) (*gatewayapi.ReadFileResponse, error) {
182+
if g.caps != nil {
183+
if err := g.caps.Supports(gatewayapi.CapGatewayExecFilesystem); err != nil {
184+
return nil, err
185+
}
186+
}
187+
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
188+
return g.gateway.ReadFileContainer(ctx, in, opts...)
189+
}
190+
191+
func (g *gatewayClientForBuild) ReadDirContainer(ctx context.Context, in *gatewayapi.ReadDirRequest, opts ...grpc.CallOption) (*gatewayapi.ReadDirResponse, error) {
192+
if g.caps != nil {
193+
if err := g.caps.Supports(gatewayapi.CapGatewayExecFilesystem); err != nil {
194+
return nil, err
195+
}
196+
}
197+
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
198+
return g.gateway.ReadDirContainer(ctx, in, opts...)
199+
}
200+
201+
func (g *gatewayClientForBuild) StatFileContainer(ctx context.Context, in *gatewayapi.StatFileRequest, opts ...grpc.CallOption) (*gatewayapi.StatFileResponse, error) {
202+
if g.caps != nil {
203+
if err := g.caps.Supports(gatewayapi.CapGatewayExecFilesystem); err != nil {
204+
return nil, err
205+
}
206+
}
207+
ctx = buildid.AppendToOutgoingContext(ctx, g.buildID)
208+
return g.gateway.StatFileContainer(ctx, in, opts...)
209+
}
210+
181211
func (g *gatewayClientForBuild) ExecProcess(ctx context.Context, opts ...grpc.CallOption) (gatewayapi.LLBBridge_ExecProcessClient, error) {
182212
if g.caps != nil {
183213
if err := g.caps.Supports(gatewayapi.CapGatewayExec); err != nil {

client/build_test.go

Lines changed: 92 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ func testWarnings(t *testing.T, sb integration.Sandbox) {
174174
product := "buildkit_test"
175175

176176
b := func(ctx context.Context, c client.Client) (*client.Result, error) {
177-
st := llb.Scratch().File(llb.Mkfile("/dummy", 0600, []byte("foo")))
177+
st := llb.Scratch().File(llb.Mkfile("/dummy", 0o600, []byte("foo")))
178178

179179
def, err := st.Marshal(ctx)
180180
if err != nil {
@@ -660,7 +660,7 @@ func testClientGatewayContainerMounts(t *testing.T, sb integration.Sandbox) {
660660

661661
tmpdir := integration.Tmpdir(t)
662662

663-
err = os.WriteFile(filepath.Join(tmpdir.Name, "local-file"), []byte("local"), 0644)
663+
err = os.WriteFile(filepath.Join(tmpdir.Name, "local-file"), []byte("local"), 0o644)
664664
require.NoError(t, err)
665665

666666
a := agent.NewKeyring()
@@ -690,31 +690,51 @@ func testClientGatewayContainerMounts(t *testing.T, sb integration.Sandbox) {
690690
// TODO How do we get a results.Ref for a cache mount, tmpfs mount
691691
}
692692

693-
containerMounts := []client.Mount{{
694-
Dest: "/cached",
695-
MountType: pb.MountType_CACHE,
696-
CacheOpt: &pb.CacheOpt{
697-
ID: t.Name(),
698-
Sharing: pb.CacheSharingOpt_SHARED,
693+
containerMounts := []client.Mount{
694+
{
695+
Dest: "/",
696+
MountType: pb.MountType_BIND,
699697
},
700-
}, {
701-
Dest: "/tmpfs",
702-
MountType: pb.MountType_TMPFS,
703-
}, {
704-
Dest: "/run/secrets/mysecret",
705-
MountType: pb.MountType_SECRET,
706-
SecretOpt: &pb.SecretOpt{
707-
ID: "/run/secrets/mysecret",
698+
{
699+
Dest: "/foo",
700+
MountType: pb.MountType_BIND,
708701
},
709-
}, {
710-
Dest: sockPath,
711-
MountType: pb.MountType_SSH,
712-
SSHOpt: &pb.SSHOpt{
713-
ID: t.Name(),
702+
{
703+
Dest: "/local",
704+
MountType: pb.MountType_BIND,
714705
},
715-
}}
706+
{
707+
Dest: "/cached",
708+
MountType: pb.MountType_CACHE,
709+
CacheOpt: &pb.CacheOpt{
710+
ID: t.Name(),
711+
Sharing: pb.CacheSharingOpt_SHARED,
712+
},
713+
},
714+
{
715+
Dest: "/tmpfs",
716+
MountType: pb.MountType_TMPFS,
717+
},
718+
{
719+
Dest: "/run/secrets/mysecret",
720+
MountType: pb.MountType_SECRET,
721+
SecretOpt: &pb.SecretOpt{
722+
ID: "/run/secrets/mysecret",
723+
},
724+
},
725+
{
726+
Dest: sockPath,
727+
MountType: pb.MountType_SSH,
728+
SSHOpt: &pb.SSHOpt{
729+
ID: t.Name(),
730+
},
731+
},
732+
}
733+
734+
// Fill in mount references.
735+
for i, m := range containerMounts {
736+
st := mounts[m.Dest]
716737

717-
for mountpoint, st := range mounts {
718738
def, err := st.Marshal(ctx)
719739
if err != nil {
720740
return nil, errors.Wrap(err, "failed to marshal state")
@@ -726,11 +746,7 @@ func testClientGatewayContainerMounts(t *testing.T, sb integration.Sandbox) {
726746
if err != nil {
727747
return nil, errors.Wrap(err, "failed to solve")
728748
}
729-
containerMounts = append(containerMounts, client.Mount{
730-
Dest: mountpoint,
731-
MountType: pb.MountType_BIND,
732-
Ref: r.Ref,
733-
})
749+
containerMounts[i].Ref = r.Ref
734750
}
735751

736752
ctr, err := c.NewContainer(ctx, client.NewContainerRequest{Mounts: containerMounts})
@@ -745,6 +761,43 @@ func testClientGatewayContainerMounts(t *testing.T, sb integration.Sandbox) {
745761
require.NoError(t, err)
746762
defer pid1.Wait()
747763

764+
files := []struct {
765+
index int
766+
path string
767+
data []byte
768+
}{
769+
{0, "/root-file", []byte(nil)},
770+
{1, "/foo-file", []byte(nil)},
771+
{2, "/local-file", []byte(`local`)},
772+
{3, "/cache-file", []byte(nil)},
773+
}
774+
for _, file := range files {
775+
cpath := containerMounts[file.index].Dest + file.path
776+
pid, err := ctr.Start(ctx, client.StartRequest{
777+
Args: []string{"test", "-f", cpath},
778+
})
779+
require.NoError(t, err, "cannot start container to check for file: %s", cpath)
780+
err = pid.Wait()
781+
require.NoError(t, err, "process for checking file failed: %s", cpath)
782+
783+
_, err = ctr.StatFile(ctx, client.StatContainerRequest{
784+
StatRequest: client.StatRequest{
785+
Path: file.path,
786+
},
787+
MountIndex: file.index,
788+
})
789+
require.NoError(t, err, "stat file for %q on mount %d failed", file.path, file.index)
790+
791+
b, err := ctr.ReadFile(ctx, client.ReadContainerRequest{
792+
ReadRequest: client.ReadRequest{
793+
Filename: file.path,
794+
},
795+
MountIndex: file.index,
796+
})
797+
require.NoError(t, err, "read file for %q on mount %d failed", file.path, file.index)
798+
require.Equal(t, file.data, b)
799+
}
800+
748801
pid, err := ctr.Start(ctx, client.StartRequest{
749802
Args: []string{"test", "-f", "/root-file"},
750803
})
@@ -1280,8 +1333,8 @@ func testClientSlowCacheRootfsRef(t *testing.T, sb integration.Sandbox) {
12801333
b := func(ctx context.Context, c client.Client) (*client.Result, error) {
12811334
id := identity.NewID()
12821335
input := llb.Scratch().File(
1283-
llb.Mkdir("/found", 0700).
1284-
Mkfile("/found/data", 0600, []byte(id)),
1336+
llb.Mkdir("/found", 0o700).
1337+
Mkfile("/found/data", 0o600, []byte(id)),
12851338
)
12861339

12871340
st := llb.Image("busybox:latest").Run(
@@ -1442,7 +1495,7 @@ func testClientGatewayExecError(t *testing.T, sb integration.Sandbox) {
14421495
"rootfs and readwrite mount",
14431496
llb.Image("busybox:latest").Run(
14441497
llb.Shlexf(`sh -c "echo %s > /data && echo %s > /rw/data && fail"`, id, id),
1445-
llb.AddMount("/rw", llb.Scratch().File(llb.Mkfile("foo", 0700, []byte(id)))),
1498+
llb.AddMount("/rw", llb.Scratch().File(llb.Mkfile("foo", 0o700, []byte(id)))),
14461499
).Root(),
14471500
2,
14481501
[]string{"/data", "/rw/data", "/rw/foo"},
@@ -1460,7 +1513,7 @@ func testClientGatewayExecError(t *testing.T, sb integration.Sandbox) {
14601513
llb.Shlexf(`sh -c "echo %s > /data && echo %s > /rw/data && fail"`, id, id),
14611514
llb.AddMount(
14621515
"/rw",
1463-
llb.Scratch().File(llb.Mkfile("foo", 0700, []byte(id))),
1516+
llb.Scratch().File(llb.Mkfile("foo", 0o700, []byte(id))),
14641517
llb.ForceNoOutput,
14651518
),
14661519
).Root(),
@@ -1579,8 +1632,8 @@ func testClientGatewaySlowCacheExecError(t *testing.T, sb integration.Sandbox) {
15791632

15801633
id := identity.NewID()
15811634
input := llb.Scratch().File(
1582-
llb.Mkdir("/found", 0700).
1583-
Mkfile("/found/data", 0600, []byte(id)),
1635+
llb.Mkdir("/found", 0o700).
1636+
Mkfile("/found/data", 0o600, []byte(id)),
15841637
)
15851638

15861639
st := llb.Image("busybox:latest").Run(
@@ -1691,17 +1744,17 @@ func testClientGatewayExecFileActionError(t *testing.T, sb integration.Sandbox)
16911744
}{{
16921745
"mkfile",
16931746
llb.Scratch().File(
1694-
llb.Mkdir("/found", 0700).
1695-
Mkfile("/found/foo", 0600, []byte(id)).
1696-
Mkfile("/notfound/foo", 0600, []byte(id)),
1747+
llb.Mkdir("/found", 0o700).
1748+
Mkfile("/found/foo", 0o600, []byte(id)).
1749+
Mkfile("/notfound/foo", 0o600, []byte(id)),
16971750
),
16981751
0, 3, "/input/found/foo",
16991752
}, {
17001753
"copy from input",
17011754
llb.Image("busybox").File(
17021755
llb.Copy(
17031756
llb.Scratch().File(
1704-
llb.Mkdir("/foo", 0600).Mkfile("/foo/bar", 0700, []byte(id)),
1757+
llb.Mkdir("/foo", 0o600).Mkfile("/foo/bar", 0o700, []byte(id)),
17051758
),
17061759
"/foo/bar",
17071760
"/notfound/baz",
@@ -1712,7 +1765,7 @@ func testClientGatewayExecFileActionError(t *testing.T, sb integration.Sandbox)
17121765
"copy from action",
17131766
llb.Image("busybox").File(
17141767
llb.Copy(
1715-
llb.Mkdir("/foo", 0600).Mkfile("/foo/bar", 0700, []byte(id)).WithState(llb.Scratch()),
1768+
llb.Mkdir("/foo", 0o600).Mkfile("/foo/bar", 0o700, []byte(id)).WithState(llb.Scratch()),
17161769
"/foo/bar",
17171770
"/notfound/baz",
17181771
),

control/gateway/gateway.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,30 @@ func (gwf *GatewayForwarder) ReleaseContainer(ctx context.Context, req *gwapi.Re
188188
return fwd.ReleaseContainer(ctx, req)
189189
}
190190

191+
func (gwf *GatewayForwarder) ReadFileContainer(ctx context.Context, req *gwapi.ReadFileRequest) (*gwapi.ReadFileResponse, error) {
192+
fwd, err := gwf.lookupForwarder(ctx)
193+
if err != nil {
194+
return nil, errors.Wrap(err, "forwarding ReadFileContainer")
195+
}
196+
return fwd.ReadFileContainer(ctx, req)
197+
}
198+
199+
func (gwf *GatewayForwarder) ReadDirContainer(ctx context.Context, req *gwapi.ReadDirRequest) (*gwapi.ReadDirResponse, error) {
200+
fwd, err := gwf.lookupForwarder(ctx)
201+
if err != nil {
202+
return nil, errors.Wrap(err, "forwarding ReadDirContainer")
203+
}
204+
return fwd.ReadDirContainer(ctx, req)
205+
}
206+
207+
func (gwf *GatewayForwarder) StatFileContainer(ctx context.Context, req *gwapi.StatFileRequest) (*gwapi.StatFileResponse, error) {
208+
fwd, err := gwf.lookupForwarder(ctx)
209+
if err != nil {
210+
return nil, errors.Wrap(err, "forwarding StatFileContainer")
211+
}
212+
return fwd.StatFileContainer(ctx, req)
213+
}
214+
191215
func (gwf *GatewayForwarder) ExecProcess(srv gwapi.LLBBridge_ExecProcessServer) error {
192216
fwd, err := gwf.lookupForwarder(srv.Context())
193217
if err != nil {

frontend/gateway/client/client.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ type Mount struct {
6666
type Container interface {
6767
Start(context.Context, StartRequest) (ContainerProcess, error)
6868
Release(context.Context) error
69+
ReadFile(ctx context.Context, req ReadContainerRequest) ([]byte, error)
70+
StatFile(ctx context.Context, req StatContainerRequest) (*fstypes.Stat, error)
71+
ReadDir(ctx context.Context, req ReadDirContainerRequest) ([]*fstypes.Stat, error)
6972
}
7073

7174
// StartRequest encapsulates the arguments to define a process within a
@@ -111,6 +114,11 @@ type ReadRequest struct {
111114
Range *FileRange
112115
}
113116

117+
type ReadContainerRequest struct {
118+
ReadRequest
119+
MountIndex int
120+
}
121+
114122
type FileRange struct {
115123
Offset int
116124
Length int
@@ -121,10 +129,20 @@ type ReadDirRequest struct {
121129
IncludePattern string
122130
}
123131

132+
type ReadDirContainerRequest struct {
133+
ReadDirRequest
134+
MountIndex int
135+
}
136+
124137
type StatRequest struct {
125138
Path string
126139
}
127140

141+
type StatContainerRequest struct {
142+
StatRequest
143+
MountIndex int
144+
}
145+
128146
// SolveRequest is same as frontend.SolveRequest but avoiding dependency
129147
type SolveRequest struct {
130148
Evaluate bool

0 commit comments

Comments
 (0)