@@ -157,100 +157,121 @@ func ensureStandaloneRunnerAvailable(ctx context.Context, printer standalone.Sta
157157 return inspectStandaloneRunner (container ), nil
158158}
159159
160- func newInstallRunner () * cobra.Command {
161- var port uint16
162- var gpuMode string
163- var doNotTrack bool
164- c := & cobra.Command {
165- Use : "install-runner" ,
166- Short : "Install Docker Model Runner (Docker Engine only)" ,
167- RunE : func (cmd * cobra.Command , args []string ) error {
168- // Ensure that we're running in a supported model runner context.
169- engineKind := modelRunner .EngineKind ()
170- if engineKind == types .ModelRunnerEngineKindDesktop {
171- // TODO: We may eventually want to auto-forward this to
172- // docker desktop enable model-runner, but we should first make
173- // sure the CLI flags match.
174- cmd .Println ("Standalone installation not supported with Docker Desktop" )
175- cmd .Println ("Use `docker desktop enable model-runner` instead" )
176- return nil
177- } else if engineKind == types .ModelRunnerEngineKindMobyManual {
178- cmd .Println ("Standalone installation not supported with MODEL_RUNNER_HOST set" )
179- return nil
180- }
160+ // runnerOptions holds common configuration for install/start commands
161+ type runnerOptions struct {
162+ port uint16
163+ gpuMode string
164+ doNotTrack bool
165+ pullImage bool
166+ }
181167
182- if port == 0 {
183- // Use "0" as a sentinel default flag value so it's not displayed automatically.
184- // The default values are written in the usage string.
185- // Hence, the user currently won't be able to set the port to 0 in order to get a random available port.
186- port = standalone .DefaultControllerPortMoby
187- }
188- // HACK: If we're in a Cloud context, then we need to use a
189- // different default port because it conflicts with Docker Desktop's
190- // default model runner host-side port. Unfortunately we can't make
191- // the port flag default dynamic (at least not easily) because of
192- // when context detection happens. So assume that a default value
193- // indicates that we want the Cloud default port. This is less
194- // problematic in Cloud since the UX there is mostly invisible.
195- if engineKind == types .ModelRunnerEngineKindCloud &&
196- port == standalone .DefaultControllerPortMoby {
197- port = standalone .DefaultControllerPortCloud
198- }
168+ // runInstallOrStart is shared logic for install-runner and start-runner commands
169+ func runInstallOrStart (cmd * cobra.Command , opts runnerOptions ) error {
170+ // Ensure that we're running in a supported model runner context.
171+ engineKind := modelRunner .EngineKind ()
172+ if engineKind == types .ModelRunnerEngineKindDesktop {
173+ // TODO: We may eventually want to auto-forward this to
174+ // docker desktop enable model-runner, but we should first make
175+ // sure the CLI flags match.
176+ cmd .Println ("Standalone installation not supported with Docker Desktop" )
177+ cmd .Println ("Use `docker desktop enable model-runner` instead" )
178+ return nil
179+ } else if engineKind == types .ModelRunnerEngineKindMobyManual {
180+ cmd .Println ("Standalone installation not supported with MODEL_RUNNER_HOST set" )
181+ return nil
182+ }
199183
200- // Set the appropriate environment.
201- environment := "moby"
202- if engineKind == types .ModelRunnerEngineKindCloud {
203- environment = "cloud"
204- }
184+ port := opts .port
185+ if port == 0 {
186+ // Use "0" as a sentinel default flag value so it's not displayed automatically.
187+ // The default values are written in the usage string.
188+ // Hence, the user currently won't be able to set the port to 0 in order to get a random available port.
189+ port = standalone .DefaultControllerPortMoby
190+ }
191+ // HACK: If we're in a Cloud context, then we need to use a
192+ // different default port because it conflicts with Docker Desktop's
193+ // default model runner host-side port. Unfortunately we can't make
194+ // the port flag default dynamic (at least not easily) because of
195+ // when context detection happens. So assume that a default value
196+ // indicates that we want the Cloud default port. This is less
197+ // problematic in Cloud since the UX there is mostly invisible.
198+ if engineKind == types .ModelRunnerEngineKindCloud &&
199+ port == standalone .DefaultControllerPortMoby {
200+ port = standalone .DefaultControllerPortCloud
201+ }
202+
203+ // Set the appropriate environment.
204+ environment := "moby"
205+ if engineKind == types .ModelRunnerEngineKindCloud {
206+ environment = "cloud"
207+ }
208+
209+ // Create a Docker client for the active context.
210+ dockerClient , err := desktop .DockerClientForContext (dockerCLI , dockerCLI .CurrentContext ())
211+ if err != nil {
212+ return fmt .Errorf ("failed to create Docker client: %w" , err )
213+ }
205214
206- // Create a Docker client for the active context.
207- dockerClient , err := desktop .DockerClientForContext (dockerCLI , dockerCLI .CurrentContext ())
208- if err != nil {
209- return fmt .Errorf ("failed to create Docker client: %w" , err )
210- }
215+ // Check if an active model runner container already exists.
216+ if ctrID , ctrName , _ , err := standalone .FindControllerContainer (cmd .Context (), dockerClient ); err != nil {
217+ return err
218+ } else if ctrID != "" {
219+ if ctrName != "" {
220+ cmd .Printf ("Model Runner container %s (%s) is already running\n " , ctrName , ctrID [:12 ])
221+ } else {
222+ cmd .Printf ("Model Runner container %s is already running\n " , ctrID [:12 ])
223+ }
224+ return nil
225+ }
211226
212- // Check if an active model runner container already exists.
213- if ctrID , ctrName , _ , err := standalone .FindControllerContainer (cmd .Context (), dockerClient ); err != nil {
214- return err
215- } else if ctrID != "" {
216- if ctrName != "" {
217- cmd .Printf ("Model Runner container %s (%s) is already running\n " , ctrName , ctrID [:12 ])
218- } else {
219- cmd .Printf ("Model Runner container %s is already running\n " , ctrID [:12 ])
220- }
221- return nil
222- }
227+ // Determine GPU support.
228+ var gpu gpupkg.GPUSupport
229+ if opts .gpuMode == "auto" {
230+ gpu , err = gpupkg .ProbeGPUSupport (cmd .Context (), dockerClient )
231+ if err != nil {
232+ return fmt .Errorf ("unable to probe GPU support: %w" , err )
233+ }
234+ } else if opts .gpuMode == "cuda" {
235+ gpu = gpupkg .GPUSupportCUDA
236+ } else if opts .gpuMode != "none" {
237+ return fmt .Errorf ("unknown GPU specification: %q" , opts .gpuMode )
238+ }
223239
224- // Determine GPU support.
225- var gpu gpupkg.GPUSupport
226- if gpuMode == "auto" {
227- gpu , err = gpupkg .ProbeGPUSupport (cmd .Context (), dockerClient )
228- if err != nil {
229- return fmt .Errorf ("unable to probe GPU support: %w" , err )
230- }
231- } else if gpuMode == "cuda" {
232- gpu = gpupkg .GPUSupportCUDA
233- } else if gpuMode != "none" {
234- return fmt .Errorf ("unknown GPU specification: %q" , gpuMode )
235- }
240+ // Ensure that we have an up-to-date copy of the image, if requested.
241+ if opts .pullImage {
242+ if err := standalone .EnsureControllerImage (cmd .Context (), dockerClient , gpu , cmd ); err != nil {
243+ return fmt .Errorf ("unable to pull latest standalone model runner image: %w" , err )
244+ }
245+ }
236246
237- // Ensure that we have an up-to-date copy of the image.
238- if err := standalone .EnsureControllerImage (cmd .Context (), dockerClient , gpu , cmd ); err != nil {
239- return fmt .Errorf ("unable to pull latest standalone model runner image: %w" , err )
240- }
247+ // Ensure that we have a model storage volume.
248+ modelStorageVolume , err := standalone .EnsureModelStorageVolume (cmd .Context (), dockerClient , cmd )
249+ if err != nil {
250+ return fmt .Errorf ("unable to initialize standalone model storage: %w" , err )
251+ }
252+ // Create the model runner container.
253+ if err := standalone .CreateControllerContainer (cmd .Context (), dockerClient , port , environment , opts .doNotTrack , gpu , modelStorageVolume , cmd , engineKind ); err != nil {
254+ return fmt .Errorf ("unable to initialize standalone model runner container: %w" , err )
255+ }
241256
242- // Ensure that we have a model storage volume.
243- modelStorageVolume , err := standalone .EnsureModelStorageVolume (cmd .Context (), dockerClient , cmd )
244- if err != nil {
245- return fmt .Errorf ("unable to initialize standalone model storage: %w" , err )
246- }
247- // Create the model runner container.
248- if err := standalone .CreateControllerContainer (cmd .Context (), dockerClient , port , environment , doNotTrack , gpu , modelStorageVolume , cmd , engineKind ); err != nil {
249- return fmt .Errorf ("unable to initialize standalone model runner container: %w" , err )
250- }
257+ // Poll until we get a response from the model runner.
258+ return waitForStandaloneRunnerAfterInstall (cmd .Context ())
259+ }
251260
252- // Poll until we get a response from the model runner.
253- return waitForStandaloneRunnerAfterInstall (cmd .Context ())
261+ func newInstallRunner () * cobra.Command {
262+ var port uint16
263+ var gpuMode string
264+ var doNotTrack bool
265+ c := & cobra.Command {
266+ Use : "install-runner" ,
267+ Short : "Install Docker Model Runner (Docker Engine only)" ,
268+ RunE : func (cmd * cobra.Command , args []string ) error {
269+ return runInstallOrStart (cmd , runnerOptions {
270+ port : port ,
271+ gpuMode : gpuMode ,
272+ doNotTrack : doNotTrack ,
273+ pullImage : true ,
274+ })
254275 },
255276 ValidArgsFunction : completion .NoComplete ,
256277 }
0 commit comments