Browse code

client: use stdlib errors

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2025/08/05 03:46:30
Showing 26 changed files
... ...
@@ -3,12 +3,12 @@ package client
3 3
 import (
4 4
 	"context"
5 5
 	"encoding/json"
6
+	"fmt"
6 7
 	"net/url"
7 8
 	"strconv"
8 9
 
9 10
 	"github.com/moby/moby/api/types/build"
10 11
 	"github.com/moby/moby/api/types/filters"
11
-	"github.com/pkg/errors"
12 12
 )
13 13
 
14 14
 // BuildCachePrune requests the daemon to delete unused cache data.
... ...
@@ -36,7 +36,7 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts build.CachePruneOpt
36 36
 	}
37 37
 	f, err := filters.ToJSON(opts.Filters)
38 38
 	if err != nil {
39
-		return nil, errors.Wrap(err, "prune could not marshal filters option")
39
+		return nil, fmt.Errorf("prune could not marshal filters option: %w", err)
40 40
 	}
41 41
 	query.Set("filters", f)
42 42
 
... ...
@@ -49,7 +49,7 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts build.CachePruneOpt
49 49
 
50 50
 	report := build.CachePruneReport{}
51 51
 	if err := json.NewDecoder(resp.Body).Decode(&report); err != nil {
52
-		return nil, errors.Wrap(err, "error retrieving disk usage")
52
+		return nil, fmt.Errorf("error retrieving disk usage: %w", err)
53 53
 	}
54 54
 
55 55
 	return &report, nil
... ...
@@ -44,6 +44,8 @@ package client
44 44
 import (
45 45
 	"context"
46 46
 	"crypto/tls"
47
+	"errors"
48
+	"fmt"
47 49
 	"net"
48 50
 	"net/http"
49 51
 	"net/url"
... ...
@@ -56,7 +58,6 @@ import (
56 56
 	"github.com/docker/go-connections/sockets"
57 57
 	"github.com/moby/moby/api/types"
58 58
 	"github.com/moby/moby/api/types/versions"
59
-	"github.com/pkg/errors"
60 59
 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
61 60
 )
62 61
 
... ...
@@ -421,7 +422,7 @@ func (cli *Client) HTTPClient() *http.Client {
421 421
 func ParseHostURL(host string) (*url.URL, error) {
422 422
 	proto, addr, ok := strings.Cut(host, "://")
423 423
 	if !ok || addr == "" {
424
-		return nil, errors.Errorf("unable to parse docker host `%s`", host)
424
+		return nil, fmt.Errorf("unable to parse docker host `%s`", host)
425 425
 	}
426 426
 
427 427
 	var basePath string
... ...
@@ -2,13 +2,13 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"io"
6 7
 	"net/url"
7 8
 	"time"
8 9
 
9 10
 	"github.com/moby/moby/api/types/container"
10 11
 	timetypes "github.com/moby/moby/api/types/time"
11
-	"github.com/pkg/errors"
12 12
 )
13 13
 
14 14
 // ContainerLogs returns the logs generated by a container in an [io.ReadCloser].
... ...
@@ -55,7 +55,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option
55 55
 	if options.Since != "" {
56 56
 		ts, err := timetypes.GetTimestamp(options.Since, time.Now())
57 57
 		if err != nil {
58
-			return nil, errors.Wrap(err, `invalid value for "since"`)
58
+			return nil, fmt.Errorf(`invalid value for "since": %w`, err)
59 59
 		}
60 60
 		query.Set("since", ts)
61 61
 	}
... ...
@@ -63,7 +63,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option
63 63
 	if options.Until != "" {
64 64
 		ts, err := timetypes.GetTimestamp(options.Until, time.Now())
65 65
 		if err != nil {
66
-			return nil, errors.Wrap(err, `invalid value for "until"`)
66
+			return nil, fmt.Errorf(`invalid value for "until": %w`, err)
67 67
 		}
68 68
 		query.Set("until", ts)
69 69
 	}
... ...
@@ -13,7 +13,6 @@ require (
13 13
 	github.com/moby/term v0.5.2
14 14
 	github.com/opencontainers/go-digest v1.0.0
15 15
 	github.com/opencontainers/image-spec v1.1.1
16
-	github.com/pkg/errors v0.9.1
17 16
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
18 17
 	go.opentelemetry.io/otel/trace v1.35.0
19 18
 	gotest.tools/v3 v3.5.2
... ...
@@ -35,8 +35,6 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
35 35
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
36 36
 github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
37 37
 github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
38
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
39
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
40 38
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
41 39
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
42 40
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
... ...
@@ -10,7 +10,6 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/moby/moby/api/types/versions"
13
-	"github.com/pkg/errors"
14 13
 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
15 14
 )
16 15
 
... ...
@@ -56,11 +55,11 @@ func setupHijackConn(dialer func(context.Context) (net.Conn, error), req *http.R
56 56
 
57 57
 	conn, err := dialer(ctx)
58 58
 	if err != nil {
59
-		return nil, "", errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
59
+		return nil, "", fmt.Errorf("cannot connect to the Docker daemon. Is 'docker daemon' running on this host?: %w", err)
60 60
 	}
61 61
 	defer func() {
62 62
 		if retErr != nil {
63
-			conn.Close()
63
+			_ = conn.Close()
64 64
 		}
65 65
 	}()
66 66
 
... ...
@@ -2,21 +2,22 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"errors"
6
+	"fmt"
5 7
 	"net/url"
6 8
 
7 9
 	"github.com/distribution/reference"
8
-	"github.com/pkg/errors"
9 10
 )
10 11
 
11 12
 // ImageTag tags an image in the docker host
12 13
 func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
13 14
 	if _, err := reference.ParseAnyReference(source); err != nil {
14
-		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source)
15
+		return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", source, err)
15 16
 	}
16 17
 
17 18
 	ref, err := reference.ParseNormalizedNamed(target)
18 19
 	if err != nil {
19
-		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target)
20
+		return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", target, err)
20 21
 	}
