Skip to content

Commit

Permalink
Add DevContainers functionality to Finch
Browse files Browse the repository at this point in the history
Signed-off-by: Sam Chew <[email protected]>
  • Loading branch information
chews93319 committed Oct 2, 2024
1 parent 2e10744 commit d9e9d75
Show file tree
Hide file tree
Showing 11 changed files with 697 additions and 244 deletions.
2 changes: 1 addition & 1 deletion cmd/finch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func initializeNerdctlCommands(
allNerdctlCommands = append(allNerdctlCommands, nerdctlCommandCreator.create(cmdName, cmdDescription))
}

if fc.Mode != nil && *fc.Mode == "dockercompat" {
if fc.DockerCompat {
for cmdName, cmdDescription := range dockerCompatCmds {
allNerdctlCommands = append(allNerdctlCommands, nerdctlCommandCreator.create(cmdName, cmdDescription))
}
Expand Down
120 changes: 120 additions & 0 deletions 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 @@ -196,6 +203,23 @@ 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"),
Expand All @@ -219,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)
}

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 {
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

0 comments on commit d9e9d75

Please sign in to comment.