From 9123d78ed55f25c668ab5d08a26057c64ce3c8ac Mon Sep 17 00:00:00 2001 From: Cole Snodgrass Date: Tue, 7 May 2024 15:20:25 -0700 Subject: [PATCH] add ping check to docker call --- internal/cmd/local/check.go | 2 +- internal/cmd/local/local_install.go | 2 +- internal/local/cmd.go | 2 +- internal/local/docker/docker.go | 35 ++++++++++++++++++++--------- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/internal/cmd/local/check.go b/internal/cmd/local/check.go index 638caa4..a1da304 100644 --- a/internal/cmd/local/check.go +++ b/internal/cmd/local/check.go @@ -23,7 +23,7 @@ var dockerClient *docker.Docker func dockerInstalled(ctx context.Context) (docker.Version, error) { var err error if dockerClient == nil { - if dockerClient, err = docker.New(); err != nil { + if dockerClient, err = docker.New(ctx); err != nil { pterm.Error.Println("Could not create Docker client") return docker.Version{}, fmt.Errorf("%w: could not create client: %w", localerr.ErrDocker, err) } diff --git a/internal/cmd/local/local_install.go b/internal/cmd/local/local_install.go index 11d064c..e4a38c9 100644 --- a/internal/cmd/local/local_install.go +++ b/internal/cmd/local/local_install.go @@ -62,7 +62,7 @@ func NewCmdInstall() *cobra.Command { // only for kind do we need to check the existing port if provider.Name == k8s.Kind { if dockerClient == nil { - dockerClient, err = docker.New() + dockerClient, err = docker.New(cmd.Context()) if err != nil { pterm.Error.Printfln("Could not connect to Docker daemon") return fmt.Errorf("could not connect to docker: %w", err) diff --git a/internal/local/cmd.go b/internal/local/cmd.go index 11cf8ad..fcf1da5 100644 --- a/internal/local/cmd.go +++ b/internal/local/cmd.go @@ -582,7 +582,7 @@ func defaultHelm(kubecfg, kubectx string) (HelmClient, error) { RestConfig: restCfg, }) if err != nil { - return nil, fmt.Errorf("coud not create helm client: %w", err) + return nil, fmt.Errorf("could not create helm client: %w", err) } return helm, nil diff --git a/internal/local/docker/docker.go b/internal/local/docker/docker.go index 67271a8..0fe0e8b 100644 --- a/internal/local/docker/docker.go +++ b/internal/local/docker/docker.go @@ -36,7 +36,7 @@ type Docker struct { } // New returns a new Docker type with a default Client implementation. -func New() (*Docker, error) { +func New(ctx context.Context) (*Docker, error) { userHome, err := os.UserHomeDir() if err != nil { return nil, fmt.Errorf("could not determine user home directory: %w", err) @@ -48,20 +48,21 @@ func New() (*Docker, error) { switch runtime.GOOS { case "darwin": // on mac, sometimes the docker host isn't set correctly, if it fails check the home directory - dockerCli, err = client.NewClientWithOpts(append(dockerOpts, client.WithHost("unix:///var/run/docker.sock"))...) + dockerCli, err = createAndPing(ctx, "unix:///var/run/docker.sock", dockerOpts) if err != nil { - // keep the original error, as we'll join with the next error (if another error occurs) - outerErr := err - // this works as the last WithHost call will win - dockerCli, err = client.NewClientWithOpts(append(dockerOpts, client.WithHost(fmt.Sprintf("unix:///%s/.docker/run/docker.sock", userHome)))...) - if err != nil { - err = fmt.Errorf("%w: %w", err, outerErr) + var err2 error + dockerCli, err2 = createAndPing(ctx, fmt.Sprintf("unix://%s/.docker/run/docker.sock", userHome), dockerOpts) + if err2 != nil { + return nil, fmt.Errorf("%w: could not create docker client: (%w, %w)", localerr.ErrDocker, err, err2) } + // if we made it here, clear out the original error, + // as we were able to successfully connect on the second attempt + err = nil } case "windows": - dockerCli, err = client.NewClientWithOpts(append(dockerOpts, client.WithHost("npipe:////./pipe/docker_engine"))...) + dockerCli, err = createAndPing(ctx, "npipe:////./pipe/docker_engine", dockerOpts) default: - dockerCli, err = client.NewClientWithOpts(append(dockerOpts, client.WithHost("unix:///var/run/docker.sock"))...) + dockerCli, err = createAndPing(ctx, "unix:///var/run/docker.sock", dockerOpts) } if err != nil { @@ -71,6 +72,20 @@ func New() (*Docker, error) { return &Docker{Client: dockerCli}, nil } +// createAndPing attempts to create a docker client and ping it to ensure we can communicate +func createAndPing(ctx context.Context, host string, opts []client.Opt) (*client.Client, error) { + dockerCli, err := client.NewClientWithOpts(append(opts, client.WithHost(host))...) + if err != nil { + return nil, fmt.Errorf("could not create docker client: %w", err) + } + + if _, err := dockerCli.Ping(ctx); err != nil { + return nil, fmt.Errorf("could not ping docker client: %w", err) + } + + return dockerCli, nil +} + // Version returns the version information from the underlying docker process. func (d *Docker) Version(ctx context.Context) (Version, error) { ver, err := d.Client.ServerVersion(ctx)