Skip to content

Commit 9eee443

Browse files
authored
Merge pull request #900 from areebahmeddd/feat/merge-dmr
run daemon and client from a single binary
2 parents a21f237 + 0580b9c commit 9eee443

8 files changed

Lines changed: 483 additions & 528 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ build-cli:
6060
$(MAKE) -C cmd/cli
6161

6262
build-dmr:
63-
go build -ldflags="-s -w" -o dmr ./cmd/dmr
63+
CGO_ENABLED=1 go build -ldflags="-s -w -X main.Version=$(shell git describe --tags --always --dirty --match 'v*')" -o dmr ./cmd/dmr
6464

6565
build-llamacpp:
6666
git submodule update --init llamacpp/native

cmd/dmr/main.go

Lines changed: 43 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,69 @@
1-
// dmr is a developer convenience wrapper that starts the model-runner server on
2-
// a free port and runs a model-cli command against it in one step.
3-
//
4-
// Usage: dmr <cli-args...>
5-
//
6-
// Example: dmr run qwen3:0.6B-Q4_0 tell me today's news
1+
// Command dmr is the unified Docker Model Runner daemon and client.
72
package main
83

94
import (
10-
"errors"
5+
"context"
116
"fmt"
12-
"net"
13-
"net/http"
147
"os"
15-
"os/exec"
168
"os/signal"
17-
"path/filepath"
18-
"strconv"
199
"syscall"
20-
"time"
21-
)
2210

23-
func freePort() (int, error) {
24-
l, err := net.Listen("tcp", "127.0.0.1:0")
25-
if err != nil {
26-
return 0, err
27-
}
28-
defer l.Close()
29-
return l.Addr().(*net.TCPAddr).Port, nil
30-
}
11+
"github.com/docker/cli/cli/command"
12+
"github.com/docker/model-runner/cmd/cli/commands"
13+
"github.com/docker/model-runner/pkg/server"
14+
"github.com/spf13/cobra"
15+
)
3116

32-
func waitForServer(url string, timeout time.Duration) error {
33-
client := &http.Client{Timeout: time.Second}
34-
deadline := time.Now().Add(timeout)
35-
for time.Now().Before(deadline) {
36-
resp, err := client.Get(url)
37-
if err == nil {
38-
resp.Body.Close()
39-
if resp.StatusCode == http.StatusOK {
40-
return nil
41-
}
42-
}
43-
time.Sleep(200 * time.Millisecond)
44-
}
45-
return fmt.Errorf("server not ready after %s", timeout)
46-
}
17+
var Version = "dev"
4718

48-
func checkBinary(path, name, expectedLayout string) error {
49-
if _, err := os.Stat(path); os.IsNotExist(err) {
50-
return fmt.Errorf("missing %s binary at %s\n\nExpected directory layout:\n%s\n\nPlease run 'make build' to build all binaries", name, path, expectedLayout)
51-
}
52-
return nil
53-
}
19+
const defaultHost = "http://localhost:12434"
20+
const defaultPort = "12434"
5421

