Skip to content

Commit 8a8455c

Browse files
authored
Merge pull request #499 from doringeman/status-endpoints
feat(cli): add kind and endpointHost to docker model status --json
2 parents 3ac8e33 + 76ea6d7 commit 8a8455c

File tree

4 files changed

+126
-30
lines changed

4 files changed

+126
-30
lines changed

cmd/cli/commands/integration_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"time"
1616

1717
"github.com/docker/model-runner/cmd/cli/desktop"
18+
"github.com/docker/model-runner/cmd/cli/pkg/types"
1819
"github.com/docker/model-runner/pkg/distribution/builder"
1920
"github.com/docker/model-runner/pkg/distribution/registry"
2021
"github.com/stretchr/testify/require"
@@ -117,7 +118,7 @@ func setupTestEnv(t *testing.T) *testEnv {
117118
registryURL := ociRegistry(t, ctx, net)
118119
dmrURL := dockerModelRunner(t, ctx, net)
119120

120-
modelRunnerCtx, err := desktop.NewContextForTest(dmrURL, nil)
121+
modelRunnerCtx, err := desktop.NewContextForTest(dmrURL, nil, types.ModelRunnerEngineKindMoby)
121122
require.NoError(t, err, "Failed to create model runner context")
122123

123124
client := desktop.New(modelRunnerCtx)

cmd/cli/commands/status.go

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import (
77
"os"
88
"strconv"
99

10-
"github.com/docker/model-runner/cmd/cli/pkg/types"
11-
1210
"github.com/docker/cli/cli-plugins/hooks"
1311
"github.com/docker/model-runner/cmd/cli/commands/completion"
1412
"github.com/docker/model-runner/cmd/cli/desktop"
13+
"github.com/docker/model-runner/cmd/cli/pkg/standalone"
14+
"github.com/docker/model-runner/cmd/cli/pkg/types"
1515
"github.com/spf13/cobra"
1616
)
1717

@@ -21,7 +21,7 @@ func newStatusCmd() *cobra.Command {
2121
Use: "status",
2222
Short: "Check if the Docker Model Runner is running",
2323
RunE: func(cmd *cobra.Command, args []string) error {
24-
standalone, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false)
24+
runner, err := ensureStandaloneRunnerAvailable(cmd.Context(), asPrinter(cmd), false)
2525
if err != nil {
2626
return fmt.Errorf("unable to initialize standalone model runner: %w", err)
2727
}
@@ -40,7 +40,7 @@ func newStatusCmd() *cobra.Command {
4040
}
4141

4242
if formatJson {
43-
return jsonStatus(standalone, status, backendStatus)
43+
return jsonStatus(asPrinter(cmd), runner, status, backendStatus)
4444
} else {
4545
textStatus(cmd, status, backendStatus)
4646
}
@@ -69,44 +69,58 @@ func textStatus(cmd *cobra.Command, status desktop.Status, backendStatus map[str
6969
}
7070
}
7171

72-
func jsonStatus(standalone *standaloneRunner, status desktop.Status, backendStatus map[string]string) error {
72+
func makeEndpoint(host string, port int) string {
73+
return "http://" + net.JoinHostPort(host, strconv.Itoa(port)) + "/v1/"
74+
}
75+
76+
func jsonStatus(printer standalone.StatusPrinter, runner *standaloneRunner, status desktop.Status, backendStatus map[string]string) error {
7377
type Status struct {
74-
Running bool `json:"running"`
75-
Backends map[string]string `json:"backends"`
76-
Endpoint string `json:"endpoint"`
78+
Running bool `json:"running"`
79+
Backends map[string]string `json:"backends"`
80+
Kind string `json:"kind"`
81+
Endpoint string `json:"endpoint"`
82+
EndpointHost string `json:"endpointHost"`
7783
}
78-
var endpoint string
84+
var endpoint, endpointHost string
7985
kind := modelRunner.EngineKind()
8086
switch kind {
8187
case types.ModelRunnerEngineKindDesktop:
82-
endpoint = "http://model-runner.docker.internal/engines/v1/"
88+
endpoint = "http://model-runner.docker.internal/v1/"
89+
endpointHost = modelRunner.URL("/v1/")
8390
case types.ModelRunnerEngineKindMobyManual:
84-
endpoint = modelRunner.URL("/engines/v1/")
91+
endpoint = modelRunner.URL("/v1/")
92+
endpointHost = endpoint
8593
case types.ModelRunnerEngineKindCloud:
86-
fallthrough
87-
case types.ModelRunnerEngineKindMoby:
88-
if standalone.gatewayIP == "" {
89-
standalone.gatewayIP = "127.0.0.1"
90-
}
91-
92-
if standalone.gatewayPort == 0 {
93-
standalone.gatewayPort = 12434
94+
gatewayIP := "127.0.0.1"
95+
var gatewayPort uint16 = standalone.DefaultControllerPortCloud
96+
if runner != nil {
97+
if runner.gatewayIP != "" {
98+
gatewayIP = runner.gatewayIP
99+
}
100+
if runner.gatewayPort != 0 {
101+
gatewayPort = runner.gatewayPort
102+
}
94103
}
95-
96-
endpoint = "http://" + net.JoinHostPort(standalone.gatewayIP, strconv.Itoa(int(standalone.gatewayPort))) + "/engines/v1/"
104+
endpoint = makeEndpoint(gatewayIP, int(gatewayPort))
105+
endpointHost = makeEndpoint("127.0.0.1", standalone.DefaultControllerPortCloud)
106+
case types.ModelRunnerEngineKindMoby:
107+
endpoint = makeEndpoint("host.docker.internal", standalone.DefaultControllerPortMoby)
108+
endpointHost = makeEndpoint("127.0.0.1", standalone.DefaultControllerPortMoby)
97109
default:
98110
return fmt.Errorf("unhandled engine kind: %v", kind)
99111
}
100112
s := Status{
101-
Running: status.Running,
102-
Backends: backendStatus,
103-
Endpoint: endpoint,
113+
Running: status.Running,
114+
Backends: backendStatus,
115+
Kind: kind.String(),
116+
Endpoint: endpoint,
117+
EndpointHost: endpointHost,
104118
}
105119
marshal, err := json.Marshal(s)
106120
if err != nil {
107121
return err
108122
}
109-
fmt.Println(string(marshal))
123+
printer.Println(string(marshal))
110124
return nil
111125
}
112126

cmd/cli/commands/status_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ package commands
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"fmt"
67
"io"
78
"net/http"
9+
"strconv"
810
"strings"
911
"testing"
1012

1113
"github.com/docker/cli/cli-plugins/hooks"
1214
"github.com/docker/model-runner/cmd/cli/desktop"
1315
mockdesktop "github.com/docker/model-runner/cmd/cli/mocks"
16+
"github.com/docker/model-runner/cmd/cli/pkg/standalone"
17+
"github.com/docker/model-runner/cmd/cli/pkg/types"
1418
"github.com/docker/model-runner/pkg/inference"
1519
"github.com/stretchr/testify/require"
1620
"go.uber.org/mock/gomock"
@@ -126,3 +130,80 @@ func TestStatus(t *testing.T) {
126130
})
127131
}
128132
}
133+
134+
func TestJsonStatus(t *testing.T) {
135+
tests := []struct {
136+
name string
137+
engineKind types.ModelRunnerEngineKind
138+
urlPrefix string
139+
expectedKind string
140+
expectedEndpoint string
141+
expectedHostEnd string
142+
}{
143+
{
144+
name: "Docker Desktop",
145+
engineKind: types.ModelRunnerEngineKindDesktop,
146+
urlPrefix: "http://localhost" + inference.ExperimentalEndpointsPrefix,
147+
expectedKind: "Docker Desktop",
148+
expectedEndpoint: "http://model-runner.docker.internal/v1/",
149+
expectedHostEnd: "http://localhost" + inference.ExperimentalEndpointsPrefix + "/v1/",
150+
},
151+
{
152+
name: "Docker Engine",
153+
engineKind: types.ModelRunnerEngineKindMoby,
154+
urlPrefix: "http://localhost:" + strconv.Itoa(standalone.DefaultControllerPortMoby),
155+
expectedKind: "Docker Engine",
156+
expectedEndpoint: makeEndpoint("host.docker.internal", standalone.DefaultControllerPortMoby),
157+
expectedHostEnd: makeEndpoint("127.0.0.1", standalone.DefaultControllerPortMoby),
158+
},
159+
{
160+
name: "Docker Cloud",
161+
engineKind: types.ModelRunnerEngineKindCloud,
162+
urlPrefix: "http://localhost:" + strconv.Itoa(standalone.DefaultControllerPortCloud),
163+
expectedKind: "Docker Cloud",
164+
expectedEndpoint: makeEndpoint("127.0.0.1", standalone.DefaultControllerPortCloud),
165+
expectedHostEnd: makeEndpoint("127.0.0.1", standalone.DefaultControllerPortCloud),
166+
},
167+
{
168+
name: "Docker Engine (Manual Install)",
169+
engineKind: types.ModelRunnerEngineKindMobyManual,
170+
urlPrefix: "http://localhost:8080",
171+
expectedKind: "Docker Engine (Manual Install)",
172+
expectedEndpoint: "http://localhost:8080/v1/",
173+
expectedHostEnd: "http://localhost:8080/v1/",
174+
},
175+
}
176+
177+
for _, test := range tests {
178+
t.Run(test.name, func(t *testing.T) {
179+
ctx, err := desktop.NewContextForTest(test.urlPrefix, nil, test.engineKind)
180+
require.NoError(t, err)
181+
modelRunner = ctx
182+
183+
var output string
184+
printer := desktop.NewSimplePrinter(func(msg string) {
185+
output = msg
186+
})
187+
status := desktop.Status{Running: true}
188+
backendStatus := map[string]string{"llama.cpp": "running"}
189+
190+
// Cloud kind needs a runner for gateway IP/port
191+
var runner *standaloneRunner
192+
if test.engineKind == types.ModelRunnerEngineKindCloud {
193+
runner = &standaloneRunner{}
194+
}
195+
196+
err = jsonStatus(printer, runner, status, backendStatus)
197+
require.NoError(t, err)
198+
199+
var result map[string]any
200+
err = json.Unmarshal([]byte(output), &result)
201+
require.NoError(t, err)
202+
203+
require.Equal(t, test.expectedKind, result["kind"])
204+
require.Equal(t, test.expectedEndpoint, result["endpoint"])
205+
require.Equal(t, test.expectedHostEnd, result["endpointHost"])
206+
require.Equal(t, true, result["running"])
207+
})
208+
}
209+
}

cmd/cli/desktop/context.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ func NewContextForMock(client DockerHttpClient) *ModelRunnerContext {
113113
}
114114
}
115115

116-
// NewContextForTest creates a ModelRunnerContext for integration testing
117-
// with a custom URL endpoint. This is intended for use in integration tests
116+
// NewContextForTest creates a ModelRunnerContext for integration and mock testing
117+
// with a custom URL endpoint and engine kind. This is intended for use in tests
118118
// where the Model Runner endpoint is dynamically created (e.g., testcontainers).
119-
func NewContextForTest(endpoint string, client DockerHttpClient) (*ModelRunnerContext, error) {
119+
func NewContextForTest(endpoint string, client DockerHttpClient, kind types.ModelRunnerEngineKind) (*ModelRunnerContext, error) {
120120
urlPrefix, err := url.Parse(endpoint)
121121
if err != nil {
122122
return nil, fmt.Errorf("invalid endpoint URL: %w", err)
@@ -127,7 +127,7 @@ func NewContextForTest(endpoint string, client DockerHttpClient) (*ModelRunnerCo
127127
}
128128

129129
return &ModelRunnerContext{
130-
kind: types.ModelRunnerEngineKindMoby,
130+
kind: kind,
131131
urlPrefix: urlPrefix,
132132
client: client,
133133
}, nil

0 commit comments

Comments
 (0)