77 "os"
88 "time"
99
10+ "github.com/docker/docker/api/types/container"
1011 "github.com/docker/model-cli/commands/completion"
1112 "github.com/docker/model-cli/desktop"
1213 gpupkg "github.com/docker/model-cli/pkg/gpu"
@@ -42,21 +43,49 @@ func waitForStandaloneRunnerAfterInstall(ctx context.Context) error {
4243 return errors .New ("standalone model runner took too long to initialize" )
4344}
4445
46+ // standaloneRunner encodes the standalone runner configuration, if one exists.
47+ type standaloneRunner struct {
48+ // hostPort is the port that the runner is listening to on the host.
49+ hostPort uint16
50+ // gatewayIP is the gateway IP address that the runner is listening on.
51+ gatewayIP string
52+ // gatewayPort is the gateway port that the runner is listening on.
53+ gatewayPort uint16
54+ }
55+
56+ // inspectStandaloneRunner inspects a standalone runner container and extracts
57+ // its configuration.
58+ func inspectStandaloneRunner (container container.Summary ) * standaloneRunner {
59+ result := & standaloneRunner {}
60+ for _ , port := range container .Ports {
61+ if port .IP == "127.0.0.1" {
62+ result .hostPort = port .PublicPort
63+ } else {
64+ // We don't really have a good way of knowing what the gateway IP
65+ // address is, but in the standard standalone configuration we only
66+ // bind to two interfaces: 127.0.0.1 and the gateway interface.
67+ result .gatewayIP = port .IP
68+ result .gatewayPort = port .PublicPort
69+ }
70+ }
71+ return result
72+ }
73+
4574// ensureStandaloneRunnerAvailable is a utility function that other commands can
4675// use to initialize a default standalone model runner. It is a no-op in
4776// unsupported contexts or if automatic installs have been disabled.
48- func ensureStandaloneRunnerAvailable (ctx context.Context , printer standalone.StatusPrinter ) error {
77+ func ensureStandaloneRunnerAvailable (ctx context.Context , printer standalone.StatusPrinter ) ( * standaloneRunner , error ) {
4978 // If we're not in a supported model runner context, then don't do anything.
5079 engineKind := modelRunner .EngineKind ()
5180 standaloneSupported := engineKind == desktop .ModelRunnerEngineKindMoby ||
5281 engineKind == desktop .ModelRunnerEngineKindCloud
5382 if ! standaloneSupported {
54- return nil
83+ return nil , nil
5584 }
5685
5786 // If automatic installation has been disabled, then don't do anything.
5887 if os .Getenv ("MODEL_RUNNER_NO_AUTO_INSTALL" ) != "" {
59- return nil
88+ return nil , nil
6089 }
6190
6291 // Ensure that the output printer is non-nil.
@@ -67,32 +96,32 @@ func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.Sta
6796 // Create a Docker client for the active context.
6897 dockerClient , err := desktop .DockerClientForContext (dockerCLI , dockerCLI .CurrentContext ())
6998 if err != nil {
70- return fmt .Errorf ("failed to create Docker client: %w" , err )
99+ return nil , fmt .Errorf ("failed to create Docker client: %w" , err )
71100 }
72101
73102 // Check if a model runner container exists.
74- container , _ , err := standalone .FindControllerContainer (ctx , dockerClient )
103+ containerID , _ , container , err := standalone .FindControllerContainer (ctx , dockerClient )
75104 if err != nil {
76- return fmt .Errorf ("unable to identify existing standalone model runner: %w" , err )
77- } else if container != "" {
78- return nil
105+ return nil , fmt .Errorf ("unable to identify existing standalone model runner: %w" , err )
106+ } else if containerID != "" {
107+ return inspectStandaloneRunner ( container ), nil
79108 }
80109
81110 // Automatically determine GPU support.
82111 gpu , err := gpupkg .ProbeGPUSupport (ctx , dockerClient )
83112 if err != nil {
84- return fmt .Errorf ("unable to probe GPU support: %w" , err )
113+ return nil , fmt .Errorf ("unable to probe GPU support: %w" , err )
85114 }
86115
87116 // Ensure that we have an up-to-date copy of the image.
88117 if err := standalone .EnsureControllerImage (ctx , dockerClient , gpu , printer ); err != nil {
89- return fmt .Errorf ("unable to pull latest standalone model runner image: %w" , err )
118+ return nil , fmt .Errorf ("unable to pull latest standalone model runner image: %w" , err )
90119 }
91120
92121 // Ensure that we have a model storage volume.
93122 modelStorageVolume , err := standalone .EnsureModelStorageVolume (ctx , dockerClient , printer )
94123 if err != nil {
95- return fmt .Errorf ("unable to initialize standalone model storage: %w" , err )
124+ return nil , fmt .Errorf ("unable to initialize standalone model storage: %w" , err )
96125 }
97126
98127 // Create the model runner container.
@@ -101,11 +130,28 @@ func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.Sta
101130 port = standalone .DefaultControllerPortCloud
102131 }
103132 if err := standalone .CreateControllerContainer (ctx , dockerClient , port , false , gpu , modelStorageVolume , printer ); err != nil {
104- return fmt .Errorf ("unable to initialize standalone model runner container: %w" , err )
133+ return nil , fmt .Errorf ("unable to initialize standalone model runner container: %w" , err )
105134 }
106135
107136 // Poll until we get a response from the model runner.
108- return waitForStandaloneRunnerAfterInstall (ctx )
137+ if err := waitForStandaloneRunnerAfterInstall (ctx ); err != nil {
138+ return nil , err
139+ }
140+
141+ // Find the runner container.
142+ //
143+ // TODO: We should actually find this before calling
144+ // waitForStandaloneRunnerAfterInstall (or have CreateControllerContainer
145+ // return the container information), and probably pass the target
146+ // information info waitForStandaloneRunnerAfterInstall, but let's wait
147+ // until we do listener port customization / detection in the next PR.
148+ containerID , _ , container , err = standalone .FindControllerContainer (ctx , dockerClient )
149+ if err != nil {
150+ return nil , fmt .Errorf ("unable to identify existing standalone model runner: %w" , err )
151+ } else if containerID == "" {
152+ return nil , errors .New ("standalone model runner not found after installation" )
153+ }
154+ return inspectStandaloneRunner (container ), nil
109155}
110156
111157func newInstallRunner () * cobra.Command {
@@ -149,7 +195,7 @@ func newInstallRunner() *cobra.Command {
149195 }
150196
151197 // Check if an active model runner container already exists.
152- if ctrID , ctrName , err := standalone .FindControllerContainer (cmd .Context (), dockerClient ); err != nil {
198+ if ctrID , ctrName , _ , err := standalone .FindControllerContainer (cmd .Context (), dockerClient ); err != nil {
153199 return err
154200 } else if ctrID != "" {
155201 if ctrName != "" {
0 commit comments