21 22
 
22 23
 	if _, isCanonical := ref.(reference.Canonical); isCanonical {
... ...
@@ -32,7 +32,7 @@ func TestImageTagInvalidReference(t *testing.T) {
32 32
 	}
33 33
 
34 34
 	err := client.ImageTag(context.Background(), "image_id", "aa/asdf$$^/aa")
35
-	assert.Check(t, is.Error(err, `Error parsing reference: "aa/asdf$$^/aa" is not a valid repository/tag: invalid reference format`))
35
+	assert.Check(t, is.Error(err, `error parsing reference: "aa/asdf$$^/aa" is not a valid repository/tag: invalid reference format`))
36 36
 }
37 37
 
38 38
 // Ensure we don't allow the use of invalid repository names or tags; these tag operations should fail.
... ...
@@ -65,19 +65,19 @@ func TestImageTagInvalidSourceImageName(t *testing.T) {
65 65
 	t.Run("test repository name begin with '-'", func(t *testing.T) {
66 66
 		t.Parallel()
67 67
 		err := client.ImageTag(ctx, "busybox:latest", "-busybox:test")
68
-		assert.Check(t, is.ErrorContains(err, "Error parsing reference"))
68
+		assert.Check(t, is.ErrorContains(err, "error parsing reference"))
69 69
 	})
70 70
 
71 71
 	t.Run("test namespace name begin with '-'", func(t *testing.T) {
72 72
 		t.Parallel()
73 73
 		err := client.ImageTag(ctx, "busybox:latest", "-test/busybox:test")
74
-		assert.Check(t, is.ErrorContains(err, "Error parsing reference"))
74
+		assert.Check(t, is.ErrorContains(err, "error parsing reference"))
75 75
 	})
76 76
 
77 77
 	t.Run("test index name begin with '-'", func(t *testing.T) {
78 78
 		t.Parallel()
79 79
 		err := client.ImageTag(ctx, "busybox:latest", "-index:5000/busybox:test")
80
-		assert.Check(t, is.ErrorContains(err, "Error parsing reference"))
80
+		assert.Check(t, is.ErrorContains(err, "error parsing reference"))
81 81
 	})
82 82
 }
83 83
 
... ...
@@ -2,6 +2,7 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"net"
6 7
 	"net/http"
7 8
 	"os"
... ...
@@ -11,7 +12,6 @@ import (
11 11
 
12 12
 	"github.com/docker/go-connections/sockets"
13 13
 	"github.com/docker/go-connections/tlsconfig"
14
-	"github.com/pkg/errors"
15 14
 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
16 15
 	"go.opentelemetry.io/otel/trace"
17 16
 )
... ...
@@ -55,7 +55,7 @@ func WithDialContext(dialContext func(ctx context.Context, network, addr string)
55 55
 			transport.DialContext = dialContext
56 56
 			return nil
57 57
 		}
58
-		return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport)
58
+		return fmt.Errorf("cannot apply dialer to transport: %T", c.client.Transport)
59 59
 	}
60 60
 }
61 61
 
