Skip to content

Commit 80f1a5d

Browse files
committed
mounter
1 parent dcabd28 commit 80f1a5d

23 files changed

Lines changed: 1429 additions & 73 deletions

‎core/mock/gen.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//go:generate mockgen -typed -destination=container/engine.go -source=../pkg/container/engine.go
66
//go:generate mockgen -typed -destination=sandboxer/engine.go -source=../pkg/sandboxer/engine.go
77
//go:generate mockgen -typed -destination=sandboxer/sandbox.go -source=../pkg/sandboxer/sandbox.go
8+
//go:generate mockgen -typed -destination=sandboxer/mounter.go -source=../pkg/sandboxer/mounter.go
89
//go:generate mockgen -typed -destination=command/console.go -source=../pkg/command/console.go
910
//go:generate mockgen -typed -destination=command/file.go -source=../pkg/command/file.go
1011
//go:generate mockgen -typed -destination=command/cmdtypes/types.go -source=../pkg/command/cmdtypes/types.go

‎core/mock/sandboxer/mounter.go‎

Lines changed: 118 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎core/pkg/executor/job_executor.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,11 @@ func (e *jobExecutor) initializeSandbox(ctx context.Context, s *scribe.Scribe) e
301301
if err = xdig.Supply(e.scope, resp.Sandbox, dig.Export(true)); err != nil {
302302
return fmt.Errorf("supply 'sandbox': %w", err)
303303
}
304+
if resp.Mounter != nil {
305+
if err = xdig.Supply(e.scope, resp.Mounter, dig.Export(true)); err != nil {
306+
return fmt.Errorf("supply 'mounter': %w", err)
307+
}
308+
}
304309
}
305310

