Skip to content
This repository was archived by the owner on Oct 6, 2025. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions pkg/standalone/containers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package standalone

import (
"archive/tar"
"bytes"
"context"
"errors"
"fmt"
Expand All @@ -27,6 +29,100 @@ const controllerContainerName = "docker-model-runner"
// the conflicting container in a capture group.
var concurrentInstallMatcher = regexp.MustCompile(`is already in use by container "([a-z0-9]+)"`)

// copyDockerConfigToContainer copies the Docker config file from the host to the container
// and sets up proper ownership and permissions for the modelrunner user.
func copyDockerConfigToContainer(ctx context.Context, dockerClient *client.Client, containerID string) error {
dockerConfigPath := os.ExpandEnv("$HOME/.docker/config.json")
if s, err := os.Stat(dockerConfigPath); err != nil || s.Mode()&os.ModeType != 0 {
return nil
}

configData, err := os.ReadFile(dockerConfigPath)
if err != nil {
return fmt.Errorf("failed to read Docker config file: %w", err)
}

var buf bytes.Buffer
tw := tar.NewWriter(&buf)
header := &tar.Header{
Name: ".docker/config.json",
Mode: 0600,
Size: int64(len(configData)),
}
if err := tw.WriteHeader(header); err != nil {
return fmt.Errorf("failed to write tar header: %w", err)
}
if _, err := tw.Write(configData); err != nil {
return fmt.Errorf("failed to write config data to tar: %w", err)
}
if err := tw.Close(); err != nil {
return fmt.Errorf("failed to close tar writer: %w", err)
}

// Ensure the .docker directory exists
mkdirCmd := "mkdir -p /home/modelrunner/.docker && chown modelrunner:modelrunner /home/modelrunner/.docker"
if err := execInContainer(ctx, dockerClient, containerID, mkdirCmd); err != nil {
return err
}

// Copy directly into the .docker directory
err = dockerClient.CopyToContainer(ctx, containerID, "/home/modelrunner", &buf, container.CopyToContainerOptions{
CopyUIDGID: true,
})
if err != nil {
return fmt.Errorf("failed to copy config file to container: %w", err)
}

// Set correct ownership and permissions
chmodCmd := "chown modelrunner:modelrunner /home/modelrunner/.docker/config.json && chmod 600 /home/modelrunner/.docker/config.json"
if err := execInContainer(ctx, dockerClient, containerID, chmodCmd); err != nil {
return err
}

return nil
}

func execInContainer(ctx context.Context, dockerClient *client.Client, containerID, cmd string) error {
execConfig := container.ExecOptions{
Cmd: []string{"sh", "-c", cmd},
}
execResp, err := dockerClient.ContainerExecCreate(ctx, containerID, execConfig)
if err != nil {
return fmt.Errorf("failed to create exec for command '%s': %w", cmd, err)
}
if err := dockerClient.ContainerExecStart(ctx, execResp.ID, container.ExecStartOptions{}); err != nil {
return fmt.Errorf("failed to start exec for command '%s': %w", cmd, err)
}

// Create a timeout context for the polling loop
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()

// Poll until the command finishes or timeout occurs
for {
inspectResp, err := dockerClient.ContainerExecInspect(ctx, execResp.ID)
if err != nil {
return fmt.Errorf("failed to inspect exec for command '%s': %w", cmd, err)
}

if !inspectResp.Running {
// Command has finished, now we can safely check the exit code
if inspectResp.ExitCode != 0 {
return fmt.Errorf("command '%s' failed with exit code %d", cmd, inspectResp.ExitCode)
}
return nil
}

// Brief sleep to avoid busy polling, with timeout check
select {
case <-time.After(100 * time.Millisecond):
// Continue polling
case <-timeoutCtx.Done():
return fmt.Errorf("command '%s' timed out after 10 seconds", cmd)
}
}
}

// FindControllerContainer searches for a running controller container. It
// returns the ID of the container (if found), the container name (if any), the
// full container summary (if found), or any error that occurred.
Expand Down Expand Up @@ -174,6 +270,12 @@ func CreateControllerContainer(ctx context.Context, dockerClient *client.Client,
_ = dockerClient.ContainerRemove(ctx, resp.ID, container.RemoveOptions{Force: true})
return fmt.Errorf("failed to start container %s: %w", controllerContainerName, err)
}

// Copy Docker config file if it exists
if err := copyDockerConfigToContainer(ctx, dockerClient, resp.ID); err != nil {
// Log warning but continue - don't fail container creation
printer.Printf("Warning: failed to copy Docker config: %v\n", err)
}
return nil
}

Expand Down