diff --git a/Makefile b/Makefile index d9da724..2d14ed8 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ .PHONY: build +DOCKER = docker + # Development build_rev := "main" @@ -35,11 +37,11 @@ run: image: @[ -n "$(name)" ] || (echo "Syntax: make image name=" >&2; exit 1) @echo "Building image codapi/$(name)" - @docker build --file sandboxes/$(name)/Dockerfile --tag codapi/$(name):latest sandboxes/$(name)/ + @$(DOCKER) build --file sandboxes/$(name)/Dockerfile --tag codapi/$(name):latest sandboxes/$(name)/ @echo "✓ codapi/$(name)" network: - docker network create --internal codapi + $(DOCKER) network create --internal codapi # Host OS diff --git a/README.md b/README.md index 6bfc792..132f519 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,10 @@ rm -f codapi.tar.gz 3. Build the sample `ash` sandbox image: ```sh -docker build --file sandboxes/ash/Dockerfile --tag codapi/ash:latest sandboxes/ash +# optional: $DOCKER +export DOCKER=docker + +$DOCKER build --file sandboxes/ash/Dockerfile --tag codapi/ash:latest sandboxes/ash ``` 4. Start the server: diff --git a/codapi-cli b/codapi-cli index aa24a1f..4f5909c 100755 --- a/codapi-cli +++ b/codapi-cli @@ -2,6 +2,7 @@ set -euo pipefail cd "$(dirname "$0")" +docker=${DOCKER:-docker} sandboxes_dir="sandboxes" codapi_url="${CODAPI_URL:-localhost:1313}" @@ -225,9 +226,9 @@ do_sandbox_rm() { fi # 1. Remove the container if it exists. - if docker ps -a -q --filter "name=$name" | grep -q .; then + if $docker ps -a -q --filter "name=$name" | grep -q .; then echo "Removing container '$name'..." - docker rm -f "$name" + $docker rm -f "$name" fi # 2. Remove the directory and its contents. diff --git a/internal/engine/docker.go b/internal/engine/docker.go index 0fe6d70..0206401 100644 --- a/internal/engine/docker.go +++ b/internal/engine/docker.go @@ -31,12 +31,18 @@ const ( type Docker struct { cfg *config.Config cmd *config.Command + exe string // docker } // NewDocker creates a new Docker engine for a specific command. func NewDocker(cfg *config.Config, sandbox, command string) Engine { cmd := cfg.Commands[sandbox][command] - return &Docker{cfg, cmd} + exe := "docker" + // override docker with similar command + if env, ok := os.LookupEnv("DOCKER"); ok { + exe = env + } + return &Docker{cfg, cmd, exe} } // Exec executes the command and returns the output. @@ -196,10 +202,10 @@ func (e *Docker) exec(box *config.Box, step *config.Step, req Request, dir strin if step.Stdin { // pass files to container from stdin stdin := filesReader(files) - stdout, stderr, err = prog.RunStdin(stdin, req.ID, "docker", args...) + stdout, stderr, err = prog.RunStdin(stdin, req.ID, e.exe, args...) } else { // pass files to container from temp directory - stdout, stderr, err = prog.Run(req.ID, "docker", args...) + stdout, stderr, err = prog.Run(req.ID, e.exe, args...) } if err == nil { @@ -213,7 +219,7 @@ func (e *Docker) exec(box *config.Box, step *config.Step, req Request, dir strin // inside the container is not related to the "docker run" process, // and will hang forever after the "docker run" process is killed go func() { - err = dockerKill(req.ID) + err = dockerKill(e.exe, req.ID) if err == nil { logx.Debug("%s: docker kill ok", req.ID) } else { @@ -259,7 +265,7 @@ func (e *Docker) buildArgs(box *config.Box, step *config.Step, req Request, dir command := expandVars(step.Command, req.ID) args = append(args, command...) - logx.Debug("%v", args) + logx.Debug("%s %v", e.exe, args) return args } @@ -345,9 +351,9 @@ func expandVars(command []string, name string) []string { } // dockerKill kills the container with the specified id/name. -func dockerKill(id string) error { +func dockerKill(exe, id string) error { ctx, cancel := context.WithTimeout(context.Background(), killTimeout) defer cancel() - cmd := exec.CommandContext(ctx, "docker", "kill", id) + cmd := exec.CommandContext(ctx, exe, "kill", id) return execy.Run(cmd) } diff --git a/internal/engine/docker_test.go b/internal/engine/docker_test.go index 6bc4b53..60a93d2 100644 --- a/internal/engine/docker_test.go +++ b/internal/engine/docker_test.go @@ -139,6 +139,7 @@ var dockerCfg = &config.Config{ func TestDockerRun(t *testing.T) { logx.Mock() + t.Setenv("DOCKER", "docker") commands := map[string]execy.CmdOut{ "docker run": {Stdout: "hello world", Stderr: "", Err: nil}, } @@ -257,6 +258,7 @@ func TestDockerRun(t *testing.T) { func TestDockerExec(t *testing.T) { logx.Mock() + t.Setenv("DOCKER", "docker") commands := map[string]execy.CmdOut{ "docker exec": {Stdout: "hello world", Stderr: "", Err: nil}, } @@ -287,6 +289,7 @@ func TestDockerExec(t *testing.T) { func TestDockerStop(t *testing.T) { logx.Mock() + t.Setenv("DOCKER", "docker") commands := map[string]execy.CmdOut{ "docker run": {Stdout: "c958ff2", Stderr: "", Err: nil}, "docker exec": {Stdout: "hello", Stderr: "", Err: nil}, diff --git a/internal/sandbox/sandbox_test.go b/internal/sandbox/sandbox_test.go index fa0af69..4c60c49 100644 --- a/internal/sandbox/sandbox_test.go +++ b/internal/sandbox/sandbox_test.go @@ -1,6 +1,7 @@ package sandbox import ( + "os" "testing" "github.com/nalgeon/be" @@ -56,9 +57,13 @@ func TestValidate(t *testing.T) { func TestExec(t *testing.T) { _ = ApplyConfig(cfg) + docker := os.Getenv("DOCKER") + if docker == "" { + docker = "docker" + } t.Run("exec", func(t *testing.T) { execy.Mock(map[string]execy.CmdOut{ - "docker run": {Stdout: "hello"}, + docker + " run": {Stdout: "hello"}, }) req := engine.Request{ ID: "http_42", diff --git a/internal/server/router_test.go b/internal/server/router_test.go index f9363ec..1d97ff2 100644 --- a/internal/server/router_test.go +++ b/internal/server/router_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "os" "testing" "github.com/nalgeon/be" @@ -57,8 +58,12 @@ func (s *server) close() { func Test_exec(t *testing.T) { _ = sandbox.ApplyConfig(cfg) + docker := os.Getenv("DOCKER") + if docker == "" { + docker = "docker" + } execy.Mock(map[string]execy.CmdOut{ - "docker run": {Stdout: "hello"}, + docker + " run": {Stdout: "hello"}, }) srv := newServer()