From e692e1f9339d5642a87c6617546d955f1561a836 Mon Sep 17 00:00:00 2001 From: Alexis Couvreur Date: Thu, 27 Jun 2024 23:54:09 +0000 Subject: [PATCH] fix(docker): ensure connection is established with host The `docker` and `docker_swarm` providers have been patched to ensure that the connection is properly established upon starting. If the docker host is not available at starting time, then the application will stop. This will prevent from trying to register to the event stream on a non working client. --- app/providers/docker_classic.go | 23 +++++++++++++++++++---- app/providers/docker_swarm.go | 23 ++++++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/app/providers/docker_classic.go b/app/providers/docker_classic.go index 9942aed4..5b37d37e 100644 --- a/app/providers/docker_classic.go +++ b/app/providers/docker_classic.go @@ -25,10 +25,16 @@ type DockerClassicProvider struct { func NewDockerClassicProvider() (*DockerClassicProvider, error) { cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { - log.Fatal(fmt.Errorf("%+v", "Could not connect to docker API")) - return nil, err + return nil, fmt.Errorf("cannot create docker client: %v", err) } + serverVersion, err := cli.ServerVersion(context.Background()) + if err != nil { + return nil, fmt.Errorf("cannot connect to docker host: %v", err) + } + + log.Trace(fmt.Sprintf("connection established with docker %s (API %s)", serverVersion.Version, serverVersion.APIVersion)) + return &DockerClassicProvider{ Client: cli, desiredReplicas: 1, @@ -144,14 +150,23 @@ func (provider *DockerClassicProvider) NotifyInstanceStopped(ctx context.Context }) for { select { - case msg := <-msgs: + case msg, ok := <-msgs: + if !ok { + log.Error("provider event stream is closed") + return + } // Send the container that has died to the channel instance <- strings.TrimPrefix(msg.Actor.Attributes["name"], "/") - case err := <-errs: + case err, ok := <-errs: + if !ok { + log.Error("provider event stream is closed", err) + return + } if errors.Is(err, io.EOF) { log.Debug("provider event stream closed") return } + log.Error("provider event stream error", err) case <-ctx.Done(): return } diff --git a/app/providers/docker_swarm.go b/app/providers/docker_swarm.go index c4c66a31..029f23d3 100644 --- a/app/providers/docker_swarm.go +++ b/app/providers/docker_swarm.go @@ -23,8 +23,16 @@ type DockerSwarmProvider struct { func NewDockerSwarmProvider() (*DockerSwarmProvider, error) { cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { - return nil, err + return nil, fmt.Errorf("cannot create docker client: %v", err) } + + serverVersion, err := cli.ServerVersion(context.Background()) + if err != nil { + return nil, fmt.Errorf("cannot connect to docker host: %v", err) + } + + log.Trace(fmt.Sprintf("connection established with docker %s (API %s)", serverVersion.Version, serverVersion.APIVersion)) + return &DockerSwarmProvider{ Client: cli, desiredReplicas: 1, @@ -161,17 +169,26 @@ func (provider *DockerSwarmProvider) NotifyInstanceStopped(ctx context.Context, go func() { for { select { - case msg := <-msgs: + case msg, ok := <-msgs: + if !ok { + log.Error("provider event stream is closed") + return + } if msg.Actor.Attributes["replicas.new"] == "0" { instance <- msg.Actor.Attributes["name"] } else if msg.Action == "remove" { instance <- msg.Actor.Attributes["name"] } - case err := <-errs: + case err, ok := <-errs: + if !ok { + log.Error("provider event stream is closed", err) + return + } if errors.Is(err, io.EOF) { log.Debug("provider event stream closed") return } + log.Error("provider event stream error", err) case <-ctx.Done(): return }