Skip to content
Open
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions pkg/docker/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ type dockerClient struct {
apiClient func() client.APIClient
}

func (c *dockerClient) client() (client.APIClient, error) {
cli := c.apiClient()
if cli == nil {
return nil, fmt.Errorf("docker client is not available. Please ensure Docker Desktop is running.")
}
return cli, nil
}

func NewClient(cli command.Cli) Client {
return &dockerClient{
apiClient: sync.OnceValue(func() client.APIClient {
Expand Down
28 changes: 28 additions & 0 deletions pkg/docker/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package docker

import (
"context"
"strings"
"testing"

"github.com/docker/docker/client"
)

func TestClientSafeError(t *testing.T) {
// Direct initialization with a nil client factory
c := &dockerClient{
apiClient: func() client.APIClient { return nil },
}

// This should NOT panic anymore
err := c.PullImage(context.Background(), "hello-world")

if err == nil {
t.Fatal("Expected an error, got nil")
}

expectedErr := "docker client is not available"
if !strings.Contains(err.Error(), expectedErr) {
t.Errorf("Expected error to contain %q, got %q", expectedErr, err.Error())
}
}
54 changes: 44 additions & 10 deletions pkg/docker/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import (
"github.com/docker/docker/api/types/network"
)

func (c *dockerClient) ContainerExists(ctx context.Context, container string) (bool, container.InspectResponse, error) {
response, err := c.apiClient().ContainerInspect(ctx, container)
func (c *dockerClient) ContainerExists(ctx context.Context, containerName string) (bool, container.InspectResponse, error) {
cli, err := c.client()
if err != nil {
return false, container.InspectResponse{}, err
}
response, err := cli.ContainerInspect(ctx, containerName)
if cerrdefs.IsNotFound(err) {
return false, response, nil
}
Expand All @@ -22,36 +26,57 @@ func (c *dockerClient) ContainerExists(ctx context.Context, container string) (b
}

func (c *dockerClient) RemoveContainer(ctx context.Context, containerID string, force bool) error {
return c.apiClient().ContainerRemove(ctx, containerID, container.RemoveOptions{
cli, err := c.client()
if err != nil {
return err
}
return cli.ContainerRemove(ctx, containerID, container.RemoveOptions{
Force: force,
})
}

func (c *dockerClient) StartContainer(ctx context.Context, containerID string, containerConfig container.Config, hostConfig container.HostConfig, networkingConfig network.NetworkingConfig) error {
resp, err := c.apiClient().ContainerCreate(ctx, &containerConfig, &hostConfig, &networkingConfig, nil, containerID)
cli, err := c.client()
if err != nil {
return err
}

resp, err := cli.ContainerCreate(ctx, &containerConfig, &hostConfig, &networkingConfig, nil, containerID)
if err != nil {
return fmt.Errorf("creating container: %w", err)
}

if err := c.apiClient().ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
return fmt.Errorf("starting container: %w", err)
}

return nil
}

func (c *dockerClient) StopContainer(ctx context.Context, containerID string, timeout int) error {
return c.apiClient().ContainerStop(ctx, containerID, container.StopOptions{
cli, err := c.client()
if err != nil {
return err
}
return cli.ContainerStop(ctx, containerID, container.StopOptions{
Timeout: &timeout,
})
}

func (c *dockerClient) InspectContainer(ctx context.Context, containerID string) (container.InspectResponse, error) {
return c.apiClient().ContainerInspect(ctx, containerID)
cli, err := c.client()
if err != nil {
return container.InspectResponse{}, err
}
return cli.ContainerInspect(ctx, containerID)
}

func (c *dockerClient) FindContainerByLabel(ctx context.Context, label string) (string, error) {
containers, err := c.apiClient().ContainerList(ctx, container.ListOptions{
cli, err := c.client()
if err != nil {
return "", err
}
containers, err := cli.ContainerList(ctx, container.ListOptions{
Filters: filters.NewArgs(filters.Arg("label", label)),
})
if err != nil {
Expand All @@ -66,7 +91,11 @@ func (c *dockerClient) FindContainerByLabel(ctx context.Context, label string) (
}

func (c *dockerClient) FindAllContainersByLabel(ctx context.Context, label string) ([]string, error) {
containers, err := c.apiClient().ContainerList(ctx, container.ListOptions{
cli, err := c.client()
if err != nil {
return nil, err
}
containers, err := cli.ContainerList(ctx, container.ListOptions{
Filters: filters.NewArgs(filters.Arg("label", label)),
})
if err != nil {
Expand All @@ -92,7 +121,12 @@ func (c *dockerClient) FindAllContainersByLabel(ctx context.Context, label strin
func (c *dockerClient) ReadLogs(ctx context.Context, containerID string, options container.LogsOptions) (io.ReadCloser, error) {
const streamHeaderSize = 8

rc, err := c.apiClient().ContainerLogs(ctx, containerID, options)
cli, err := c.client()
if err != nil {
return nil, err
}

rc, err := cli.ContainerLogs(ctx, containerID, options)
if err != nil {
return nil, err
}
Expand Down
21 changes: 17 additions & 4 deletions pkg/docker/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import (
)

func (c *dockerClient) ImageExists(ctx context.Context, name string) (bool, error) {
_, err := c.apiClient().ContainerInspect(ctx, name)
cli, err := c.client()
if err != nil {
return false, err
}
_, err = cli.ImageInspect(ctx, name)
if cerrdefs.IsNotFound(err) {
return false, nil
}
Expand Down Expand Up @@ -48,11 +52,20 @@ func (c *dockerClient) PullImage(ctx context.Context, name string) error {
}

func (c *dockerClient) InspectImage(ctx context.Context, name string) (image.InspectResponse, error) {
return c.apiClient().ImageInspect(ctx, name)
cli, err := c.client()
if err != nil {
return image.InspectResponse{}, err
}
return cli.ImageInspect(ctx, name)
}

func (c *dockerClient) pullImage(ctx context.Context, imageName string, registryAuthFn func() string) error {
inspect, err := c.apiClient().ImageInspect(ctx, imageName)
cli, err := c.client()
if err != nil {
return err
}

inspect, err := cli.ImageInspect(ctx, imageName)
if err != nil && !cerrdefs.IsNotFound(err) {
return fmt.Errorf("inspecting docker image %s: %w", imageName, err)
}
Expand Down Expand Up @@ -80,7 +93,7 @@ func (c *dockerClient) pullImage(ctx context.Context, imageName string, registry
pullOptions.RegistryAuth = registryAuthFn()
}

response, err := c.apiClient().ImagePull(ctx, imageName, pullOptions)
response, err := cli.ImagePull(ctx, imageName, pullOptions)
if err != nil {
fmt.Fprintf(os.Stderr, " - warning: pulling docker image %s: %v", imageName, err)
return nil
Expand Down
18 changes: 15 additions & 3 deletions pkg/docker/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import (
)

func (c *dockerClient) CreateNetwork(ctx context.Context, name string, internal bool, labels map[string]string) error {
_, err := c.apiClient().NetworkCreate(ctx, name, network.CreateOptions{
cli, err := c.client()
if err != nil {
return err
}
_, err = cli.NetworkCreate(ctx, name, network.CreateOptions{
Internal: internal,
Labels: labels,
})
Expand All @@ -19,11 +23,19 @@ func (c *dockerClient) CreateNetwork(ctx context.Context, name string, internal
}

func (c *dockerClient) RemoveNetwork(ctx context.Context, name string) error {
return c.apiClient().NetworkRemove(ctx, name)
cli, err := c.client()
if err != nil {
return err
}
return cli.NetworkRemove(ctx, name)
}

func (c *dockerClient) ConnectNetwork(ctx context.Context, networkName, container, hostname string) error {
return c.apiClient().NetworkConnect(ctx, networkName, container, &network.EndpointSettings{
cli, err := c.client()
if err != nil {
return err
}
return cli.NetworkConnect(ctx, networkName, container, &network.EndpointSettings{
Aliases: []string{hostname},
})
}
6 changes: 5 additions & 1 deletion pkg/docker/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ import (
)

func (c *dockerClient) InspectVolume(ctx context.Context, name string) (volume.Volume, error) {
return c.apiClient().VolumeInspect(ctx, name)
cli, err := c.client()
if err != nil {
return volume.Volume{}, err
}
return cli.VolumeInspect(ctx, name)
}