Skip to content

Commit a49df29

Browse files
authored
feat: Add BindMount handler for Linux (runfinch#1119)
Add BindMount handler for Linux Add handleBindMount to remove consistency entity\nRevise comments for linting and clarity Signed-off-by: Sam Chew <stchew@amazon.com>
1 parent c004516 commit a49df29

File tree

5 files changed

+198
-150
lines changed

5 files changed

+198
-150
lines changed

cmd/finch/nerdctl.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ var argHandlerMap = map[string]map[string]argHandler{
218218
"image build": {
219219
"--load": handleDockerBuildLoad,
220220
},
221+
"container run": {
222+
"--mount": handleBindMounts,
223+
},
221224
}
222225

223226
var cmdFlagSetMap = map[string]map[string]sets.Set[string]{
@@ -339,3 +342,67 @@ func handleDockerCompatInspect(_ NerdctlCommandSystemDeps, fc *config.Finch, cmd
339342

340343
return nil
341344
}
345+
346+
// handles the argument & value of --mount option
347+
//
348+
// invokes OS specific path handler for source path of the bind mount
349+
// and removes the consistency key-value entity from value
350+
func handleBindMounts(systemDeps NerdctlCommandSystemDeps, _ *config.Finch, nerdctlCmdArgs []string, index int) error {
351+
prefix := nerdctlCmdArgs[index]
352+
var (
353+
v string
354+
found bool
355+
before string
356+
)
357+
if strings.Contains(nerdctlCmdArgs[index], "=") {
358+
before, v, found = strings.Cut(prefix, "=")
359+
} else {
360+
if (index + 1) < len(nerdctlCmdArgs) {
361+
v = nerdctlCmdArgs[index+1]
362+
} else {
363+
return fmt.Errorf("invalid positional parameter for %s", prefix)
364+
}
365+
}
366+
367+
// eg --mount type=bind,source="$(pwd)"/target,target=/app,readonly
368+
// eg --mount type=bind, source=${pwd}/source_dir, target=<path>/target_dir, consistency=cached
369+
// https://docs.docker.com/storage/bind-mounts/#choose-the--v-or---mount-flag order does not matter, so convert to a map
370+
entries := strings.Split(v, ",")
371+
m := make(map[string]string)
372+
ro := []string{}
373+
for _, e := range entries {
374+
parts := strings.Split(e, "=")
375+
if len(parts) < 2 {
376+
ro = append(ro, parts...)
377+
} else {
378+
m[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
379+
}
380+
}
381+
// Check if type is bind mount, else return
382+
if m["type"] != "bind" {
383+
return nil
384+
}
385+
386+
// Remove 'consistency' key-value pair, if present
387+
delete(m, "consistency")
388+
389+
// Invoke the OS specific path handler
390+
err := handleBindMountPath(systemDeps, m)
391+
if err != nil {
392+
return err
393+
}
394+
395+
// Convert to string representation
396+
s := mapToString(m)
397+
// append read-only key if present
398+
if len(ro) > 0 {
399+
s = s + "," + strings.Join(ro, ",")
400+
}
401+
if found {
402+
nerdctlCmdArgs[index] = fmt.Sprintf("%s=%s", before, s)
403+
} else {
404+
nerdctlCmdArgs[index+1] = s
405+
}
406+
407+
return nil
408+
}

cmd/finch/nerdctl_darwin.go

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/lima-vm/lima/pkg/networks"
1414

1515
"github.com/runfinch/finch/pkg/command"
16-
"github.com/runfinch/finch/pkg/config"
1716
"github.com/runfinch/finch/pkg/flog"
1817
)
1918

@@ -23,11 +22,7 @@ func convertToWSLPath(_ NerdctlCommandSystemDeps, _ string) (string, error) {
2322

2423
var osAliasMap = map[string]string{}
2524

26-
var osArgHandlerMap = map[string]map[string]argHandler{
27-
"container run": {
28-
"--mount": handleBindMounts,
29-
},
30-
}
25+
var osArgHandlerMap = map[string]map[string]argHandler{}
3126

3227
var osCommandHandlerMap = map[string]commandHandler{}
3328

@@ -50,64 +45,8 @@ func resolveIP(host string, logger flog.Logger, _ command.Creator) (string, erro
5045
return host, nil
5146
}
5247

53-
// removes the consistency key-value entity from --mount.
54-
func handleBindMounts(_ NerdctlCommandSystemDeps, _ *config.Finch, nerdctlCmdArgs []string, index int) error {
55-
prefix := nerdctlCmdArgs[index]
56-
var (
57-
v string
58-
found bool
59-
before string
60-
)
61-
if strings.Contains(nerdctlCmdArgs[index], "=") {
62-
before, v, found = strings.Cut(prefix, "=")
63-
} else {
64-
if (index + 1) < len(nerdctlCmdArgs) {
65-
v = nerdctlCmdArgs[index+1]
66-
} else {
67-
return fmt.Errorf("invalid positional parameter for %s", prefix)
68-
}
69-
}
70-
71-
// This is where the 'consistency=cached' strings should be removed....
72-
// "consistency will be one of the keys in the following map"
73-
74-
// eg --mount type=bind,source="$(pwd)"/target,target=/app,readonly
75-
// eg --mount type=bind,
76-
// source=/Users/stchew/projs/arbtest_devcontainers_extensions,
77-
// target=/workspaces/arbtest_devcontainers_extensions,
78-
// consistency=cached
79-
// https://docs.docker.com/storage/bind-mounts/#choose-the--v-or---mount-flag order does not matter, so convert to a map
80-
entries := strings.Split(v, ",")
81-
m := make(map[string]string)
82-
ro := []string{}
83-
for _, e := range entries {
84-
parts := strings.Split(e, "=")
85-
if len(parts) < 2 {
86-
ro = append(ro, parts...)
87-
} else {
88-
m[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
89-
}
90-
}
91-
// Check if type is bind mount, else return
92-
if m["type"] != "bind" {
93-
return nil
94-
}
95-
96-
// Remove 'consistency' key-value pair
97-
delete(m, "consistency")
98-
99-
// Convert to string representation
100-
s := mapToString(m)
101-
// append read-only key if present
102-
if len(ro) > 0 {
103-
s = s + "," + strings.Join(ro, ",")
104-
}
105-
if found {
106-
nerdctlCmdArgs[index] = fmt.Sprintf("%s=%s", before, s)
107-
} else {
108-
nerdctlCmdArgs[index+1] = s
109-
}
110-
48+
func handleBindMountPath(_ NerdctlCommandSystemDeps, _ map[string]string) error {
49+
// Do nothing by default
11150
return nil
11251
}
11352

cmd/finch/nerdctl_darwin_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"os"
1212
"path/filepath"
13+
"strings"
1314
"testing"
1415

1516
"github.com/golang/mock/gomock"
@@ -1208,6 +1209,36 @@ func TestNerdctlCommand_run(t *testing.T) {
12081209
c.EXPECT().Run()
12091210
},
12101211
},
1212+
{
1213+
name: "bindmount with src and consistency",
1214+
cmdName: "run",
1215+
fc: &config.Finch{},
1216+
args: []string{"--mount", "type=bind,src=./src,consistency=cached", "alpine:latest"},
1217+
wantErr: nil,
1218+
mockSvc: func(
1219+
_ *testing.T,
1220+
lcc *mocks.NerdctlCmdCreator,
1221+
_ *mocks.CommandCreator,
1222+
ncsd *mocks.NerdctlCommandSystemDeps,
1223+
logger *mocks.Logger,
1224+
ctrl *gomock.Controller,
1225+
_ afero.Fs,
1226+
) {
1227+
getVMStatusC := mocks.NewCommand(ctrl)
1228+
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
1229+
getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil)
1230+
logger.EXPECT().Debugf("Status of virtual machine: %s", "Running")
1231+
ncsd.EXPECT().LookupEnv("AWS_ACCESS_KEY_ID").Return("", false)
1232+
ncsd.EXPECT().LookupEnv("AWS_SECRET_ACCESS_KEY").Return("", false)
1233+
ncsd.EXPECT().LookupEnv("AWS_SESSION_TOKEN").Return("", false)
1234+
ncsd.EXPECT().LookupEnv("COSIGN_PASSWORD").Return("", false)
1235+
ncsd.EXPECT().LookupEnv("COMPOSE_FILE").Return("", false)
1236+
c := mocks.NewCommand(ctrl)
1237+
lcc.EXPECT().Create("shell", limaInstanceName, "sudo", "-E", nerdctlCmdName, "container", "run",
1238+
"--mount", ContainsMultipleStrs([]string{"bind", "type", "!consistency"}), "alpine:latest").Return(c)
1239+
c.EXPECT().Run()
1240+
},
1241+
},
12111242
}
12121243

12131244
for _, tc := range testCases {
@@ -1664,3 +1695,58 @@ func TestNerdctlCommand_run_miscCommand(t *testing.T) {
16641695
})
16651696
}
16661697
}
1698+
1699+
type ContainsSubstring struct {
1700+
substr string
1701+
}
1702+
1703+
func (m *ContainsSubstring) Matches(x interface{}) bool {
1704+
s, ok := x.(string)
1705+
if !ok {
1706+
return false
1707+
}
1708+
return strings.Contains(s, m.substr)
1709+
}
1710+
1711+
func (m *ContainsSubstring) String() string {
1712+
return fmt.Sprintf("contains substring %q", m.substr)
1713+
}
1714+
1715+
func ContainsStr(substr string) gomock.Matcher {
1716+
return &ContainsSubstring{substr: substr}
1717+
}
1718+
1719+
type ContainsMultipleSubstrings struct {
1720+
substrs []string
1721+
}
1722+
1723+
func (m *ContainsMultipleSubstrings) Matches(x interface{}) bool {
1724+
s, ok := x.(string)
1725+
if !ok {
1726+
return false
1727+
}
1728+
// Check if each substrings is present in the input string
1729+
// except strings that start with "!"
1730+
passTest := true
1731+
for _, substr := range m.substrs {
1732+
if substr[0] == '!' {
1733+
if strings.Contains(s, substr[1:]) {
1734+
passTest = false
1735+
}
1736+
continue
1737+
}
1738+
1739+
if !strings.Contains(s, substr) {
1740+
passTest = false
1741+
}
1742+
}
1743+
return passTest
1744+
}
1745+
1746+
func (m *ContainsMultipleSubstrings) String() string {
1747+
return fmt.Sprintf("contains substrings %q", m.substrs)
1748+
}
1749+
1750+
func ContainsMultipleStrs(substrs []string) gomock.Matcher {
1751+
return &ContainsMultipleSubstrings{substrs: substrs}
1752+
}

