Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Dockercompat Mode & DevContainer Support #1100

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/finch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,12 @@ func initializeNerdctlCommands(
for cmdName, cmdDescription := range nerdctlCmds {
allNerdctlCommands = append(allNerdctlCommands, nerdctlCommandCreator.create(cmdName, cmdDescription))
}

if fc.DockerCompat {
for cmdName, cmdDescription := range dockerCompatCmds {
allNerdctlCommands = append(allNerdctlCommands, nerdctlCommandCreator.create(cmdName, cmdDescription))
}
}

return allNerdctlCommands
}
126 changes: 125 additions & 1 deletion cmd/finch/nerdctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ package main
import (
"encoding/json"
"fmt"
"strings"

"golang.org/x/exp/slices"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/spf13/cobra"

Expand Down Expand Up @@ -41,6 +43,11 @@ type nerdctlCommandCreator struct {
fc *config.Finch
}

type (
argHandler func(systemDeps NerdctlCommandSystemDeps, fc *config.Finch, args []string, index int) error
commandHandler func(systemDeps NerdctlCommandSystemDeps, fc *config.Finch, cmdName *string, args *[]string) error
)

func newNerdctlCommandCreator(
ncc command.NerdctlCmdCreator,
ecc command.Creator,
Expand Down Expand Up @@ -192,12 +199,33 @@ var nerdctlCmds = map[string]string{
"wait": "Block until one or more containers stop, then print their exit codes",
}

var dockerCompatCmds = map[string]string{
"buildx": "build version",
}

var aliasMap = map[string]string{
"build": "image build",
"run": "container run",
"cp": "container cp",
}

var commandHandlerMap = map[string]commandHandler{
"buildx": handleBuildx,
"inspect": handleDockerCompatInspect,
}

var argHandlerMap = map[string]map[string]argHandler{
"image build": {
"--load": handleDockerBuildLoad,
},
}

var cmdFlagSetMap = map[string]map[string]sets.Set[string]{
"container run": {
"shortBoolFlags": sets.New[string]("-d", "-i", "-t"),
"longBoolFlags": sets.New[string](
"--detach", "--init", "--interactive", "--oom-kill-disable",
"--privileged", "--read-only", "--rm", "--rootfs", "--tty"),
"--privileged", "--read-only", "--rm", "--rootfs", "--tty", "--sig-proxy"),
"shortArgFlags": sets.New[string]("-e", "-h", "-m", "-u", "-w", "-p", "-l", "-v"),
},
"exec": {
Expand All @@ -215,3 +243,99 @@ var cmdFlagSetMap = map[string]map[string]sets.Set[string]{
"shortArgFlags": sets.New[string]("-e", "-h", "-m", "-u", "-w", "-p", "-l", "-v"),
},
}

// converts "docker build --load" flag to "nerdctl build --output=type=docker".
func handleDockerBuildLoad(_ NerdctlCommandSystemDeps, fc *config.Finch, nerdctlCmdArgs []string, index int) error {
if fc.DockerCompat {
nerdctlCmdArgs[index] = "--output=type=docker"
}

return nil
}

func handleBuildx(_ NerdctlCommandSystemDeps, fc *config.Finch, cmdName *string, args *[]string) error {
if fc == nil || !fc.DockerCompat {
return nil
}

if cmdName != nil && *cmdName == "buildx" {
subCmd := (*args)[0]
buildxSubcommands := []string{"bake", "create", "debug", "du", "imagetools", "inspect", "ls", "prune", "rm", "stop", "use", "version"}

if slices.Contains(buildxSubcommands, subCmd) {
return fmt.Errorf("unsupported buildx command: %s", subCmd)
}

logrus.Warn("buildx is not supported. using standard buildkit instead...")
if subCmd == "build" {
*args = (*args)[1:]
}
*cmdName = "build"
}
// else, continue with the original command
return nil
}

func handleDockerCompatInspect(_ NerdctlCommandSystemDeps, fc *config.Finch, cmdName *string, args *[]string) error {
if !fc.DockerCompat {
return nil
}

if *args == nil {
return fmt.Errorf("invalid arguments: args (null pointer)")
}

modeDockerCompat := `--mode=dockercompat`
inspectType := ""
sizeArg := ""
savedArgs := []string{}
skip := false

for idx, arg := range *args {
if skip {
skip = false
continue
}

if (arg == "--type") && (idx < len(*args)-1) {
inspectType = (*args)[idx+1]
skip = true
continue
}

if strings.Contains(arg, "--type") && strings.Contains(arg, "=") {
inspectType = strings.Split(arg, "=")[1]
continue
}

if (arg == "--size") || (arg == "-s") {
sizeArg = "--size"
continue
}

savedArgs = append(savedArgs, arg)
}

switch inspectType {
case "image":
*cmdName = "image inspect"
*args = append([]string{modeDockerCompat}, savedArgs...)
case "volume":
*cmdName = "volume inspect"
if sizeArg != "" {
*args = append([]string{sizeArg}, savedArgs...)
} else {
*args = append([]string{}, savedArgs...)
}
case "container":
*cmdName = "inspect"
*args = append([]string{modeDockerCompat}, savedArgs...)
case "":
*cmdName = "inspect"
*args = append([]string{modeDockerCompat}, savedArgs...)
default:
return fmt.Errorf("unsupported inspect type: %s", inspectType)
Shubhranshu153 marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}
83 changes: 78 additions & 5 deletions cmd/finch/nerdctl_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,23 @@ 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"
)

func convertToWSLPath(_ NerdctlCommandSystemDeps, _ string) (string, error) {
return "", nil
}

var aliasMap = map[string]string{
"run": "container run",
}
var osAliasMap = map[string]string{}

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

var commandHandlerMap = map[string]commandHandler{}
var osCommandHandlerMap = map[string]commandHandler{}

func (nc *nerdctlCommand) GetCmdArgs() []string {
return []string{"shell", limaInstanceName, "sudo", "-E"}
Expand All @@ -46,3 +49,73 @@ 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 {
Shubhranshu153 marked this conversation as resolved.
Show resolved Hide resolved
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
}

return nil
}

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, ",")
}
Loading
Loading