Skip to content

Commit c004516

Browse files
authored
feat: Add Dockercompat Mode & DevContainer Support (runfinch#1100)
* Port changes from Shubham fork Signed-off-by: Sam Chew <[email protected]> * Add DevContainers functionality to Finch Signed-off-by: Sam Chew <[email protected]> --------- Signed-off-by: Sam Chew <[email protected]>
1 parent befa9bb commit c004516

8 files changed

+706
-93
lines changed

Diff for: cmd/finch/main.go

+7
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,12 @@ func initializeNerdctlCommands(
4444
for cmdName, cmdDescription := range nerdctlCmds {
4545
allNerdctlCommands = append(allNerdctlCommands, nerdctlCommandCreator.create(cmdName, cmdDescription))
4646
}
47+
48+
if fc.DockerCompat {
49+
for cmdName, cmdDescription := range dockerCompatCmds {
50+
allNerdctlCommands = append(allNerdctlCommands, nerdctlCommandCreator.create(cmdName, cmdDescription))
51+
}
52+
}
53+
4754
return allNerdctlCommands
4855
}

Diff for: cmd/finch/nerdctl.go

+125-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ package main
66
import (
77
"encoding/json"
88
"fmt"
9+
"strings"
910

1011
"golang.org/x/exp/slices"
1112

1213
"github.com/aws/aws-sdk-go-v2/aws"
14+
"github.com/sirupsen/logrus"
1315
"github.com/spf13/afero"
1416
"github.com/spf13/cobra"
1517

@@ -41,6 +43,11 @@ type nerdctlCommandCreator struct {
4143
fc *config.Finch
4244
}
4345

46+
type (
47+
argHandler func(systemDeps NerdctlCommandSystemDeps, fc *config.Finch, args []string, index int) error
48+
commandHandler func(systemDeps NerdctlCommandSystemDeps, fc *config.Finch, cmdName *string, args *[]string) error
49+
)
50+
4451
func newNerdctlCommandCreator(
4552
ncc command.NerdctlCmdCreator,
4653
ecc command.Creator,
@@ -192,12 +199,33 @@ var nerdctlCmds = map[string]string{
192199
"wait": "Block until one or more containers stop, then print their exit codes",
193200
}
194201

202+
var dockerCompatCmds = map[string]string{
203+
"buildx": "build version",
204+
}
205+
206+
var aliasMap = map[string]string{
207+
"build": "image build",
208+
"run": "container run",
209+
"cp": "container cp",
210+
}
211+
212+
var commandHandlerMap = map[string]commandHandler{
213+
"buildx": handleBuildx,
214+
"inspect": handleDockerCompatInspect,
215+
}
216+
217+
var argHandlerMap = map[string]map[string]argHandler{
218+
"image build": {
219+
"--load": handleDockerBuildLoad,
220+
},
221+
}
222+
195223
var cmdFlagSetMap = map[string]map[string]sets.Set[string]{
196224
"container run": {
197225
"shortBoolFlags": sets.New[string]("-d", "-i", "-t"),
198226
"longBoolFlags": sets.New[string](
199227
"--detach", "--init", "--interactive", "--oom-kill-disable",
200-
"--privileged", "--read-only", "--rm", "--rootfs", "--tty"),
228+
"--privileged", "--read-only", "--rm", "--rootfs", "--tty", "--sig-proxy"),
201229
"shortArgFlags": sets.New[string]("-e", "-h", "-m", "-u", "-w", "-p", "-l", "-v"),
202230
},
203231
"exec": {
@@ -215,3 +243,99 @@ var cmdFlagSetMap = map[string]map[string]sets.Set[string]{
215243
"shortArgFlags": sets.New[string]("-e", "-h", "-m", "-u", "-w", "-p", "-l", "-v"),
216244
},
217245
}
246+
247+
// converts "docker build --load" flag to "nerdctl build --output=type=docker".
248+
func handleDockerBuildLoad(_ NerdctlCommandSystemDeps, fc *config.Finch, nerdctlCmdArgs []string, index int) error {
249+
if fc.DockerCompat {
250+
nerdctlCmdArgs[index] = "--output=type=docker"
251+
}
252+
253+
return nil
254+
}
255+
256+
func handleBuildx(_ NerdctlCommandSystemDeps, fc *config.Finch, cmdName *string, args *[]string) error {
257+
if fc == nil || !fc.DockerCompat {
258+
return nil
259+
}
260+
261+
if cmdName != nil && *cmdName == "buildx" {
262+
subCmd := (*args)[0]
263+
buildxSubcommands := []string{"bake", "create", "debug", "du", "imagetools", "inspect", "ls", "prune", "rm", "stop", "use", "version"}
264+
265+
if slices.Contains(buildxSubcommands, subCmd) {
266+
return fmt.Errorf("unsupported buildx command: %s", subCmd)
267+
}
268+
269+
logrus.Warn("buildx is not supported. using standard buildkit instead...")
270+
if subCmd == "build" {
271+
*args = (*args)[1:]
272+
}
273+
*cmdName = "build"
274+
}
275+
// else, continue with the original command
276+
return nil
277+
}
278+
279+
func handleDockerCompatInspect(_ NerdctlCommandSystemDeps, fc *config.Finch, cmdName *string, args *[]string) error {
280+
if !fc.DockerCompat {
281+
return nil
282+
}
283+
284+
if *args == nil {
285+
return fmt.Errorf("invalid arguments: args (null pointer)")
286+
}
287+
288+
modeDockerCompat := `--mode=dockercompat`
289+
inspectType := ""
290+
sizeArg := ""
291+
savedArgs := []string{}
292+
skip := false
293+
294+
for idx, arg := range *args {
295+
if skip {
296+
skip = false
297+
continue
298+
}
299+
300+
if (arg == "--type") && (idx < len(*args)-1) {
301+
inspectType = (*args)[idx+1]
302+
skip = true
303+
continue
304+
}
305+
306+
if strings.Contains(arg, "--type") && strings.Contains(arg, "=") {
307+
inspectType = strings.Split(arg, "=")[1]
308+
continue
309+
}
310+
311+
if (arg == "--size") || (arg == "-s") {
312+
sizeArg = "--size"
313+
continue
314+
}
315+
316+
savedArgs = append(savedArgs, arg)
317+
}
318+
319+
switch inspectType {
320+
case "image":
321+
*cmdName = "image inspect"
322+
*args = append([]string{modeDockerCompat}, savedArgs...)
323+
case "volume":
324+
*cmdName = "volume inspect"
325+
if sizeArg != "" {
326+
*args = append([]string{sizeArg}, savedArgs...)
327+
} else {
328+
*args = append([]string{}, savedArgs...)
329+
}
330+
case "container":
331+
*cmdName = "inspect"
332+
*args = append([]string{modeDockerCompat}, savedArgs...)
333+
case "":
334+
*cmdName = "inspect"
335+
*args = append([]string{modeDockerCompat}, savedArgs...)
336+
default:
337+
return fmt.Errorf("unsupported inspect type: %s", inspectType)
338+
}
339+
340+
return nil
341+
}

Diff for: cmd/finch/nerdctl_darwin.go

+78-5
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,23 @@ import (
1313
"github.com/lima-vm/lima/pkg/networks"
1414

1515
"github.com/runfinch/finch/pkg/command"
16+
"github.com/runfinch/finch/pkg/config"
1617
"github.com/runfinch/finch/pkg/flog"
1718
)
1819

1920
func convertToWSLPath(_ NerdctlCommandSystemDeps, _ string) (string, error) {
2021
return "", nil
2122
}
2223

23-
var aliasMap = map[string]string{
24-
"run": "container run",
25-
}
24+
var osAliasMap = map[string]string{}
2625

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

29-
var commandHandlerMap = map[string]commandHandler{}
32+
var osCommandHandlerMap = map[string]commandHandler{}
3033

3134
func (nc *nerdctlCommand) GetCmdArgs() []string {
3235
return []string{"shell", limaInstanceName, "sudo", "-E"}
@@ -46,3 +49,73 @@ func resolveIP(host string, logger flog.Logger, _ command.Creator) (string, erro
4649
}
4750
return host, nil
4851
}
52+
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+
111+
return nil
112+
}
113+
114+
func mapToString(m map[string]string) string {
115+
var parts []string
116+
for k, v := range m {
117+
part := fmt.Sprintf("%s=%s", k, v)
118+
parts = append(parts, part)
119+
}
120+
return strings.Join(parts, ",")
121+
}

0 commit comments

Comments
 (0)