... ...
@@ -73,7 +73,7 @@ func WithHost(host string) Opt {
73 73
 		if transport, ok := c.client.Transport.(*http.Transport); ok {
74 74
 			return sockets.ConfigureTransport(transport, c.proto, c.addr)
75 75
 		}
76
-		return errors.Errorf("cannot apply host to transport: %T", c.client.Transport)
76
+		return fmt.Errorf("cannot apply host to transport: %T", c.client.Transport)
77 77
 	}
78 78
 }
79 79
 
... ...
@@ -140,7 +140,7 @@ func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt {
140 140
 	return func(c *Client) error {
141 141
 		transport, ok := c.client.Transport.(*http.Transport)
142 142
 		if !ok {
143
-			return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport)
143
+			return fmt.Errorf("cannot apply tls config to transport: %T", c.client.Transport)
144 144
 		}
145 145
 		config, err := tlsconfig.Client(tlsconfig.Options{
146 146
 			CAFile:             cacertPath,
... ...
@@ -149,7 +149,7 @@ func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt {
149 149
 			ExclusiveRootPools: true,
150 150
 		})
151 151
 		if err != nil {
152
-			return errors.Wrap(err, "failed to create tls config")
152
+			return fmt.Errorf("failed to create tls config: %w", err)
153 153
 		}
154 154
 		transport.TLSClientConfig = config
155 155
 		return nil
... ...
@@ -3,6 +3,8 @@ package client
3 3
 import (
4 4
 	"context"
5 5
 	"encoding/json"
6
+	"errors"
7
+	"fmt"
6 8
 	"io"
7 9
 	"net/http"
8 10
 	"net/url"
... ...
@@ -11,7 +13,6 @@ import (
11 11
 	"github.com/distribution/reference"
12 12
 	"github.com/moby/moby/api/types"
13 13
 	"github.com/moby/moby/api/types/registry"
14
-	"github.com/pkg/errors"
15 14
 )
16 15
 
17 16
 // PluginInstallOptions holds parameters to install a plugin.
... ...
@@ -36,7 +37,7 @@ type PluginInstallOptions struct {
36 36
 func (cli *Client) PluginInstall(ctx context.Context, name string, options PluginInstallOptions) (_ io.ReadCloser, retErr error) {
37 37
 	query := url.Values{}
38 38
 	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
39
-		return nil, errors.Wrap(err, "invalid remote reference")
39
+		return nil, fmt.Errorf("invalid remote reference: %w", err)
40 40
 	}
41 41
 	query.Set("remote", options.RemoteRef)
42 42
 
... ...
@@ -128,7 +129,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values,
128 128
 			return nil, err
129 129
 		}
130 130
 		if !accept {
131
-			return nil, errors.Errorf("permission denied while installing plugin %s", options.RemoteRef)
131
+			return nil, errors.New("permission denied while installing plugin " + options.RemoteRef)
132 132
 		}
133 133
 	}
134 134
 	return privileges, nil
... ...
@@ -2,6 +2,7 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"io"
6 7
 	"net/http"
7 8
 	"net/url"
... ...
@@ -9,7 +10,6 @@ import (
9 9
 	"github.com/distribution/reference"
10 10
 	"github.com/moby/moby/api/types"
11 11
 	"github.com/moby/moby/api/types/registry"
12
-	"github.com/pkg/errors"
13 12
 )
14 13
 
15 14
 // PluginUpgrade upgrades a plugin
... ...
@@ -24,7 +24,7 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options Plugi
24 24
 	}
25 25
 	query := url.Values{}
26 26
 	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
27
-		return nil, errors.Wrap(err, "invalid remote reference")
27
+		return nil, fmt.Errorf("invalid remote reference: %w", err)
28 28
 	}
29 29
 	query.Set("remote", options.RemoteRef)
30 30
 
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"bytes"
5 5
 	"context"
6 6
 	"encoding/json"
7
+	"errors"
7 8
 	"fmt"
8 9
 	"io"
9 10
 	"net"
... ...
@@ -14,7 +15,6 @@ import (
14 14
 	"strings"
15 15
 
16 16
 	"github.com/moby/moby/api/types"
17
-	"github.com/pkg/errors"
18 17
 )
19 18
 
20 19
 // head sends an http request to the docker API using the method HEAD.
... ...
@@ -144,7 +144,7 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
144 144
 	}
145 145
 
146 146
 	if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") {
147
-		return nil, errConnectionFailed{errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings")}
147
+		return nil, errConnectionFailed{fmt.Errorf("the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings: %w", err)}
148 148
 	}
149 149
 
150 150
 	// Don't decorate context sentinel errors; users may be comparing to
