Skip to content

Commit

Permalink
Add BindMount handler for Linux
Browse files Browse the repository at this point in the history
Add handleBindMount to remove consistency entity\nRevise comments for linting and clarity

Signed-off-by: Sam Chew <[email protected]>
  • Loading branch information
chews93319 committed Oct 2, 2024
1 parent c004516 commit 6f4f775
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 150 deletions.
64 changes: 64 additions & 0 deletions cmd/finch/nerdctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ var argHandlerMap = map[string]map[string]argHandler{
"image build": {
"--load": handleDockerBuildLoad,
},
"container run": {
"--mount": handleBindMounts,
},
}

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

return nil
}

// handles the argument & value of --mount option
//
// invokes OS specific path handler for source path of the bind mount
// and removes the consistency key-value entity from value
func handleBindMounts(systemDeps NerdctlCommandSystemDeps, _ *config.Finch, nerdctlCmdArgs []string, index int) error {
prefix := nerdctlCmdArgs[index]
var (
v string
found bool
before string
)
if strings.Contains(nerdctlCmdArgs[index], "=") {
before, v, found = strings.Cut(prefix, "=")
} else {
if (index + 1) < len(nerdctlCmdArgs) {
v = nerdctlCmdArgs[index+1]
} else {
return fmt.Errorf("invalid positional parameter for %s", prefix)
}
}

// eg --mount type=bind,source="$(pwd)"/target,target=/app,readonly
// eg --mount type=bind, source=${pwd}/source_dir, target=<path>/target_dir, consistency=cached
// https://docs.docker.com/storage/bind-mounts/#choose-the--v-or---mount-flag order does not matter, so convert to a map
entries := strings.Split(v, ",")
m := make(map[string]string)
ro := []string{}
for _, e := range entries {
parts := strings.Split(e, "=")
if len(parts) < 2 {
ro = append(ro, parts...)
} else {
m[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
}
// Check if type is bind mount, else return
if m["type"] != "bind" {
return nil
}

// Remove 'consistency' key-value pair, if present
delete(m, "consistency")

// Invoke the OS specific path handler
handleBindMountPath(systemDeps, m)

Check failure on line 390 in cmd/finch/nerdctl.go

View workflow job for this annotation

GitHub Actions / lint

Error return value is not checked (errcheck)

// Convert to string representation
s := mapToString(m)
// append read-only key if present
if len(ro) > 0 {
s = s + "," + strings.Join(ro, ",")
}
if found {
nerdctlCmdArgs[index] = fmt.Sprintf("%s=%s", before, s)
} else {
nerdctlCmdArgs[index+1] = s
}

return nil
}
67 changes: 3 additions & 64 deletions cmd/finch/nerdctl_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/lima-vm/lima/pkg/networks"

"github.com/runfinch/finch/pkg/command"
"github.com/runfinch/finch/pkg/config"
"github.com/runfinch/finch/pkg/flog"
)

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

var osAliasMap = map[string]string{}

var osArgHandlerMap = map[string]map[string]argHandler{
"container run": {
"--mount": handleBindMounts,
},
}
var osArgHandlerMap = map[string]map[string]argHandler{}

var osCommandHandlerMap = map[string]commandHandler{}

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

// removes the consistency key-value entity from --mount.
func handleBindMounts(_ NerdctlCommandSystemDeps, _ *config.Finch, nerdctlCmdArgs []string, index int) error {
prefix := nerdctlCmdArgs[index]
var (
v string
found bool
before string
)
if strings.Contains(nerdctlCmdArgs[index], "=") {
before, v, found = strings.Cut(prefix, "=")
} else {
if (index + 1) < len(nerdctlCmdArgs) {
v = nerdctlCmdArgs[index+1]
} else {
return fmt.Errorf("invalid positional parameter for %s", prefix)
}
}

// This is where the 'consistency=cached' strings should be removed....
// "consistency will be one of the keys in the following map"

// eg --mount type=bind,source="$(pwd)"/target,target=/app,readonly
// eg --mount type=bind,
// source=/Users/stchew/projs/arbtest_devcontainers_extensions,
// target=/workspaces/arbtest_devcontainers_extensions,
// consistency=cached
// https://docs.docker.com/storage/bind-mounts/#choose-the--v-or---mount-flag order does not matter, so convert to a map
entries := strings.Split(v, ",")
m := make(map[string]string)
ro := []string{}
for _, e := range entries {
parts := strings.Split(e, "=")
if len(parts) < 2 {
ro = append(ro, parts...)
} else {
m[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
}
// Check if type is bind mount, else return
if m["type"] != "bind" {
return nil
}

// Remove 'consistency' key-value pair
delete(m, "consistency")

// Convert to string representation
s := mapToString(m)
// append read-only key if present
if len(ro) > 0 {
s = s + "," + strings.Join(ro, ",")
}
if found {
nerdctlCmdArgs[index] = fmt.Sprintf("%s=%s", before, s)
} else {
nerdctlCmdArgs[index+1] = s
}

func handleBindMountPath(_ NerdctlCommandSystemDeps, _ map[string]string) error {
// Do nothing by default
return nil
}

Expand Down
86 changes: 86 additions & 0 deletions cmd/finch/nerdctl_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/golang/mock/gomock"
Expand Down Expand Up @@ -1208,6 +1209,36 @@ func TestNerdctlCommand_run(t *testing.T) {
c.EXPECT().Run()
},
},
{
name: "bindmount with src and consistency",
cmdName: "run",
fc: &config.Finch{},
args: []string{"--mount", "type=bind,src=./src,consistency=cached", "alpine:latest"},
wantErr: nil,
mockSvc: func(
_ *testing.T,
lcc *mocks.NerdctlCmdCreator,
_ *mocks.CommandCreator,
ncsd *mocks.NerdctlCommandSystemDeps,
logger *mocks.Logger,
ctrl *gomock.Controller,
_ afero.Fs,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "Running")
ncsd.EXPECT().LookupEnv("AWS_ACCESS_KEY_ID").Return("", false)
ncsd.EXPECT().LookupEnv("AWS_SECRET_ACCESS_KEY").Return("", false)
ncsd.EXPECT().LookupEnv("AWS_SESSION_TOKEN").Return("", false)
ncsd.EXPECT().LookupEnv("COSIGN_PASSWORD").Return("", false)
ncsd.EXPECT().LookupEnv("COMPOSE_FILE").Return("", false)
c := mocks.NewCommand(ctrl)
lcc.EXPECT().Create("shell", limaInstanceName, "sudo", "-E", nerdctlCmdName, "container", "run",
"--mount", ContainsMultipleStrs([]string{"bind", "type", "!consistency"}), "alpine:latest").Return(c)
c.EXPECT().Run()
},
},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -1664,3 +1695,58 @@ func TestNerdctlCommand_run_miscCommand(t *testing.T) {
})
}
}