306311
if location, err := e.setupEventFile(ctx); err != nil {

‎core/pkg/sandboxer/container/engine.go‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,13 @@ func (e *containerizedEngine) Launch(ctx context.Context, req *sandboxer.LaunchR
6262

6363
var jobSb sandboxer.Sandbox
6464
if req.JobContainer != nil {
65-
if resp.JobContainer, err = b.RunJobContainer(ctx, req.JobContainer, addSandboxMounts(sb)); err != nil {
65+
targetLayout := DefaultLayout
66+
mountOpt := addMounterMounts(resp.Mounter, sb, targetLayout)
67+
68+
if resp.JobContainer, err = b.RunJobContainer(ctx, req.JobContainer, mountOpt); err != nil {
6669
return
6770
}
68-
if jobSb, err = NewSandbox(ctx, client, resp.JobContainer.Id, DefaultLayout); err != nil {
71+
if jobSb, err = NewSandbox(ctx, client, resp.JobContainer.Id, targetLayout); err != nil {
6972
return
7073
}
7174
}

‎core/pkg/sandboxer/container/engine_test.go‎

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,67 @@ func (s *ContainerizedEngineTestSuite) TestLaunch() {
129129
s.Require().NoError(resp.Sandbox.Terminate(s.T().Context()))
130130
})
131131

132+
s.Run("with job container and mounter overlay mounts", func() {
133+
req := &sandboxer.LaunchRequest{
134+
Forge: s.forge,
135+
JobContainer: &workflows.Container{Image: "alpine"},
136+
}
137+
138+
customMounts := []*types.Mount{
139+
{Type: "bind", Source: "/host/workspace", Target: "/job/workspace"},
140+
{Type: "bind", Source: "/host/temp", Target: "/job/temp"},
141+
{Type: "bind", Source: "/host/runtimes/node", Target: "/job/runtimes/node"},
142+
}
143+
mounter := mock_sandboxer.NewMockMounter(s.ctrl)
144+
mounter.EXPECT().OverlayMounts(gomock.Any(), true).Return(customMounts)
145+
146+
s.mockEng.EXPECT().Launch(gomock.Any(), req).Return(&sandboxer.LaunchResponse{
147+
Sandbox: s.mockSb,
148+
Mounter: mounter,
149+
ContainerEngine: func(context.Context) (c.Engine, error) {
150+
return s.mockClient, nil
151+
},
152+
}, nil)
153+
154+
s.mockSb.EXPECT().Layout().Return(sandboxer.StandardLayout("/opt/drassi/")).AnyTimes()
155+
156+
s.mockClient.EXPECT().NetworkCreate(gomock.Any(), gomock.Any()).Return("net-123", nil)
157+
s.mockClient.EXPECT().Address().Return("unix:///var/run/docker.sock")
158+
s.mockClient.EXPECT().ImagePull(gomock.Any(), "alpine", gomock.Any()).Return(nil)
159+
160+
var capturedSpec *types.ContainerSpec
161+
s.mockClient.EXPECT().ContainerRun(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
162+
func(_ context.Context, spec *types.ContainerSpec, _ *c.RunOptions) (string, error) {
163+
capturedSpec = spec
164+
return "c-123", nil
165+
},
166+
)
167+
168+
s.mockClient.EXPECT().CopyIn(gomock.Any(), "c-123", gomock.Any()).Return(nil)
169+
s.mockClient.EXPECT().ContainerInspect(gomock.Any(), "c-123").Return(&types.ContainerSpec{
170+
Environment: map[string]string{"PATH": "/bin"},
171+
}, nil)
172+
173+
resp, err := s.eng.Launch(s.T().Context(), req)
174+
s.Require().NoError(err)
175+
s.Require().NotNil(resp)
176+
s.Require().NotNil(resp.JobContainer)
177+
s.Require().Equal("c-123", resp.JobContainer.Id)
178+
179+
s.Require().NotNil(capturedSpec)
180+
for _, m := range customMounts {
181+
s.Require().Contains(capturedSpec.Mounts, m)
182+
}
183+
184+
s.mockClient.EXPECT().ContainerRemove(gomock.Any(), &c.RemoveOptions{Id: "c-123"}).Return(nil)
185+
s.mockClient.EXPECT().ContainerRemove(gomock.Any(), &c.RemoveOptions{Labels: s.forge.WellKnownLabels()}).Return(nil)
186+
s.mockClient.EXPECT().VolumeRemove(gomock.Any(), &c.RemoveOptions{Labels: s.forge.WellKnownLabels()}).Return(nil)
187+
s.mockClient.EXPECT().NetworkRemove(gomock.Any(), &c.RemoveOptions{Id: "net-123"}).Return(nil)
188+
s.mockSb.EXPECT().Terminate(gomock.Any()).Return(nil)
189+
190+
s.Require().NoError(resp.Sandbox.Terminate(s.T().Context()))
191+
})
192+
132193
s.Run("with service containers only", func() {
133194
req := &sandboxer.LaunchRequest{
134195
Forge: s.forge,
@@ -201,3 +262,64 @@ func (s *ContainerizedEngineTestSuite) TestLaunch() {
201262
s.Require().ErrorContains(err, "docker daemon down")
202263
})
203264
}
265+
266+
func (s *ContainerizedEngineTestSuite) TestAddMounterMounts() {
267+
target := DefaultLayout
268+
269+
s.Run("with mounter", func() {
270+
mounterMounts := []*types.Mount{
271+
{Type: "bind", Source: "/src/w", Target: target.Workspace()},
272+
{Type: "bind", Source: "/src/r", Target: target.Runtimes()},
273+
}
274+
mounter := mock_sandboxer.NewMockMounter(s.ctrl)
275+
mounter.EXPECT().OverlayMounts(target, true).Return(mounterMounts)
276+
opt := addMounterMounts(mounter, nil, target)
277+
spec := &types.ContainerSpec{}
278+
err := opt(spec)
279+
s.Require().NoError(err)
280+
s.Require().Equal(mounterMounts, spec.Mounts)
281+
})
282+
283+
s.Run("with nil mounter and sandbox", func() {
284+
mockLayout := sandboxer.StandardLayout("/sb")
285+
sb := mock_sandboxer.NewMockSandbox(s.ctrl)
286+
sb.EXPECT().Layout().Return(mockLayout).AnyTimes()
287+
288+
opt := addMounterMounts(nil, sb, target)
289+
spec := &types.ContainerSpec{}
290+
err := opt(spec)
291+
s.Require().NoError(err)
292+
expected := []*types.Mount{
293+
{Type: "bind", Source: mockLayout.Workspace(), Target: target.Workspace()},
294+
{Type: "bind", Source: mockLayout.Temp(), Target: target.Temp()},
295+
}
296+
s.Require().Equal(expected, spec.Mounts)
297+
})
298+
299+
s.Run("with nil mounter and nil sandbox", func() {
300+
opt := addMounterMounts(nil, nil, target)
301+
spec := &types.ContainerSpec{}
302+
err := opt(spec)
303+
s.Require().NoError(err)
304+
expected := []*types.Mount{
305+
{Type: "volume", Target: DefaultJobDir},
306+
}
307+
s.Require().Equal(expected, spec.Mounts)
308+
})
309+
310+
s.Run("addSandboxMounts delegator", func() {
311+
mockLayout := sandboxer.StandardLayout("/sb")
312+
sb := mock_sandboxer.NewMockSandbox(s.ctrl)
313+
sb.EXPECT().Layout().Return(mockLayout).AnyTimes()
314+
315+
opt := addSandboxMounts(sb)
316+
spec := &types.ContainerSpec{}
317+
err := opt(spec)
318+
s.Require().NoError(err)
319+
expected := []*types.Mount{
320+
{Type: "bind", Source: mockLayout.Workspace(), Target: target.Workspace()},
321+
{Type: "bind", Source: mockLayout.Temp(), Target: target.Temp()},
322+
}
323+
s.Require().Equal(expected, spec.Mounts)
324+
})
325+
}

‎core/pkg/sandboxer/container/options.go‎

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -86,28 +86,21 @@ func SetNetwork(netId string) Option {
8686
}
8787

8888
func addSandboxMounts(sb sandboxer.Sandbox) Option {
89-
mounts := make([]*types.Mount, 0)
90-
if sb == nil {
91-
m := &types.Mount{
92-
Type: "volume",
93-
Source: "", // anonymous volume
94-
Target: DefaultJobDir,
89+
return addMounterMounts(nil, sb, DefaultLayout)
90+
}
91+
92+
func addMounterMounts(mounter sandboxer.Mounter, sb sandboxer.Sandbox, targetLayout sandboxer.Layout) Option {
93+
var mounts []*types.Mount
94+
if mounter != nil {
95+
mounts = mounter.OverlayMounts(targetLayout, true)
96+
} else if sb != nil {
97+
mounts = []*types.Mount{
98+
{Type: "bind", Source: sb.Layout().Workspace(), Target: targetLayout.Workspace()},
99+
{Type: "bind", Source: sb.Layout().Temp(), Target: targetLayout.Temp()},
95100
}
96-
mounts = append(mounts, m)
97101
} else {
98-
layout := sb.Layout()
99-
containerLayout := DefaultLayout
100-
dir := map[string]string{
101-
containerLayout.Workspace(): layout.Workspace(),
102-
containerLayout.Temp(): layout.Temp(),
103-
}
104-
for k, v := range dir {
105-
m := &types.Mount{
106-
Type: "bind",
107-
Source: v,
108-
Target: k,
109-
}
110-
mounts = append(mounts, m)
102+
mounts = []*types.Mount{
103+
{Type: "volume", Target: DefaultJobDir},
111104
}
112105
}
113106

@@ -117,6 +110,20 @@ func addSandboxMounts(sb sandboxer.Sandbox) Option {
117110
}
118111
}
119112

113+
func MountJobVolume(volume, target string) Option {
114+
if target == "" {
115+
target = DefaultJobDir
116+
}
117+
return func(spec *types.ContainerSpec) error {
118+
spec.Mounts = append(spec.Mounts, &types.Mount{
119+
Type: "volume",
120+
Source: volume,
121+
Target: target,
122+
})
123+
return nil
124+
}
125+
}
126+
120127
func MountApiSocket(c container.Engine) Option {
121128
socket := c.Address()
122129
if proto, loc, ok := strings.Cut(socket, "://"); ok {

‎core/pkg/sandboxer/container/sandbox_test.go‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,22 @@ func TestExportedHelpers(t *testing.T) {
5353
require.NoError(t, cu(context.Background()))
5454
require.True(t, called)
5555
})
56+
57+
t.Run("MountJobVolume adds volume mount", func(t *testing.T) {
58+
spec := &types.ContainerSpec{}
59+
opt := MountJobVolume("", "")
60+
require.NoError(t, opt(spec))
61+
require.Len(t, spec.Mounts, 1)
62+
require.Equal(t, "volume", spec.Mounts[0].Type)
63+
require.Equal(t, "", spec.Mounts[0].Source)
64+
require.Equal(t, DefaultJobDir, spec.Mounts[0].Target)
65+
66+
specCustom := &types.ContainerSpec{}
67+
optCustom := MountJobVolume("my-vol", "/custom/dir")
68+
require.NoError(t, optCustom(specCustom))
69+
require.Len(t, specCustom.Mounts, 1)
70+
require.Equal(t, "volume", specCustom.Mounts[0].Type)
71+
require.Equal(t, "my-vol", specCustom.Mounts[0].Source)
72+
require.Equal(t, "/custom/dir", specCustom.Mounts[0].Target)
73+
})
5674
}

‎core/pkg/sandboxer/docker/engine.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ func (e *engine) Launch(ctx context.Context, req *sandboxer.LaunchRequest) (resp
8282
resp = nil
8383
}(ctx)
8484

85+
jobVol := req.Forge.CanonicalName()
8586
layout := container.DefaultLayout
8687
spec, err := e.resolveTemplate(req,
88+
container.MountJobVolume(jobVol, container.DefaultJobDir),
8789
container.SetNetwork(netId),
8890
container.SetLabels(labels),
8991
container.SetWorkdir(layout.Workspace()),
@@ -119,6 +121,7 @@ func (e *engine) Launch(ctx context.Context, req *sandboxer.LaunchRequest) (resp
119121
Id: conId,
120122
Network: netId,
121123
},
124+
Mounter: newMounter(jobVol),
122125
}
123126

124127
if len(req.ServiceContainers) > 0 {

0 commit comments

Comments
 (0)