... ...
@@ -162,11 +162,11 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
162 162
 		// Unwrap the error to remove request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
163 163
 		// which are irrelevant if we weren't able to connect.
164 164
 		err = errors.Unwrap(err)
165
-		return nil, errConnectionFailed{errors.Wrapf(err, "failed to connect to the docker API at %v; check if the path is correct and if the daemon is running", cli.host)}
165
+		return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v; check if the path is correct and if the daemon is running: %w", cli.host, err)}
166 166
 	}
167 167
 	var dnsErr *net.DNSError
168 168
 	if errors.As(err, &dnsErr) {
169
-		return nil, errConnectionFailed{errors.Wrapf(dnsErr, "failed to connect to the docker API at %v", cli.host)}
169
+		return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v: %w", cli.host, dnsErr)}
170 170
 	}
171 171
 
172 172
 	var nErr net.Error
... ...
@@ -193,14 +193,14 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
193 193
 	if strings.Contains(err.Error(), `open //./pipe/docker_engine`) {
194 194
 		// Checks if client is running with elevated privileges
195 195
 		if f, elevatedErr := os.Open(`\\.\PHYSICALDRIVE0`); elevatedErr != nil {
196
-			err = errors.Wrap(err, "in the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect")
196
+			err = fmt.Errorf("in the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect: %w", err)
197 197
 		} else {
198 198
 			_ = f.Close()
199
-			err = errors.Wrap(err, "this error may indicate that the docker daemon is not running")
199
+			err = fmt.Errorf("this error may indicate that the docker daemon is not running: %w", err)
200 200
 		}
201 201
 	}
202 202
 
203
-	return nil, errConnectionFailed{errors.Wrap(err, "error during connect")}
203
+	return nil, errConnectionFailed{fmt.Errorf("error during connect: %w", err)}
204 204
 }
205 205
 
206 206
 func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
... ...
@@ -252,7 +252,7 @@ func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
252 252
 	if serverResp.Header.Get("Content-Type") == "application/json" {
253 253
 		var errorResponse types.ErrorResponse
254 254
 		if err := json.Unmarshal(body, &errorResponse); err != nil {
255
-			return errors.Wrap(err, "Error reading JSON")
255
+			return fmt.Errorf("error reading JSON: %w", err)
256 256
 		}
257 257
 		if errorResponse.Message == "" {
258 258
 			// Error-message is empty, which means that we successfully parsed the
... ...
@@ -285,7 +285,7 @@ func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
285 285
 		// situations where a proxy is involved, returning a HTML response.
286 286
 		daemonErr = errors.New(strings.TrimSpace(string(body)))
287 287
 	}
288
-	return errors.Wrap(daemonErr, "Error response from daemon")
288
+	return fmt.Errorf("Error response from daemon: %w", daemonErr)
289 289
 }
290 290
 
