gitlab.com/jfprevost/gitlab-runner-notlscheck@v11.11.4+incompatible/helpers/docker/official_docker_client.go (about) 1 package docker_helpers 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "path/filepath" 10 "runtime" 11 "time" 12 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/api/types/container" 15 "github.com/docker/docker/api/types/network" 16 "github.com/docker/docker/client" 17 "github.com/docker/go-connections/tlsconfig" 18 "github.com/sirupsen/logrus" 19 ) 20 21 // The default API version used to create a new docker client. 22 const DefaultAPIVersion = "1.25" 23 24 // IsErrNotFound checks whether a returned error is due to an image or container 25 // not being found. Proxies the docker implementation. 26 func IsErrNotFound(err error) bool { 27 return client.IsErrNotFound(err) 28 } 29 30 // type officialDockerClient wraps a "github.com/docker/docker/client".Client, 31 // giving it the methods it needs to satisfy the docker_helpers.Client interface 32 type officialDockerClient struct { 33 client *client.Client 34 35 // Close() means "close idle connections held by engine-api's transport" 36 Transport *http.Transport 37 } 38 39 func newOfficialDockerClient(c DockerCredentials, apiVersion string) (*officialDockerClient, error) { 40 transport, err := newHTTPTransport(c) 41 if err != nil { 42 logrus.Errorln("Error creating TLS Docker client:", err) 43 return nil, err 44 } 45 httpClient := &http.Client{Transport: transport} 46 47 dockerClient, err := client.NewClient(c.Host, apiVersion, httpClient, nil) 48 if err != nil { 49 transport.CloseIdleConnections() 50 logrus.Errorln("Error creating Docker client:", err) 51 return nil, err 52 } 53 54 return &officialDockerClient{ 55 client: dockerClient, 56 Transport: transport, 57 }, nil 58 } 59 60 func wrapError(method string, err error, started time.Time) error { 61 if err == nil { 62 return nil 63 } 64 65 seconds := int(time.Since(started).Seconds()) 66 67 if _, file, line, ok := runtime.Caller(2); ok { 68 return fmt.Errorf("%s (%s:%d:%ds)", err.Error(), filepath.Base(file), line, seconds) 69 } 70 71 return fmt.Errorf("%s (%s:%ds)", err.Error(), method, seconds) 72 } 73 74 func (c *officialDockerClient) ImageInspectWithRaw(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) { 75 started := time.Now() 76 image, data, err := c.client.ImageInspectWithRaw(ctx, imageID) 77 return image, data, wrapError("ImageInspectWithRaw", err, started) 78 } 79 80 func (c *officialDockerClient) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) { 81 started := time.Now() 82 container, err := c.client.ContainerCreate(ctx, config, hostConfig, networkingConfig, containerName) 83 return container, wrapError("ContainerCreate", err, started) 84 } 85 86 func (c *officialDockerClient) ContainerStart(ctx context.Context, containerID string, options types.ContainerStartOptions) error { 87 started := time.Now() 88 err := c.client.ContainerStart(ctx, containerID, options) 89 return wrapError("ContainerCreate", err, started) 90 } 91 92 func (c *officialDockerClient) ContainerKill(ctx context.Context, containerID string, signal string) error { 93 started := time.Now() 94 err := c.client.ContainerKill(ctx, containerID, signal) 95 return wrapError("ContainerWait", err, started) 96 } 97 98 func (c *officialDockerClient) ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error) { 99 started := time.Now() 100 data, err := c.client.ContainerInspect(ctx, containerID) 101 return data, wrapError("ContainerInspect", err, started) 102 } 103 104 func (c *officialDockerClient) ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error) { 105 started := time.Now() 106 response, err := c.client.ContainerAttach(ctx, container, options) 107 return response, wrapError("ContainerAttach", err, started) 108 } 109 110 func (c *officialDockerClient) ContainerRemove(ctx context.Context, containerID string, options types.ContainerRemoveOptions) error { 111 started := time.Now() 112 err := c.client.ContainerRemove(ctx, containerID, options) 113 return wrapError("ContainerRemove", err, started) 114 } 115 116 func (c *officialDockerClient) ContainerLogs(ctx context.Context, container string, options types.ContainerLogsOptions) (io.ReadCloser, error) { 117 started := time.Now() 118 rc, err := c.client.ContainerLogs(ctx, container, options) 119 return rc, wrapError("ContainerLogs", err, started) 120 } 121 122 func (c *officialDockerClient) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) { 123 started := time.Now() 124 resp, err := c.client.ContainerExecCreate(ctx, container, config) 125 return resp, wrapError("ContainerExecCreate", err, started) 126 } 127 128 func (c *officialDockerClient) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) { 129 started := time.Now() 130 resp, err := c.client.ContainerExecAttach(ctx, execID, config) 131 return resp, wrapError("ContainerExecAttach", err, started) 132 } 133 134 func (c *officialDockerClient) NetworkDisconnect(ctx context.Context, networkID string, containerID string, force bool) error { 135 started := time.Now() 136 err := c.client.NetworkDisconnect(ctx, networkID, containerID, force) 137 return wrapError("NetworkDisconnect", err, started) 138 } 139 140 func (c *officialDockerClient) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) { 141 started := time.Now() 142 networks, err := c.client.NetworkList(ctx, options) 143 return networks, wrapError("NetworkList", err, started) 144 } 145 146 func (c *officialDockerClient) Info(ctx context.Context) (types.Info, error) { 147 started := time.Now() 148 info, err := c.client.Info(ctx) 149 return info, wrapError("Info", err, started) 150 } 151 152 func (c *officialDockerClient) ImageImportBlocking(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) error { 153 started := time.Now() 154 readCloser, err := c.client.ImageImport(ctx, source, ref, options) 155 if err != nil { 156 return wrapError("ImageImport", err, started) 157 } 158 defer readCloser.Close() 159 160 // TODO: respect the context here 161 if _, err := io.Copy(ioutil.Discard, readCloser); err != nil { 162 return wrapError("io.Copy: Failed to import image", err, started) 163 } 164 165 return nil 166 } 167 168 func (c *officialDockerClient) ImagePullBlocking(ctx context.Context, ref string, options types.ImagePullOptions) error { 169 started := time.Now() 170 readCloser, err := c.client.ImagePull(ctx, ref, options) 171 if err != nil { 172 return wrapError("ImagePull", err, started) 173 } 174 defer readCloser.Close() 175 176 // TODO: respect the context here 177 if _, err := io.Copy(ioutil.Discard, readCloser); err != nil { 178 return wrapError("io.Copy: Failed to pull image", err, started) 179 } 180 181 return nil 182 } 183 184 func (c *officialDockerClient) Close() error { 185 c.Transport.CloseIdleConnections() 186 return nil 187 } 188 189 // New attempts to create a new Docker client of the specified version. If the 190 // specified version is empty, it will use the default version. 191 // 192 // If no host is given in the DockerCredentials, it will attempt to look up 193 // details from the environment. If that fails, it will use the default 194 // connection details for your platform. 195 func New(c DockerCredentials, apiVersion string) (Client, error) { 196 if c.Host == "" { 197 c = credentialsFromEnv() 198 } 199 200 // Use the default if nothing is specified by caller *or* environment 201 if c.Host == "" { 202 c.Host = client.DefaultDockerHost 203 } 204 205 if apiVersion == "" { 206 apiVersion = DefaultAPIVersion 207 } 208 209 return newOfficialDockerClient(c, apiVersion) 210 } 211 212 func newHTTPTransport(c DockerCredentials) (*http.Transport, error) { 213 url, err := client.ParseHostURL(c.Host) 214 if err != nil { 215 return nil, err 216 } 217 218 tr := &http.Transport{} 219 220 if err := configureTransport(tr, url.Scheme, url.Host); err != nil { 221 return nil, err 222 } 223 224 // FIXME: is a TLS connection with InsecureSkipVerify == true ever wanted? 225 if c.TLSVerify { 226 options := tlsconfig.Options{} 227 228 if c.CertPath != "" { 229 options.CAFile = filepath.Join(c.CertPath, "ca.pem") 230 options.CertFile = filepath.Join(c.CertPath, "cert.pem") 231 options.KeyFile = filepath.Join(c.CertPath, "key.pem") 232 } 233 234 tlsConfig, err := tlsconfig.Client(options) 235 if err != nil { 236 tr.CloseIdleConnections() 237 return nil, err 238 } 239 240 tr.TLSClientConfig = tlsConfig 241 } 242 243 return tr, nil 244 }