type ContainsSubstring struct {
substr string
}

func (m *ContainsSubstring) Matches(x interface{}) bool {
s, ok := x.(string)
if !ok {
return false
}
return strings.Contains(s, m.substr)
}

func (m *ContainsSubstring) String() string {
return fmt.Sprintf("contains substring %q", m.substr)
}

func ContainsStr(substr string) gomock.Matcher {
return &ContainsSubstring{substr: substr}
}

type ContainsMultipleSubstrings struct {
substrs []string
}

func (m *ContainsMultipleSubstrings) Matches(x interface{}) bool {
s, ok := x.(string)
if !ok {
return false
}
// Check if each substrings is present in the input string
// except strings that start with "!"
passTest := true
for _, substr := range m.substrs {
if substr[0] == '!' {
if strings.Contains(s, substr[1:]) {
passTest = false
}
continue
}

if !strings.Contains(s, substr) {
passTest = false
}
}
return passTest
}

func (m *ContainsMultipleSubstrings) String() string {
return fmt.Sprintf("contains substrings %q", m.substrs)
}

func ContainsMultipleStrs(substrs []string) gomock.Matcher {
return &ContainsMultipleSubstrings{substrs: substrs}
}
27 changes: 19 additions & 8 deletions cmd/finch/nerdctl_native.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
)

func (nc *nerdctlCommand) run(cmdName string, args []string) error {

var (
hasCmdHandler, hasArgHandler bool
cmdHandler commandHandler
Expand Down Expand Up @@ -70,14 +69,12 @@ func (nc *nerdctlCommand) run(cmdName string, args []string) error {
}
}

// TODO: Extra manipulation if overwriting cmdName with alias
//splitName := strings.Split(cmdName, " ")
//cmdArgs := append([]string{splitName[0]}, splitName[1:]...)
//cmdArgs = append(cmdArgs, args...)

cmdArgs := append([]string{cmdName}, args...)
// Extra manipulation for cases that overwrite cmdName with alias
splitName := strings.Split(cmdName, " ")
cmdArgs := append([]string{splitName[0]}, splitName[1:]...)
cmdArgs = append(cmdArgs, args...)

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

var osCommandHandlerMap = map[string]commandHandler{}

func mapToString(m map[string]string) string {
var parts []string
for k, v := range m {
part := fmt.Sprintf("%s=%s", k, v)
parts = append(parts, part)
}
return strings.Join(parts, ",")
}

func handleBindMountPath(_ NerdctlCommandSystemDeps, _ map[string]string) error {
// Do nothing by default
return nil
}
Loading

0 comments on commit 6f4f775

Please sign in to comment.