feat: conditionally pull images
This commit is contained in:
parent
8b65a3573c
commit
f5bfa62330
@ -59,13 +59,15 @@ const (
|
|||||||
// Client provides a thin wrapper around the Docker API client, and provides
|
// Client provides a thin wrapper around the Docker API client, and provides
|
||||||
// additional functionality such as exposing container stats.
|
// additional functionality such as exposing container stats.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
id shortid.ID
|
id shortid.ID
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
wg sync.WaitGroup
|
mu sync.Mutex
|
||||||
apiClient DockerClient
|
wg sync.WaitGroup
|
||||||
networkID string
|
apiClient DockerClient
|
||||||
logger *slog.Logger
|
networkID string
|
||||||
|
pulledImages map[string]struct{}
|
||||||
|
logger *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new Client.
|
// NewClient creates a new Client.
|
||||||
@ -79,12 +81,13 @@ func NewClient(ctx context.Context, apiClient DockerClient, logger *slog.Logger)
|
|||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
client := &Client{
|
client := &Client{
|
||||||
id: id,
|
id: id,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
apiClient: apiClient,
|
apiClient: apiClient,
|
||||||
networkID: network.ID,
|
networkID: network.ID,
|
||||||
logger: logger,
|
pulledImages: make(map[string]struct{}),
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
@ -154,15 +157,10 @@ func (a *Client) RunContainer(ctx context.Context, params RunContainerParams) (<
|
|||||||
defer a.wg.Done()
|
defer a.wg.Done()
|
||||||
defer close(errC)
|
defer close(errC)
|
||||||
|
|
||||||
containerStateC <- domain.Container{State: "pulling"}
|
if err := a.pullImageIfNeeded(ctx, params.ContainerConfig.Image, containerStateC); err != nil {
|
||||||
|
|
||||||
pullReader, err := a.apiClient.ImagePull(ctx, params.ContainerConfig.Image, image.PullOptions{})
|
|
||||||
if err != nil {
|
|
||||||
sendError(fmt.Errorf("image pull: %w", err))
|
sendError(fmt.Errorf("image pull: %w", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _ = io.Copy(io.Discard, pullReader)
|
|
||||||
_ = pullReader.Close()
|
|
||||||
|
|
||||||
containerConfig := *params.ContainerConfig
|
containerConfig := *params.ContainerConfig
|
||||||
containerConfig.Labels = make(map[string]string)
|
containerConfig.Labels = make(map[string]string)
|
||||||
@ -208,6 +206,32 @@ func (a *Client) RunContainer(ctx context.Context, params RunContainerParams) (<
|
|||||||
return containerStateC, errC
|
return containerStateC, errC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pullImageIfNeeded pulls the image if it has not already been pulled.
|
||||||
|
func (a *Client) pullImageIfNeeded(ctx context.Context, imageName string, containerStateC chan<- domain.Container) error {
|
||||||
|
a.mu.Lock()
|
||||||
|
_, ok := a.pulledImages[imageName]
|
||||||
|
a.mu.Unlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
containerStateC <- domain.Container{State: "pulling"}
|
||||||
|
|
||||||
|
pullReader, err := a.apiClient.ImagePull(ctx, imageName, image.PullOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _ = io.Copy(io.Discard, pullReader)
|
||||||
|
_ = pullReader.Close()
|
||||||
|
|
||||||
|
a.mu.Lock()
|
||||||
|
a.pulledImages[imageName] = struct{}{}
|
||||||
|
a.mu.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// runContainerLoop is the control loop for a single container. It returns only
|
// runContainerLoop is the control loop for a single container. It returns only
|
||||||
// when the container exits.
|
// when the container exits.
|
||||||
func (a *Client) runContainerLoop(
|
func (a *Client) runContainerLoop(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user