cmd/finch/nerdctl_native.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
)
1717

1818
func (nc *nerdctlCommand) run(cmdName string, args []string) error {
19-
2019
var (
2120
hasCmdHandler, hasArgHandler bool
2221
cmdHandler commandHandler
@@ -70,14 +69,12 @@ func (nc *nerdctlCommand) run(cmdName string, args []string) error {
7069
}
7170
}
7271

73-
// TODO: Extra manipulation if overwriting cmdName with alias
74-
//splitName := strings.Split(cmdName, " ")
75-
//cmdArgs := append([]string{splitName[0]}, splitName[1:]...)
76-
//cmdArgs = append(cmdArgs, args...)
77-
78-
cmdArgs := append([]string{cmdName}, args...)
72+
// Extra manipulation for cases that overwrite cmdName with alias
73+
splitName := strings.Split(cmdName, " ")
74+
cmdArgs := append([]string{splitName[0]}, splitName[1:]...)
75+
cmdArgs = append(cmdArgs, args...)
7976

80-
if nc.shouldReplaceForHelp(cmdName, args) {
77+
if nc.shouldReplaceForHelp(splitName[0], args) {
8178
return nc.ncc.RunWithReplacingStdout(
8279
[]command.Replacement{{Source: "nerdctl", Target: "finch"}},
8380
cmdArgs...,
@@ -92,3 +89,17 @@ var osAliasMap = map[string]string{}
9289
var osArgHandlerMap = map[string]map[string]argHandler{}
9390

9491
var osCommandHandlerMap = map[string]commandHandler{}
92+
93+
func mapToString(m map[string]string) string {
94+
var parts []string
95+
for k, v := range m {
96+
part := fmt.Sprintf("%s=%s", k, v)
97+
parts = append(parts, part)
98+
}
99+
return strings.Join(parts, ",")
100+
}
101+
102+
func handleBindMountPath(_ NerdctlCommandSystemDeps, _ map[string]string) error {
103+
// Do nothing by default
104+
return nil
105+
}

0 commit comments

Comments
 (0)