291 291
 func (cli *Client) addHeaders(req *http.Request, headers http.Header) *http.Request {
... ...
@@ -153,7 +153,7 @@ func TestResponseErrors(t *testing.T) {
153 153
 			doc:         "malformed JSON",
154 154
 			contentType: "application/json",
155 155
 			response:    `{"message":"Some error occurred`,
156
-			expected:    `Error reading JSON: unexpected end of JSON input`,
156
+			expected:    `error reading JSON: unexpected end of JSON input`,
157 157
 		},
158 158
 		{
159 159
 			// Server response that's valid JSON, but not the expected (types.ErrorResponse) scheme
... ...
@@ -204,7 +204,7 @@ func TestResponseErrors(t *testing.T) {
204 204
   <p>If this problem persists, please <a href="https://example.com/support">contact support</a>.</p>
205 205
 </body>
206 206
 </html>`,
207
-			expected: `Error reading JSON: invalid character '<' looking for beginning of value`,
207
+			expected: `error reading JSON: invalid character '<' looking for beginning of value`,
208 208
 		},
209 209
 	}
210 210
 	for _, tc := range tests {
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"context"
5 5
 	"encoding/json"
6
+	"errors"
6 7
 	"fmt"
7 8
 	"net/http"
8 9
 	"strings"
... ...
@@ -12,7 +13,6 @@ import (
12 12
 	"github.com/moby/moby/api/types/swarm"
13 13
 	"github.com/moby/moby/api/types/versions"
14 14
 	"github.com/opencontainers/go-digest"
15
-	"github.com/pkg/errors"
16 15
 )
17 16
 
18 17
 // ServiceCreate creates a new service.
... ...
@@ -200,11 +200,11 @@ func validateAPIVersion(c swarm.ServiceSpec, apiVersion string) error {
200 200
 	for _, m := range c.TaskTemplate.ContainerSpec.Mounts {
201 201
 		if m.BindOptions != nil {
202 202
 			if m.BindOptions.NonRecursive && versions.LessThan(apiVersion, "1.40") {
203
-				return errors.Errorf("bind-recursive=disabled requires API v1.40 or later")
203
+				return errors.New("bind-recursive=disabled requires API v1.40 or later")
204 204
 			}
205 205
 			// ReadOnlyNonRecursive can be safely ignored when API < 1.44
206 206
 			if m.BindOptions.ReadOnlyForceRecursive && versions.LessThan(apiVersion, "1.44") {
207
-				return errors.Errorf("bind-recursive=readonly requires API v1.44 or later")
207
+				return errors.New("bind-recursive=readonly requires API v1.44 or later")
208 208
 			}
209 209
 		}
210 210
 	}
... ...
@@ -2,13 +2,13 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"io"
6 7
 	"net/url"
7 8
 	"time"
8 9
 
9 10
 	"github.com/moby/moby/api/types/container"
10 11
 	timetypes "github.com/moby/moby/api/types/time"
11
-	"github.com/pkg/errors"
12 12
 )
13 13
 
14 14
 // ServiceLogs returns the logs generated by a service in an [io.ReadCloser].
... ...
@@ -31,7 +31,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options co
31 31
 	if options.Since != "" {
32 32
 		ts, err := timetypes.GetTimestamp(options.Since, time.Now())
33 33
 		if err != nil {
34
-			return nil, errors.Wrap(err, `invalid value for "since"`)
34
+			return nil, fmt.Errorf(`invalid value for "since": %w`, err)
35 35
 		}
36 36
 		query.Set("since", ts)
37 37
 	}
... ...
@@ -3,12 +3,12 @@ package client
3 3
 import (
4 4
 	"context"
5 5
 	"encoding/json"
6
+	"fmt"
6 7
 	"net/url"
7 8
 	"strconv"
8 9
 
9 10
 	"github.com/moby/moby/api/types/build"
10 11
 	"github.com/moby/moby/api/types/filters"
11
-	"github.com/pkg/errors"
12 12
 )
13 13
 
14 14
 // BuildCachePrune requests the daemon to delete unused cache data.
... ...
@@ -36,7 +36,7 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts build.CachePruneOpt
36 36
 	}
37 37
 	f, err := filters.ToJSON(opts.Filters)
38 38
 	if err != nil {
39
-		return nil, errors.Wrap(err, "prune could not marshal filters option")
39
+		return nil, fmt.Errorf("prune could not marshal filters option: %w", err)
40 40
 	}
41 41
 	query.Set("filters", f)
42 42
 
... ...
@@ -49,7 +49,7 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts build.CachePruneOpt
49 49
 
50 50
 	report := build.CachePruneReport{}
51 51
 	if err := json.NewDecoder(resp.Body).Decode(&report); err != nil {
52
-		return nil, errors.Wrap(err, "error retrieving disk usage")
52
+		return nil, fmt.Errorf("error retrieving disk usage: %w", err)
53 53
 	}
54 54
 
55 55
 	return &report, nil
... ...
@@ -44,6 +44,8 @@ package client
44 44
 import (
45 45
 	"context"
46 46
 	"crypto/tls"
47
+	"errors"
48
+	"fmt"
47 49
 	"net"
48 50
 	"net/http"
49 51
 	"net/url"
... ...
@@ -56,7 +58,6 @@ import (
56 56
 	"github.com/docker/go-connections/sockets"
57 57
 	"github.com/moby/moby/api/types"
58 58
 	"github.com/moby/moby/api/types/versions"
59
-	"github.com/pkg/errors"
60 59
 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
61 60
 )
62 61
 
... ...
@@ -421,7 +422,7 @@ func (cli *Client) HTTPClient() *http.Client {
421 421
 func ParseHostURL(host string) (*url.URL, error) {
422 422
 	proto, addr, ok := strings.Cut(host, "://")
423 423
 	if !ok || addr == "" {
424
-		return nil, errors.Errorf("unable to parse docker host `%s`", host)
424
+		return nil, fmt.Errorf("unable to parse docker host `%s`", host)
425 425
 	}
426 426
 
427 427
 	var basePath string
... ...
@@ -2,13 +2,13 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"io"
6 7
 	"net/url"
7 8
 	"time"
8 9
 
9 10
 	"github.com/moby/moby/api/types/container"
10 11
 	timetypes "github.com/moby/moby/api/types/time"
11
-	"github.com/pkg/errors"
12 12
 )
13 13
 
14 14
 // ContainerLogs returns the logs generated by a container in an [io.ReadCloser].
... ...
@@ -55,7 +55,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option
55 55
 	if options.Since != "" {
56 56
 		ts, err := timetypes.GetTimestamp(options.Since, time.Now())
57 57
 		if err != nil {
58
-			return nil, errors.Wrap(err, `invalid value for "since"`)
58
+			return nil, fmt.Errorf(`invalid value for "since": %w`, err)
59 59
 		}
60 60
 		query.Set("since", ts)
61 61
 	}
... ...
@@ -63,7 +63,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option
63 63
 	if options.Until != "" {
64 64
 		ts, err := timetypes.GetTimestamp(options.Until, time.Now())
65 65
 		if err != nil {
66
-			return nil, errors.Wrap(err, `invalid value for "until"`)
66
+			return nil, fmt.Errorf(`invalid value for "until": %w`, err)
67 67
 		}
68 68
 		query.Set("until", ts)
69 69
 	}
... ...
@@ -10,7 +10,6 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/moby/moby/api/types/versions"
13
-	"github.com/pkg/errors"
14 13
 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
15 14
 )
16 15
 
... ...
@@ -56,11 +55,11 @@ func setupHijackConn(dialer func(context.Context) (net.Conn, error), req *http.R
56 56
 
57 57
 	conn, err := dialer(ctx)
58 58
 	if err != nil {
59
-		return nil, "", errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
59
+		return nil, "", fmt.Errorf("cannot connect to the Docker daemon. Is 'docker daemon' running on this host?: %w", err)
60 60
 	}
61 61
 	defer func() {
62 62
 		if retErr != nil {
63
-			conn.Close()
63
+			_ = conn.Close()
64 64
 		}
65 65
 	}()
66 66
 
... ...
@@ -2,21 +2,22 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"errors"
6
+	"fmt"
5 7
 	"net/url"
6 8
 
7 9
 	"github.com/distribution/reference"
8
-	"github.com/pkg/errors"
9 10
 )
10 11
 
11 12
 // ImageTag tags an image in the docker host
12 13
 func (cli *Client) ImageTag(ctx context.Context, source, target string) error {
13 14
 	if _, err := reference.ParseAnyReference(source); err != nil {
14
-		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", source)
15
+		return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", source, err)
15 16
 	}
16 17
 
17 18
 	ref, err := reference.ParseNormalizedNamed(target)
18 19
 	if err != nil {
19
-		return errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", target)
20
+		return fmt.Errorf("error parsing reference: %q is not a valid repository/tag: %w", target, err)
20 21
 	}
21 22
 
22 23
 	if _, isCanonical := ref.(reference.Canonical); isCanonical {
... ...
@@ -2,6 +2,7 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"net"
6 7
 	"net/http"
7 8
 	"os"
... ...
@@ -11,7 +12,6 @@ import (
11 11
 
12 12
 	"github.com/docker/go-connections/sockets"
13 13
 	"github.com/docker/go-connections/tlsconfig"
14
-	"github.com/pkg/errors"
15 14
 	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
16 15
 	"go.opentelemetry.io/otel/trace"
17 16
 )
... ...
@@ -55,7 +55,7 @@ func WithDialContext(dialContext func(ctx context.Context, network, addr string)
55 55
 			transport.DialContext = dialContext
56 56
 			return nil
57 57
 		}
58
-		return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport)
58
+		return fmt.Errorf("cannot apply dialer to transport: %T", c.client.Transport)
59 59
 	}
60 60
 }
61 61
 
... ...
@@ -73,7 +73,7 @@ func WithHost(host string) Opt {
73 73
 		if transport, ok := c.client.Transport.(*http.Transport); ok {
74 74
 			return sockets.ConfigureTransport(transport, c.proto, c.addr)
75 75
 		}
76
-		return errors.Errorf("cannot apply host to transport: %T", c.client.Transport)
76
+		return fmt.Errorf("cannot apply host to transport: %T", c.client.Transport)
77 77
 	}
78 78
 }
79 79
 
... ...
@@ -140,7 +140,7 @@ func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt {
140 140
 	return func(c *Client) error {
141 141
 		transport, ok := c.client.Transport.(*http.Transport)
142 142
 		if !ok {
143
-			return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport)
143
+			return fmt.Errorf("cannot apply tls config to transport: %T", c.client.Transport)
144 144
 		}
145 145
 		config, err := tlsconfig.Client(tlsconfig.Options{
146 146
 			CAFile:             cacertPath,
... ...
@@ -149,7 +149,7 @@ func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt {
149 149
 			ExclusiveRootPools: true,
150 150
 		})
151 151
 		if err != nil {
152
-			return errors.Wrap(err, "failed to create tls config")
152
+			return fmt.Errorf("failed to create tls config: %w", err)
153 153
 		}
154 154
 		transport.TLSClientConfig = config
155 155
 		return nil
... ...
@@ -3,6 +3,8 @@ package client
3 3
 import (
4 4
 	"context"
5 5
 	"encoding/json"
6
+	"errors"
7
+	"fmt"
6 8
 	"io"
7 9
 	"net/http"
8 10
 	"net/url"
... ...
@@ -11,7 +13,6 @@ import (
11 11
 	"github.com/distribution/reference"
12 12
 	"github.com/moby/moby/api/types"
13 13
 	"github.com/moby/moby/api/types/registry"
14
-	"github.com/pkg/errors"
15 14
 )
16 15
 
17 16
 // PluginInstallOptions holds parameters to install a plugin.
... ...
@@ -36,7 +37,7 @@ type PluginInstallOptions struct {
36 36
 func (cli *Client) PluginInstall(ctx context.Context, name string, options PluginInstallOptions) (_ io.ReadCloser, retErr error) {
37 37
 	query := url.Values{}
38 38
 	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
39
-		return nil, errors.Wrap(err, "invalid remote reference")
39
+		return nil, fmt.Errorf("invalid remote reference: %w", err)
40 40
 	}
41 41
 	query.Set("remote", options.RemoteRef)
42 42
 
... ...
@@ -128,7 +129,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values,
128 128
 			return nil, err
129 129
 		}
130 130
 		if !accept {
131
-			return nil, errors.Errorf("permission denied while installing plugin %s", options.RemoteRef)
131
+			return nil, errors.New("permission denied while installing plugin " + options.RemoteRef)
132 132
 		}
133 133
 	}
134 134
 	return privileges, nil
... ...
@@ -2,6 +2,7 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"io"
6 7
 	"net/http"
7 8
 	"net/url"
... ...
@@ -9,7 +10,6 @@ import (
9 9
 	"github.com/distribution/reference"
10 10
 	"github.com/moby/moby/api/types"
11 11
 	"github.com/moby/moby/api/types/registry"
12
-	"github.com/pkg/errors"
13 12
 )
14 13
 
15 14
 // PluginUpgrade upgrades a plugin
... ...
@@ -24,7 +24,7 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options Plugi
24 24
 	}
25 25
 	query := url.Values{}
26 26
 	if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil {
27
-		return nil, errors.Wrap(err, "invalid remote reference")
27
+		return nil, fmt.Errorf("invalid remote reference: %w", err)
28 28
 	}
29 29
 	query.Set("remote", options.RemoteRef)
30 30
 
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"bytes"
5 5
 	"context"
6 6
 	"encoding/json"
7
+	"errors"
7 8
 	"fmt"
8 9
 	"io"
9 10
 	"net"
... ...
@@ -14,7 +15,6 @@ import (
14 14
 	"strings"
15 15
 
16 16
 	"github.com/moby/moby/api/types"
17
-	"github.com/pkg/errors"
18 17
 )
19 18
 
20 19
 // head sends an http request to the docker API using the method HEAD.
... ...
@@ -144,7 +144,7 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
144 144
 	}
145 145
 
146 146
 	if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") {
147
-		return nil, errConnectionFailed{errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings")}
147
+		return nil, errConnectionFailed{fmt.Errorf("the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings: %w", err)}
148 148
 	}
149 149
 
150 150
 	// Don't decorate context sentinel errors; users may be comparing to
... ...
@@ -162,11 +162,11 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
162 162
 		// Unwrap the error to remove request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
163 163
 		// which are irrelevant if we weren't able to connect.
164 164
 		err = errors.Unwrap(err)
165
-		return nil, errConnectionFailed{errors.Wrapf(err, "failed to connect to the docker API at %v; check if the path is correct and if the daemon is running", cli.host)}
165
+		return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v; check if the path is correct and if the daemon is running: %w", cli.host, err)}
166 166
 	}
167 167
 	var dnsErr *net.DNSError
168 168
 	if errors.As(err, &dnsErr) {
169
-		return nil, errConnectionFailed{errors.Wrapf(dnsErr, "failed to connect to the docker API at %v", cli.host)}
169
+		return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v: %w", cli.host, dnsErr)}
170 170
 	}
171 171
 
172 172
 	var nErr net.Error
... ...
@@ -193,14 +193,14 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
193 193
 	if strings.Contains(err.Error(), `open //./pipe/docker_engine`) {
194 194
 		// Checks if client is running with elevated privileges
195 195
 		if f, elevatedErr := os.Open(`\\.\PHYSICALDRIVE0`); elevatedErr != nil {
196
-			err = errors.Wrap(err, "in the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect")
196
+			err = fmt.Errorf("in the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect: %w", err)
197 197
 		} else {
198 198
 			_ = f.Close()
199
-			err = errors.Wrap(err, "this error may indicate that the docker daemon is not running")
199
+			err = fmt.Errorf("this error may indicate that the docker daemon is not running: %w", err)
200 200
 		}
201 201
 	}
202 202
 
203
-	return nil, errConnectionFailed{errors.Wrap(err, "error during connect")}
203
+	return nil, errConnectionFailed{fmt.Errorf("error during connect: %w", err)}
204 204
 }
205 205
 
206 206
 func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
... ...
@@ -252,7 +252,7 @@ func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
252 252
 	if serverResp.Header.Get("Content-Type") == "application/json" {
253 253
 		var errorResponse types.ErrorResponse
254 254
 		if err := json.Unmarshal(body, &errorResponse); err != nil {
255
-			return errors.Wrap(err, "Error reading JSON")
255
+			return fmt.Errorf("error reading JSON: %w", err)
256 256
 		}
257 257
 		if errorResponse.Message == "" {
258 258
 			// Error-message is empty, which means that we successfully parsed the
... ...
@@ -285,7 +285,7 @@ func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
285 285
 		// situations where a proxy is involved, returning a HTML response.
286 286
 		daemonErr = errors.New(strings.TrimSpace(string(body)))
287 287
 	}
288
-	return errors.Wrap(daemonErr, "Error response from daemon")
288
+	return fmt.Errorf("Error response from daemon: %w", daemonErr)
289 289
 }
290 290
 
291 291
 func (cli *Client) addHeaders(req *http.Request, headers http.Header) *http.Request {
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"context"
5 5
 	"encoding/json"
6
+	"errors"
6 7
 	"fmt"
7 8
 	"net/http"
8 9
 	"strings"
... ...
@@ -12,7 +13,6 @@ import (
12 12
 	"github.com/moby/moby/api/types/swarm"
13 13
 	"github.com/moby/moby/api/types/versions"
14 14
 	"github.com/opencontainers/go-digest"
15
-	"github.com/pkg/errors"
16 15
 )
17 16
 
18 17
 // ServiceCreate creates a new service.
... ...
@@ -200,11 +200,11 @@ func validateAPIVersion(c swarm.ServiceSpec, apiVersion string) error {
200 200
 	for _, m := range c.TaskTemplate.ContainerSpec.Mounts {
201 201
 		if m.BindOptions != nil {
202 202
 			if m.BindOptions.NonRecursive && versions.LessThan(apiVersion, "1.40") {
203
-				return errors.Errorf("bind-recursive=disabled requires API v1.40 or later")
203
+				return errors.New("bind-recursive=disabled requires API v1.40 or later")
204 204
 			}
205 205
 			// ReadOnlyNonRecursive can be safely ignored when API < 1.44
206 206
 			if m.BindOptions.ReadOnlyForceRecursive && versions.LessThan(apiVersion, "1.44") {
207
-				return errors.Errorf("bind-recursive=readonly requires API v1.44 or later")
207
+				return errors.New("bind-recursive=readonly requires API v1.44 or later")
208 208
 			}
209 209
 		}
210 210
 	}
... ...
@@ -2,13 +2,13 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"fmt"
5 6
 	"io"
6 7
 	"net/url"
7 8
 	"time"
8 9
 
9 10
 	"github.com/moby/moby/api/types/container"
10 11
 	timetypes "github.com/moby/moby/api/types/time"
11
-	"github.com/pkg/errors"
12 12
 )
13 13
 
14 14
 // ServiceLogs returns the logs generated by a service in an [io.ReadCloser].
... ...
@@ -31,7 +31,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options co
31 31
 	if options.Since != "" {
32 32
 		ts, err := timetypes.GetTimestamp(options.Since, time.Now())
33 33
 		if err != nil {
34
-			return nil, errors.Wrap(err, `invalid value for "since"`)
34
+			return nil, fmt.Errorf(`invalid value for "since": %w`, err)
35 35
 		}
36 36
 		query.Set("since", ts)
37 37
 	}