@@ -2,8 +2,12 @@ package service
22
33import (
44 "context"
5+ "errors"
56 "fmt"
7+ "os"
8+ "time"
69
10+ "github.com/cenkalti/backoff/v5"
711 "github.com/steveiliop56/ding"
812 "github.com/tinyauthapp/tinyauth/internal/model"
913 "github.com/tinyauthapp/tinyauth/internal/utils/decoders"
@@ -14,6 +18,10 @@ import (
1418 "github.com/docker/docker/client"
1519)
1620
21+ var (
22+ ErrPingFailed = fmt .Errorf ("failed to ping docker" )
23+ )
24+
1725type DockerService struct {
1826 log * logger.Logger
1927 client * client.Client
@@ -31,24 +39,52 @@ type DockerServiceInput struct {
3139}
3240
3341func NewDockerService (i DockerServiceInput ) (* DockerService , error ) {
34- client , err := client . NewClientWithOpts ( client . FromEnv )
35- if err != nil {
36- return nil , err
42+ service := & DockerService {
43+ log : i . Log ,
44+ context : i . Ctx ,
3745 }
3846
39- client . NegotiateAPIVersion ( i . Ctx )
47+ service . log . App . Debug (). Msg ( "Attempting to connect to Docker" )
4048
41- _ , err = client .Ping (i .Ctx )
49+ if os .Getenv ("DOCKER_HOST" ) == "" {
50+ cli , err := service .connect ()
51+ if err != nil {
52+ if errors .Is (err , ErrPingFailed ) {
53+ service .log .App .Debug ().Msg ("Docker not connected" )
54+ return nil , nil
55+ }
56+ return nil , fmt .Errorf ("failed to connect to docker: %w" , err )
57+ }
58+ service .client = cli
59+ } else {
60+ exp := backoff .NewExponentialBackOff ()
61+ exp .InitialInterval = 3 * time .Second
62+ exp .RandomizationFactor = 0.1
63+ exp .Multiplier = 1.5
64+ exp .Reset ()
65+
66+ operation := func () (* client.Client , error ) {
67+ if service .client != nil {
68+ service .client .Close ()
69+ }
70+ cli , err := service .connect ()
71+ if err != nil {
72+ return nil , err
73+ }
74+ return cli , nil
75+ }
4276
43- if err != nil {
44- i .Log .App .Debug ().Err (err ).Msg ("Docker not connected" )
45- return nil , nil
46- }
77+ cli , err := backoff .Retry (service .context , operation , backoff .WithBackOff (exp ), backoff .WithMaxTries (3 ))
4778
48- service := & DockerService {
49- log : i .Log ,
50- client : client ,
51- context : i .Ctx ,
79+ if err != nil {
80+ if errors .Is (err , ErrPingFailed ) {
81+ service .log .App .Debug ().Msg ("Docker not connected after retrying" )
82+ return nil , nil
83+ }
84+ return nil , fmt .Errorf ("failed to connect to docker after retrying: %w" , err )
85+ }
86+
87+ service .client = cli
5288 }
5389
5490 service .isConnected = true
@@ -59,6 +95,22 @@ func NewDockerService(i DockerServiceInput) (*DockerService, error) {
5995 return service , nil
6096}
6197
98+ func (docker * DockerService ) connect () (* client.Client , error ) {
99+ cli , err := client .NewClientWithOpts (client .FromEnv , client .WithAPIVersionNegotiation ())
100+
101+ if err != nil {
102+ return nil , err
103+ }
104+
105+ _ , err = cli .Ping (docker .context )
106+
107+ if err != nil {
108+ return nil , ErrPingFailed
109+ }
110+
111+ return cli , nil
112+ }
113+
62114func (docker * DockerService ) getContainers () ([]container.Summary , error ) {
63115 return docker .client .ContainerList (docker .context , container.ListOptions {})
64116}
0 commit comments