5522
func main() {
56-
self, err := os.Executable()
57-
if err != nil {
58-
fmt.Fprintf(os.Stderr, "dmr: %v\n", err)
59-
os.Exit(1)
60-
}
61-
dir := filepath.Dir(self)
62-
63-
serverBin := filepath.Join(dir, "model-runner")
64-
cliBin := filepath.Join(dir, "cmd", "cli", "model-cli")
65-
66-
expectedLayout := fmt.Sprintf(`%s/
67-
├── model-runner (server binary)
68-
├── dmr (this wrapper)
69-
└── cmd/
70-
└── cli/
71-
└── model-cli (CLI binary)`, dir)
72-
73-
if err := checkBinary(serverBin, "model-runner", expectedLayout); err != nil {
74-
fmt.Fprintf(os.Stderr, "dmr: %v\n", err)
75-
os.Exit(1)
76-
}
77-
if err := checkBinary(cliBin, "model-cli", expectedLayout); err != nil {
78-
fmt.Fprintf(os.Stderr, "dmr: %v\n", err)
23+
if err := run(); err != nil {
24+
fmt.Fprintln(os.Stderr, err)
7925
os.Exit(1)
8026
}
27+
}
8128

82-
port, err := freePort()
29+
func run() error {
30+
cli, err := command.NewDockerCli()
8331
if err != nil {
84-
fmt.Fprintf(os.Stderr, "dmr: failed to find free port: %v\n", err)
85-
os.Exit(1)
32+
return fmt.Errorf("unable to initialize CLI: %w", err)
8633
}
87-
portStr := strconv.Itoa(port)
88-
serverURL := "http://localhost:" + portStr
89-
90-
fmt.Fprintf(os.Stderr, "dmr: starting model-runner on port %d\n", port)
9134

92-
server := exec.Command(serverBin)
93-
server.Env = append(os.Environ(), "MODEL_RUNNER_PORT="+portStr)
94-
server.Stderr = os.Stderr
95-
server.Stdout = os.Stdout
35+
root := commands.NewRootCmd(cli)
36+
root.Use = "dmr"
37+
root.Short = "Docker Model Runner"
9638

97-
if err := server.Start(); err != nil {
98-
fmt.Fprintf(os.Stderr, "dmr: failed to start model-runner: %v\n", err)
99-
os.Exit(1)
39+
if os.Getenv("MODEL_RUNNER_HOST") == "" {
40+
if err := os.Setenv("MODEL_RUNNER_HOST", defaultHost); err != nil {
41+
return fmt.Errorf("unable to set MODEL_RUNNER_HOST: %w", err)
42+
}
10043
}
101-
defer server.Process.Kill()
10244

103-
sigCh := make(chan os.Signal, 1)
104-
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
105-
go func() {
106-
<-sigCh
107-
server.Process.Kill()
108-
}()
45+
root.AddCommand(newServeCmd())
10946

110-
if err := waitForServer(serverURL+"/", 30*time.Second); err != nil {
111-
fmt.Fprintf(os.Stderr, "dmr: %v\n", err)
112-
os.Exit(1)
113-
}
47+
return root.Execute()
48+
}
11449

115-
// #nosec G702 - Intentional: dmr is a CLI wrapper that forwards arguments to model-cli
116-
cli := exec.Command(cliBin, os.Args[1:]...)
117-
cli.Env = append(os.Environ(), "MODEL_RUNNER_HOST="+serverURL)
118-
cli.Stdin = os.Stdin
119-
cli.Stdout = os.Stdout
120-
cli.Stderr = os.Stderr
50+
func newServeCmd() *cobra.Command {
51+
return &cobra.Command{
52+
Use: "serve",
53+
Short: "Start the Docker Model Runner daemon",
54+
// skip Docker CLI init; serve runs the daemon directly
55+
PersistentPreRunE: func(*cobra.Command, []string) error { return nil },
56+
RunE: func(cmd *cobra.Command, _ []string) error {
57+
if os.Getenv("MODEL_RUNNER_PORT") == "" {
58+
if err := os.Setenv("MODEL_RUNNER_PORT", defaultPort); err != nil {
59+
return fmt.Errorf("unable to set MODEL_RUNNER_PORT: %w", err)
60+
}
61+
}
12162

122-
if err := cli.Run(); err != nil {
123-
var exitErr *exec.ExitError
124-
if errors.As(err, &exitErr) {
125-
os.Exit(exitErr.ExitCode())
126-
}
127-
fmt.Fprintf(os.Stderr, "dmr: %v\n", err)
128-
os.Exit(1)
63+
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
64+
defer cancel()
65+
66+
return server.Run(ctx, server.Config{Version: Version})
67+
},
12968
}
13069
}

0 commit comments

Comments
 (0)