From bbcc8cafa68df1184686208d37086ab779b159ae Mon Sep 17 00:00:00 2001 From: Stavros Date: Thu, 3 Sep 2026 12:14:30 +0300 Subject: [PATCH] feat: add backoff to docker connection for proxies (#1114) --- internal/service/docker_service.go | 82 ++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 15 deletions(-) diff --git a/internal/service/docker_service.go b/internal/service/docker_service.go index 21265a2e..28b12f8e 100644 --- a/internal/service/docker_service.go +++ b/internal/service/docker_service.go @@ -2,8 +2,12 @@ package service import ( "context" + "errors" "fmt" + "os" + "time" + "github.com/cenkalti/backoff/v5" "github.com/steveiliop56/ding" "github.com/tinyauthapp/tinyauth/internal/model" "github.com/tinyauthapp/tinyauth/internal/utils/decoders" @@ -14,6 +18,10 @@ import ( "github.com/docker/docker/client" ) +var ( + ErrPingFailed = fmt.Errorf("failed to ping docker") +) + type DockerService struct { log *logger.Logger client *client.Client @@ -31,26 +39,54 @@ type DockerServiceInput struct { } func NewDockerService(i DockerServiceInput) (*DockerService, error) { - client, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { - return nil, err - } - - client.NegotiateAPIVersion(i.Ctx) - - _, err = client.Ping(i.Ctx) - - if err != nil { - i.Log.App.Debug().Err(err).Msg("Docker not connected") - return nil, nil - } - service := &DockerService{ log: i.Log, - client: client, context: i.Ctx, } + service.log.App.Debug().Msg("Attempting to connect to Docker") + + if os.Getenv("DOCKER_HOST") == "" { + cli, err := service.connect() + if err != nil { + if errors.Is(err, ErrPingFailed) { + service.log.App.Debug().Msg("Docker not connected") + return nil, nil + } + return nil, fmt.Errorf("failed to connect to docker: %w", err) + } + service.client = cli + } else { + exp := backoff.NewExponentialBackOff() + exp.InitialInterval = 3 * time.Second + exp.RandomizationFactor = 0.1 + exp.Multiplier = 1.5 + exp.Reset() + + operation := func() (*client.Client, error) { + if service.client != nil { + service.client.Close() + } + cli, err := service.connect() + if err != nil { + return nil, err + } + return cli, nil + } + + cli, err := backoff.Retry(service.context, operation, backoff.WithBackOff(exp), backoff.WithMaxTries(3)) + + if err != nil { + if errors.Is(err, ErrPingFailed) { + service.log.App.Debug().Msg("Docker not connected after retrying") + return nil, nil + } + return nil, fmt.Errorf("failed to connect to docker after retrying: %w", err) + } + + service.client = cli + } + service.isConnected = true service.log.App.Debug().Msg("Docker connected successfully") @@ -59,6 +95,22 @@ func NewDockerService(i DockerServiceInput) (*DockerService, error) { return service, nil } +func (docker *DockerService) connect() (*client.Client, error) { + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + + if err != nil { + return nil, err + } + + _, err = cli.Ping(docker.context) + + if err != nil { + return nil, ErrPingFailed + } + + return cli, nil +} + func (docker *DockerService) getContainers() ([]container.Summary, error) { return docker.client.ContainerList(docker.context, container.ListOptions{}) }