The API does not produce these as a response; the fields in the Ping
struct, including the Swarm status are propagated from headers returned
by the /_ping endpoint.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -214,16 +214,6 @@ type Info struct {
|
| 214 | 214 |
Warnings []string `json:",omitempty"` |
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 |
-// Status provides information about the current swarm status and role, |
|
| 218 |
-// obtained from the "Swarm" header in the API response. |
|
| 219 |
-type Status struct {
|
|
| 220 |
- // NodeState represents the state of the node. |
|
| 221 |
- NodeState LocalNodeState |
|
| 222 |
- |
|
| 223 |
- // ControlAvailable indicates if the node is a swarm manager. |
|
| 224 |
- ControlAvailable bool |
|
| 225 |
-} |
|
| 226 |
- |
|
| 227 | 217 |
// Peer represents a peer. |
| 228 | 218 |
type Peer struct {
|
| 229 | 219 |
NodeID string |
| ... | ... |
@@ -1,10 +1,5 @@ |
| 1 | 1 |
package types |
| 2 | 2 |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/moby/moby/api/types/build" |
|
| 5 |
- "github.com/moby/moby/api/types/swarm" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 | 3 |
const ( |
| 9 | 4 |
// MediaTypeRawStream is vendor specific MIME-Type set for raw TTY streams |
| 10 | 5 |
MediaTypeRawStream = "application/vnd.docker.raw-stream" |
| ... | ... |
@@ -22,24 +17,6 @@ const ( |
| 22 | 22 |
MediaTypeJSONSequence = "application/json-seq" |
| 23 | 23 |
) |
| 24 | 24 |
|
| 25 |
-// Ping contains response of Engine API: |
|
| 26 |
-// GET "/_ping" |
|
| 27 |
-type Ping struct {
|
|
| 28 |
- APIVersion string |
|
| 29 |
- OSType string |
|
| 30 |
- Experimental bool |
|
| 31 |
- BuilderVersion build.BuilderVersion |
|
| 32 |
- |
|
| 33 |
- // SwarmStatus provides information about the current swarm status of the |
|
| 34 |
- // engine, obtained from the "Swarm" header in the API response. |
|
| 35 |
- // |
|
| 36 |
- // It can be a nil struct if the API version does not provide this header |
|
| 37 |
- // in the ping response, or if an error occurred, in which case the client |
|
| 38 |
- // should use other ways to get the current swarm status, such as the /swarm |
|
| 39 |
- // endpoint. |
|
| 40 |
- SwarmStatus *swarm.Status |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 | 25 |
// ComponentVersion describes the version information for a specific component. |
| 44 | 26 |
type ComponentVersion struct {
|
| 45 | 27 |
Name string |
| ... | ... |
@@ -56,7 +56,6 @@ import ( |
| 56 | 56 |
|
| 57 | 57 |
cerrdefs "github.com/containerd/errdefs" |
| 58 | 58 |
"github.com/docker/go-connections/sockets" |
| 59 |
- "github.com/moby/moby/api/types" |
|
| 60 | 59 |
"github.com/moby/moby/api/types/versions" |
| 61 | 60 |
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" |
| 62 | 61 |
) |
| ... | ... |
@@ -270,7 +269,7 @@ func (cli *Client) checkVersion(ctx context.Context) error {
|
| 270 | 270 |
return nil |
| 271 | 271 |
} |
| 272 | 272 |
|
| 273 |
- ping, err := cli.Ping(ctx) |
|
| 273 |
+ ping, err := cli.Ping(ctx, PingOptions{})
|
|
| 274 | 274 |
if err != nil {
|
| 275 | 275 |
return err |
| 276 | 276 |
} |
| ... | ... |
@@ -317,7 +316,7 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
|
| 317 | 317 |
cli.negotiateLock.Lock() |
| 318 | 318 |
defer cli.negotiateLock.Unlock() |
| 319 | 319 |
|
| 320 |
- ping, err := cli.Ping(ctx) |
|
| 320 |
+ ping, err := cli.Ping(ctx, PingOptions{})
|
|
| 321 | 321 |
if err != nil {
|
| 322 | 322 |
// FIXME(thaJeztah): Ping returns an error when failing to connect to the API; we should not swallow the error here, and instead returning it. |
| 323 | 323 |
return |
| ... | ... |
@@ -338,7 +337,8 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
|
| 338 | 338 |
// |
| 339 | 339 |
// If the API server's ping response does not contain an API version, it falls |
| 340 | 340 |
// back to the oldest API version supported. |
| 341 |
-func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) {
|
|
| 341 |
+func (cli *Client) NegotiateAPIVersionPing(pingResponse PingResult) {
|
|
| 342 |
+ // TODO(thaJeztah): should this take a "Ping" option? It only consumes the version. This method should be removed overall and not be exported. |
|
| 342 | 343 |
if !cli.manualOverride {
|
| 343 | 344 |
// Avoid concurrent modification of version-related fields |
| 344 | 345 |
cli.negotiateLock.Lock() |
| ... | ... |
@@ -35,7 +35,7 @@ type stableAPIClient interface {
|
| 35 | 35 |
DaemonHost() string |
| 36 | 36 |
ServerVersion(ctx context.Context) (types.Version, error) |
| 37 | 37 |
NegotiateAPIVersion(ctx context.Context) |
| 38 |
- NegotiateAPIVersionPing(types.Ping) |
|
| 38 |
+ NegotiateAPIVersionPing(PingResult) |
|
| 39 | 39 |
HijackDialer |
| 40 | 40 |
Dialer() func(context.Context) (net.Conn, error) |
| 41 | 41 |
Close() error |
| ... | ... |
@@ -187,7 +187,7 @@ type SystemAPIClient interface {
|
| 187 | 187 |
Info(ctx context.Context) (system.Info, error) |
| 188 | 188 |
RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) |
| 189 | 189 |
DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) |
| 190 |
- Ping(ctx context.Context) (types.Ping, error) |
|
| 190 |
+ Ping(ctx context.Context, options PingOptions) (PingResult, error) |
|
| 191 | 191 |
} |
| 192 | 192 |
|
| 193 | 193 |
// VolumeAPIClient defines API client methods for the volumes |
| ... | ... |
@@ -11,7 +11,6 @@ import ( |
| 11 | 11 |
"strings" |
| 12 | 12 |
"testing" |
| 13 | 13 |
|
| 14 |
- "github.com/moby/moby/api/types" |
|
| 15 | 14 |
"gotest.tools/v3/assert" |
| 16 | 15 |
is "gotest.tools/v3/assert/cmp" |
| 17 | 16 |
"gotest.tools/v3/skip" |
| ... | ... |
@@ -267,7 +266,7 @@ func TestNegotiateAPIVersionEmpty(t *testing.T) {
|
| 267 | 267 |
const expected = fallbackAPIVersion |
| 268 | 268 |
|
| 269 | 269 |
// test downgrade |
| 270 |
- client.NegotiateAPIVersionPing(types.Ping{})
|
|
| 270 |
+ client.NegotiateAPIVersionPing(PingResult{})
|
|
| 271 | 271 |
assert.Check(t, is.Equal(client.ClientVersion(), expected)) |
| 272 | 272 |
} |
| 273 | 273 |
|
| ... | ... |
@@ -330,7 +329,7 @@ func TestNegotiateAPIVersion(t *testing.T) {
|
| 330 | 330 |
} |
| 331 | 331 |
client, err := NewClientWithOpts(opts...) |
| 332 | 332 |
assert.NilError(t, err) |
| 333 |
- client.NegotiateAPIVersionPing(types.Ping{APIVersion: tc.pingVersion})
|
|
| 333 |
+ client.NegotiateAPIVersionPing(PingResult{APIVersion: tc.pingVersion})
|
|
| 334 | 334 |
assert.Check(t, is.Equal(tc.expectedVersion, client.ClientVersion())) |
| 335 | 335 |
}) |
| 336 | 336 |
} |
| ... | ... |
@@ -346,7 +345,7 @@ func TestNegotiateAPIVersionOverride(t *testing.T) {
|
| 346 | 346 |
assert.NilError(t, err) |
| 347 | 347 |
|
| 348 | 348 |
// test that we honored the env var |
| 349 |
- client.NegotiateAPIVersionPing(types.Ping{APIVersion: "1.24"})
|
|
| 349 |
+ client.NegotiateAPIVersionPing(PingResult{APIVersion: "1.24"})
|
|
| 350 | 350 |
assert.Check(t, is.Equal(client.ClientVersion(), expected)) |
| 351 | 351 |
} |
| 352 | 352 |
|
| ... | ... |
@@ -404,7 +403,7 @@ func TestNegotiateAPIVersionWithEmptyVersion(t *testing.T) {
|
| 404 | 404 |
assert.NilError(t, err) |
| 405 | 405 |
|
| 406 | 406 |
const expected = "1.50" |
| 407 |
- client.NegotiateAPIVersionPing(types.Ping{APIVersion: expected})
|
|
| 407 |
+ client.NegotiateAPIVersionPing(PingResult{APIVersion: expected})
|
|
| 408 | 408 |
assert.Check(t, is.Equal(client.ClientVersion(), expected)) |
| 409 | 409 |
} |
| 410 | 410 |
|
| ... | ... |
@@ -415,7 +414,7 @@ func TestNegotiateAPIVersionWithFixedVersion(t *testing.T) {
|
| 415 | 415 |
client, err := NewClientWithOpts(WithVersion(customVersion)) |
| 416 | 416 |
assert.NilError(t, err) |
| 417 | 417 |
|
| 418 |
- client.NegotiateAPIVersionPing(types.Ping{APIVersion: "1.49"})
|
|
| 418 |
+ client.NegotiateAPIVersionPing(PingResult{APIVersion: "1.49"})
|
|
| 419 | 419 |
assert.Check(t, is.Equal(client.ClientVersion(), customVersion)) |
| 420 | 420 |
} |
| 421 | 421 |
|
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "context" |
|
| 5 | 4 |
"net/http" |
| 6 | 5 |
"runtime" |
| 7 | 6 |
"testing" |
| ... | ... |
@@ -72,7 +71,7 @@ func TestWithUserAgent(t *testing.T) {
|
| 72 | 72 |
}), |
| 73 | 73 |
) |
| 74 | 74 |
assert.NilError(t, err) |
| 75 |
- _, err = c.Ping(context.Background()) |
|
| 75 |
+ _, err = c.Ping(t.Context(), PingOptions{})
|
|
| 76 | 76 |
assert.NilError(t, err) |
| 77 | 77 |
assert.NilError(t, c.Close()) |
| 78 | 78 |
}) |
| ... | ... |
@@ -87,7 +86,7 @@ func TestWithUserAgent(t *testing.T) {
|
| 87 | 87 |
}), |
| 88 | 88 |
) |
| 89 | 89 |
assert.NilError(t, err) |
| 90 |
- _, err = c.Ping(context.Background()) |
|
| 90 |
+ _, err = c.Ping(t.Context(), PingOptions{})
|
|
| 91 | 91 |
assert.NilError(t, err) |
| 92 | 92 |
assert.NilError(t, c.Close()) |
| 93 | 93 |
}) |
| ... | ... |
@@ -101,7 +100,7 @@ func TestWithUserAgent(t *testing.T) {
|
| 101 | 101 |
}), |
| 102 | 102 |
) |
| 103 | 103 |
assert.NilError(t, err) |
| 104 |
- _, err = c.Ping(context.Background()) |
|
| 104 |
+ _, err = c.Ping(t.Context(), PingOptions{})
|
|
| 105 | 105 |
assert.NilError(t, err) |
| 106 | 106 |
assert.NilError(t, c.Close()) |
| 107 | 107 |
}) |
| ... | ... |
@@ -115,7 +114,7 @@ func TestWithUserAgent(t *testing.T) {
|
| 115 | 115 |
}), |
| 116 | 116 |
) |
| 117 | 117 |
assert.NilError(t, err) |
| 118 |
- _, err = c.Ping(context.Background()) |
|
| 118 |
+ _, err = c.Ping(t.Context(), PingOptions{})
|
|
| 119 | 119 |
assert.NilError(t, err) |
| 120 | 120 |
assert.NilError(t, c.Close()) |
| 121 | 121 |
}) |
| ... | ... |
@@ -130,7 +129,7 @@ func TestWithUserAgent(t *testing.T) {
|
| 130 | 130 |
}), |
| 131 | 131 |
) |
| 132 | 132 |
assert.NilError(t, err) |
| 133 |
- _, err = c.Ping(context.Background()) |
|
| 133 |
+ _, err = c.Ping(t.Context(), PingOptions{})
|
|
| 134 | 134 |
assert.NilError(t, err) |
| 135 | 135 |
assert.NilError(t, c.Close()) |
| 136 | 136 |
}) |
| ... | ... |
@@ -6,11 +6,42 @@ import ( |
| 6 | 6 |
"path" |
| 7 | 7 |
"strings" |
| 8 | 8 |
|
| 9 |
- "github.com/moby/moby/api/types" |
|
| 10 | 9 |
"github.com/moby/moby/api/types/build" |
| 11 | 10 |
"github.com/moby/moby/api/types/swarm" |
| 12 | 11 |
) |
| 13 | 12 |
|
| 13 |
+// PingOptions holds options for [client.Ping]. |
|
| 14 |
+type PingOptions struct {
|
|
| 15 |
+ // Add future optional parameters here |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+// PingResult holds the result of a [Client.Ping] API call. |
|
| 19 |
+type PingResult struct {
|
|
| 20 |
+ APIVersion string |
|
| 21 |
+ OSType string |
|
| 22 |
+ Experimental bool |
|
| 23 |
+ BuilderVersion build.BuilderVersion |
|
| 24 |
+ |
|
| 25 |
+ // SwarmStatus provides information about the current swarm status of the |
|
| 26 |
+ // engine, obtained from the "Swarm" header in the API response. |
|
| 27 |
+ // |
|
| 28 |
+ // It can be a nil struct if the API version does not provide this header |
|
| 29 |
+ // in the ping response, or if an error occurred, in which case the client |
|
| 30 |
+ // should use other ways to get the current swarm status, such as the /swarm |
|
| 31 |
+ // endpoint. |
|
| 32 |
+ SwarmStatus *SwarmStatus |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// SwarmStatus provides information about the current swarm status and role, |
|
| 36 |
+// obtained from the "Swarm" header in the API response. |
|
| 37 |
+type SwarmStatus struct {
|
|
| 38 |
+ // NodeState represents the state of the node. |
|
| 39 |
+ NodeState swarm.LocalNodeState |
|
| 40 |
+ |
|
| 41 |
+ // ControlAvailable indicates if the node is a swarm manager. |
|
| 42 |
+ ControlAvailable bool |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 14 | 45 |
// Ping pings the server and returns the value of the "Docker-Experimental", |
| 15 | 46 |
// "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use |
| 16 | 47 |
// a HEAD request on the endpoint, but falls back to GET if HEAD is not supported |
| ... | ... |
@@ -18,13 +49,13 @@ import ( |
| 18 | 18 |
// may be returned if the daemon is in an unhealthy state, but returns errors |
| 19 | 19 |
// for other non-success status codes, failing to connect to the API, or failing |
| 20 | 20 |
// to parse the API response. |
| 21 |
-func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
|
| 21 |
+func (cli *Client) Ping(ctx context.Context, options PingOptions) (PingResult, error) {
|
|
| 22 | 22 |
// Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest() |
| 23 | 23 |
// because ping requests are used during API version negotiation, so we want |
| 24 | 24 |
// to hit the non-versioned /_ping endpoint, not /v1.xx/_ping |
| 25 | 25 |
req, err := cli.buildRequest(ctx, http.MethodHead, path.Join(cli.basePath, "/_ping"), nil, nil) |
| 26 | 26 |
if err != nil {
|
| 27 |
- return types.Ping{}, err
|
|
| 27 |
+ return PingResult{}, err
|
|
| 28 | 28 |
} |
| 29 | 29 |
resp, err := cli.doRequest(req) |
| 30 | 30 |
defer ensureReaderClosed(resp) |
| ... | ... |
@@ -33,7 +64,7 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
| 33 | 33 |
// we got a "OK" (200) status. For non-200 status-codes, we fall |
| 34 | 34 |
// back to doing a GET request, as a HEAD request won't have a |
| 35 | 35 |
// response-body to get error details from. |
| 36 |
- return newPingResponse(resp), nil |
|
| 36 |
+ return newPingResult(resp), nil |
|
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 | 39 |
// HEAD failed or returned a non-OK status; fallback to GET. |
| ... | ... |
@@ -42,29 +73,29 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
| 42 | 42 |
defer ensureReaderClosed(resp) |
| 43 | 43 |
if err != nil {
|
| 44 | 44 |
// Failed to connect. |
| 45 |
- return types.Ping{}, err
|
|
| 45 |
+ return PingResult{}, err
|
|
| 46 | 46 |
} |
| 47 | 47 |
|
| 48 | 48 |
// GET request succeeded but may have returned a non-200 status. |
| 49 | 49 |
// Return a Ping response, together with any error returned by |
| 50 | 50 |
// the API server. |
| 51 |
- return newPingResponse(resp), checkResponseErr(resp) |
|
| 51 |
+ return newPingResult(resp), checkResponseErr(resp) |
|
| 52 | 52 |
} |
| 53 | 53 |
|
| 54 |
-func newPingResponse(resp *http.Response) types.Ping {
|
|
| 54 |
+func newPingResult(resp *http.Response) PingResult {
|
|
| 55 | 55 |
if resp == nil {
|
| 56 |
- return types.Ping{}
|
|
| 56 |
+ return PingResult{}
|
|
| 57 | 57 |
} |
| 58 |
- var swarmStatus *swarm.Status |
|
| 58 |
+ var swarmStatus *SwarmStatus |
|
| 59 | 59 |
if si := resp.Header.Get("Swarm"); si != "" {
|
| 60 | 60 |
state, role, _ := strings.Cut(si, "/") |
| 61 |
- swarmStatus = &swarm.Status{
|
|
| 61 |
+ swarmStatus = &SwarmStatus{
|
|
| 62 | 62 |
NodeState: swarm.LocalNodeState(state), |
| 63 | 63 |
ControlAvailable: role == "manager", |
| 64 | 64 |
} |
| 65 | 65 |
} |
| 66 | 66 |
|
| 67 |
- return types.Ping{
|
|
| 67 |
+ return PingResult{
|
|
| 68 | 68 |
APIVersion: resp.Header.Get("Api-Version"),
|
| 69 | 69 |
OSType: resp.Header.Get("Ostype"),
|
| 70 | 70 |
Experimental: resp.Header.Get("Docker-Experimental") == "true",
|
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "context" |
|
| 5 | 4 |
"errors" |
| 6 | 5 |
"fmt" |
| 7 | 6 |
"io" |
| ... | ... |
@@ -10,7 +9,6 @@ import ( |
| 10 | 10 |
"strings" |
| 11 | 11 |
"testing" |
| 12 | 12 |
|
| 13 |
- "github.com/moby/moby/api/types/swarm" |
|
| 14 | 13 |
"gotest.tools/v3/assert" |
| 15 | 14 |
is "gotest.tools/v3/assert/cmp" |
| 16 | 15 |
) |
| ... | ... |
@@ -34,19 +32,19 @@ func TestPingFail(t *testing.T) {
|
| 34 | 34 |
})) |
| 35 | 35 |
assert.NilError(t, err) |
| 36 | 36 |
|
| 37 |
- ping, err := client.Ping(context.Background()) |
|
| 37 |
+ ping, err := client.Ping(t.Context(), PingOptions{})
|
|
| 38 | 38 |
assert.Check(t, is.ErrorContains(err, "some error with the server")) |
| 39 | 39 |
assert.Check(t, is.Equal(false, ping.Experimental)) |
| 40 | 40 |
assert.Check(t, is.Equal("", ping.APIVersion))
|
| 41 |
- var si *swarm.Status |
|
| 41 |
+ var si *SwarmStatus |
|
| 42 | 42 |
assert.Check(t, is.Equal(si, ping.SwarmStatus)) |
| 43 | 43 |
|
| 44 | 44 |
withHeader = true |
| 45 |
- ping2, err := client.Ping(context.Background()) |
|
| 45 |
+ ping2, err := client.Ping(t.Context(), PingOptions{})
|
|
| 46 | 46 |
assert.Check(t, is.ErrorContains(err, "some error with the server")) |
| 47 | 47 |
assert.Check(t, is.Equal(true, ping2.Experimental)) |
| 48 | 48 |
assert.Check(t, is.Equal("awesome", ping2.APIVersion))
|
| 49 |
- assert.Check(t, is.Equal(swarm.Status{NodeState: "inactive"}, *ping2.SwarmStatus))
|
|
| 49 |
+ assert.Check(t, is.Equal(SwarmStatus{NodeState: "inactive"}, *ping2.SwarmStatus))
|
|
| 50 | 50 |
} |
| 51 | 51 |
|
| 52 | 52 |
// TestPingWithError tests the case where there is a protocol error in the ping. |
| ... | ... |
@@ -57,11 +55,11 @@ func TestPingWithError(t *testing.T) {
|
| 57 | 57 |
})) |
| 58 | 58 |
assert.NilError(t, err) |
| 59 | 59 |
|
| 60 |
- ping, err := client.Ping(context.Background()) |
|
| 60 |
+ ping, err := client.Ping(t.Context(), PingOptions{})
|
|
| 61 | 61 |
assert.Check(t, is.ErrorContains(err, "some connection error")) |
| 62 | 62 |
assert.Check(t, is.Equal(false, ping.Experimental)) |
| 63 | 63 |
assert.Check(t, is.Equal("", ping.APIVersion))
|
| 64 |
- var si *swarm.Status |
|
| 64 |
+ var si *SwarmStatus |
|
| 65 | 65 |
assert.Check(t, is.Equal(si, ping.SwarmStatus)) |
| 66 | 66 |
} |
| 67 | 67 |
|
| ... | ... |
@@ -78,11 +76,11 @@ func TestPingSuccess(t *testing.T) {
|
| 78 | 78 |
return resp, nil |
| 79 | 79 |
})) |
| 80 | 80 |
assert.NilError(t, err) |
| 81 |
- ping, err := client.Ping(context.Background()) |
|
| 81 |
+ ping, err := client.Ping(t.Context(), PingOptions{})
|
|
| 82 | 82 |
assert.NilError(t, err) |
| 83 | 83 |
assert.Check(t, is.Equal(true, ping.Experimental)) |
| 84 | 84 |
assert.Check(t, is.Equal("awesome", ping.APIVersion))
|
| 85 |
- assert.Check(t, is.Equal(swarm.Status{NodeState: "active", ControlAvailable: true}, *ping.SwarmStatus))
|
|
| 85 |
+ assert.Check(t, is.Equal(SwarmStatus{NodeState: "active", ControlAvailable: true}, *ping.SwarmStatus))
|
|
| 86 | 86 |
} |
| 87 | 87 |
|
| 88 | 88 |
// TestPingHeadFallback tests that the client falls back to GET if HEAD fails. |
| ... | ... |
@@ -131,7 +129,7 @@ func TestPingHeadFallback(t *testing.T) {
|
| 131 | 131 |
return resp, nil |
| 132 | 132 |
})) |
| 133 | 133 |
assert.NilError(t, err) |
| 134 |
- ping, _ := client.Ping(context.Background()) |
|
| 134 |
+ ping, _ := client.Ping(t.Context(), PingOptions{})
|
|
| 135 | 135 |
assert.Check(t, is.Equal(ping.APIVersion, "1.2.3")) |
| 136 | 136 |
assert.Check(t, is.DeepEqual(reqs, tc.expected)) |
| 137 | 137 |
}) |
| ... | ... |
@@ -210,7 +210,7 @@ func TestResponseErrors(t *testing.T) {
|
| 210 | 210 |
client, err = NewClientWithOpts(WithHTTPClient(client.client), WithVersion(tc.apiVersion)) |
| 211 | 211 |
} |
| 212 | 212 |
assert.NilError(t, err) |
| 213 |
- _, err = client.Ping(context.Background()) |
|
| 213 |
+ _, err = client.Ping(t.Context(), PingOptions{})
|
|
| 214 | 214 |
assert.Check(t, is.Error(err, tc.expected)) |
| 215 | 215 |
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) |
| 216 | 216 |
}) |
| ... | ... |
@@ -229,7 +229,7 @@ func TestInfiniteError(t *testing.T) {
|
| 229 | 229 |
})) |
| 230 | 230 |
assert.NilError(t, err) |
| 231 | 231 |
|
| 232 |
- _, err = client.Ping(context.Background()) |
|
| 232 |
+ _, err = client.Ping(t.Context(), PingOptions{})
|
|
| 233 | 233 |
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) |
| 234 | 234 |
assert.Check(t, is.ErrorContains(err, "request returned Internal Server Error")) |
| 235 | 235 |
} |
| ... | ... |
@@ -2235,7 +2235,7 @@ func (s *DockerDaemonSuite) TestFailedPluginRemove(c *testing.T) {
|
| 2235 | 2235 |
d.Restart(c) |
| 2236 | 2236 |
ctx, cancel = context.WithTimeout(testutil.GetContext(c), 30*time.Second) |
| 2237 | 2237 |
defer cancel() |
| 2238 |
- _, err = apiClient.Ping(ctx) |
|
| 2238 |
+ _, err = apiClient.Ping(ctx, client.PingOptions{})
|
|
| 2239 | 2239 |
assert.NilError(c, err) |
| 2240 | 2240 |
|
| 2241 | 2241 |
_, err = apiClient.PluginInspect(ctx, name, client.PluginInspectOptions{})
|
| ... | ... |
@@ -68,7 +68,7 @@ func TestPingSwarmHeader(t *testing.T) {
|
| 68 | 68 |
|
| 69 | 69 |
t.Run("before swarm init", func(t *testing.T) {
|
| 70 | 70 |
ctx := testutil.StartSpan(ctx, t) |
| 71 |
- p, err := apiClient.Ping(ctx) |
|
| 71 |
+ p, err := apiClient.Ping(ctx, client.PingOptions{})
|
|
| 72 | 72 |
assert.NilError(t, err) |
| 73 | 73 |
assert.Equal(t, p.SwarmStatus.NodeState, swarm.LocalNodeStateInactive) |
| 74 | 74 |
assert.Equal(t, p.SwarmStatus.ControlAvailable, false) |
| ... | ... |
@@ -79,7 +79,7 @@ func TestPingSwarmHeader(t *testing.T) {
|
| 79 | 79 |
|
| 80 | 80 |
t.Run("after swarm init", func(t *testing.T) {
|
| 81 | 81 |
ctx := testutil.StartSpan(ctx, t) |
| 82 |
- p, err := apiClient.Ping(ctx) |
|
| 82 |
+ p, err := apiClient.Ping(ctx, client.PingOptions{})
|
|
| 83 | 83 |
assert.NilError(t, err) |
| 84 | 84 |
assert.Equal(t, p.SwarmStatus.NodeState, swarm.LocalNodeStateActive) |
| 85 | 85 |
assert.Equal(t, p.SwarmStatus.ControlAvailable, true) |
| ... | ... |
@@ -90,7 +90,7 @@ func TestPingSwarmHeader(t *testing.T) {
|
| 90 | 90 |
|
| 91 | 91 |
t.Run("after swarm leave", func(t *testing.T) {
|
| 92 | 92 |
ctx := testutil.StartSpan(ctx, t) |
| 93 |
- p, err := apiClient.Ping(ctx) |
|
| 93 |
+ p, err := apiClient.Ping(ctx, client.PingOptions{})
|
|
| 94 | 94 |
assert.NilError(t, err) |
| 95 | 95 |
assert.Equal(t, p.SwarmStatus.NodeState, swarm.LocalNodeStateInactive) |
| 96 | 96 |
assert.Equal(t, p.SwarmStatus.ControlAvailable, false) |
| ... | ... |
@@ -116,7 +116,7 @@ func TestPingBuilderHeader(t *testing.T) {
|
| 116 | 116 |
expected = build.BuilderV1 |
| 117 | 117 |
} |
| 118 | 118 |
|
| 119 |
- p, err := apiClient.Ping(ctx) |
|
| 119 |
+ p, err := apiClient.Ping(ctx, client.PingOptions{})
|
|
| 120 | 120 |
assert.NilError(t, err) |
| 121 | 121 |
assert.Equal(t, p.BuilderVersion, expected) |
| 122 | 122 |
}) |
| ... | ... |
@@ -130,7 +130,7 @@ func TestPingBuilderHeader(t *testing.T) {
|
| 130 | 130 |
defer d.Stop(t) |
| 131 | 131 |
|
| 132 | 132 |
expected := build.BuilderV1 |
| 133 |
- p, err := apiClient.Ping(ctx) |
|
| 133 |
+ p, err := apiClient.Ping(ctx, client.PingOptions{})
|
|
| 134 | 134 |
assert.NilError(t, err) |
| 135 | 135 |
assert.Equal(t, p.BuilderVersion, expected) |
| 136 | 136 |
}) |
| ... | ... |
@@ -214,16 +214,6 @@ type Info struct {
|
| 214 | 214 |
Warnings []string `json:",omitempty"` |
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 |
-// Status provides information about the current swarm status and role, |
|
| 218 |
-// obtained from the "Swarm" header in the API response. |
|
| 219 |
-type Status struct {
|
|
| 220 |
- // NodeState represents the state of the node. |
|
| 221 |
- NodeState LocalNodeState |
|
| 222 |
- |
|
| 223 |
- // ControlAvailable indicates if the node is a swarm manager. |
|
| 224 |
- ControlAvailable bool |
|
| 225 |
-} |
|
| 226 |
- |
|
| 227 | 217 |
// Peer represents a peer. |
| 228 | 218 |
type Peer struct {
|
| 229 | 219 |
NodeID string |
| ... | ... |
@@ -1,10 +1,5 @@ |
| 1 | 1 |
package types |
| 2 | 2 |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/moby/moby/api/types/build" |
|
| 5 |
- "github.com/moby/moby/api/types/swarm" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 | 3 |
const ( |
| 9 | 4 |
// MediaTypeRawStream is vendor specific MIME-Type set for raw TTY streams |
| 10 | 5 |
MediaTypeRawStream = "application/vnd.docker.raw-stream" |
| ... | ... |
@@ -22,24 +17,6 @@ const ( |
| 22 | 22 |
MediaTypeJSONSequence = "application/json-seq" |
| 23 | 23 |
) |
| 24 | 24 |
|
| 25 |
-// Ping contains response of Engine API: |
|
| 26 |
-// GET "/_ping" |
|
| 27 |
-type Ping struct {
|
|
| 28 |
- APIVersion string |
|
| 29 |
- OSType string |
|
| 30 |
- Experimental bool |
|
| 31 |
- BuilderVersion build.BuilderVersion |
|
| 32 |
- |
|
| 33 |
- // SwarmStatus provides information about the current swarm status of the |
|
| 34 |
- // engine, obtained from the "Swarm" header in the API response. |
|
| 35 |
- // |
|
| 36 |
- // It can be a nil struct if the API version does not provide this header |
|
| 37 |
- // in the ping response, or if an error occurred, in which case the client |
|
| 38 |
- // should use other ways to get the current swarm status, such as the /swarm |
|
| 39 |
- // endpoint. |
|
| 40 |
- SwarmStatus *swarm.Status |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 | 25 |
// ComponentVersion describes the version information for a specific component. |
| 44 | 26 |
type ComponentVersion struct {
|
| 45 | 27 |
Name string |
| ... | ... |
@@ -56,7 +56,6 @@ import ( |
| 56 | 56 |
|
| 57 | 57 |
cerrdefs "github.com/containerd/errdefs" |
| 58 | 58 |
"github.com/docker/go-connections/sockets" |
| 59 |
- "github.com/moby/moby/api/types" |
|
| 60 | 59 |
"github.com/moby/moby/api/types/versions" |
| 61 | 60 |
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" |
| 62 | 61 |
) |
| ... | ... |
@@ -270,7 +269,7 @@ func (cli *Client) checkVersion(ctx context.Context) error {
|
| 270 | 270 |
return nil |
| 271 | 271 |
} |
| 272 | 272 |
|
| 273 |
- ping, err := cli.Ping(ctx) |
|
| 273 |
+ ping, err := cli.Ping(ctx, PingOptions{})
|
|
| 274 | 274 |
if err != nil {
|
| 275 | 275 |
return err |
| 276 | 276 |
} |
| ... | ... |
@@ -317,7 +316,7 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
|
| 317 | 317 |
cli.negotiateLock.Lock() |
| 318 | 318 |
defer cli.negotiateLock.Unlock() |
| 319 | 319 |
|
| 320 |
- ping, err := cli.Ping(ctx) |
|
| 320 |
+ ping, err := cli.Ping(ctx, PingOptions{})
|
|
| 321 | 321 |
if err != nil {
|
| 322 | 322 |
// FIXME(thaJeztah): Ping returns an error when failing to connect to the API; we should not swallow the error here, and instead returning it. |
| 323 | 323 |
return |
| ... | ... |
@@ -338,7 +337,8 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
|
| 338 | 338 |
// |
| 339 | 339 |
// If the API server's ping response does not contain an API version, it falls |
| 340 | 340 |
// back to the oldest API version supported. |
| 341 |
-func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) {
|
|
| 341 |
+func (cli *Client) NegotiateAPIVersionPing(pingResponse PingResult) {
|
|
| 342 |
+ // TODO(thaJeztah): should this take a "Ping" option? It only consumes the version. This method should be removed overall and not be exported. |
|
| 342 | 343 |
if !cli.manualOverride {
|
| 343 | 344 |
// Avoid concurrent modification of version-related fields |
| 344 | 345 |
cli.negotiateLock.Lock() |
| ... | ... |
@@ -35,7 +35,7 @@ type stableAPIClient interface {
|
| 35 | 35 |
DaemonHost() string |
| 36 | 36 |
ServerVersion(ctx context.Context) (types.Version, error) |
| 37 | 37 |
NegotiateAPIVersion(ctx context.Context) |
| 38 |
- NegotiateAPIVersionPing(types.Ping) |
|
| 38 |
+ NegotiateAPIVersionPing(PingResult) |
|
| 39 | 39 |
HijackDialer |
| 40 | 40 |
Dialer() func(context.Context) (net.Conn, error) |
| 41 | 41 |
Close() error |
| ... | ... |
@@ -187,7 +187,7 @@ type SystemAPIClient interface {
|
| 187 | 187 |
Info(ctx context.Context) (system.Info, error) |
| 188 | 188 |
RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) |
| 189 | 189 |
DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) |
| 190 |
- Ping(ctx context.Context) (types.Ping, error) |
|
| 190 |
+ Ping(ctx context.Context, options PingOptions) (PingResult, error) |
|
| 191 | 191 |
} |
| 192 | 192 |
|
| 193 | 193 |
// VolumeAPIClient defines API client methods for the volumes |
| ... | ... |
@@ -6,11 +6,42 @@ import ( |
| 6 | 6 |
"path" |
| 7 | 7 |
"strings" |
| 8 | 8 |
|
| 9 |
- "github.com/moby/moby/api/types" |
|
| 10 | 9 |
"github.com/moby/moby/api/types/build" |
| 11 | 10 |
"github.com/moby/moby/api/types/swarm" |
| 12 | 11 |
) |
| 13 | 12 |
|
| 13 |
+// PingOptions holds options for [client.Ping]. |
|
| 14 |
+type PingOptions struct {
|
|
| 15 |
+ // Add future optional parameters here |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+// PingResult holds the result of a [Client.Ping] API call. |
|
| 19 |
+type PingResult struct {
|
|
| 20 |
+ APIVersion string |
|
| 21 |
+ OSType string |
|
| 22 |
+ Experimental bool |
|
| 23 |
+ BuilderVersion build.BuilderVersion |
|
| 24 |
+ |
|
| 25 |
+ // SwarmStatus provides information about the current swarm status of the |
|
| 26 |
+ // engine, obtained from the "Swarm" header in the API response. |
|
| 27 |
+ // |
|
| 28 |
+ // It can be a nil struct if the API version does not provide this header |
|
| 29 |
+ // in the ping response, or if an error occurred, in which case the client |
|
| 30 |
+ // should use other ways to get the current swarm status, such as the /swarm |
|
| 31 |
+ // endpoint. |
|
| 32 |
+ SwarmStatus *SwarmStatus |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// SwarmStatus provides information about the current swarm status and role, |
|
| 36 |
+// obtained from the "Swarm" header in the API response. |
|
| 37 |
+type SwarmStatus struct {
|
|
| 38 |
+ // NodeState represents the state of the node. |
|
| 39 |
+ NodeState swarm.LocalNodeState |
|
| 40 |
+ |
|
| 41 |
+ // ControlAvailable indicates if the node is a swarm manager. |
|
| 42 |
+ ControlAvailable bool |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 14 | 45 |
// Ping pings the server and returns the value of the "Docker-Experimental", |
| 15 | 46 |
// "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use |
| 16 | 47 |
// a HEAD request on the endpoint, but falls back to GET if HEAD is not supported |
| ... | ... |
@@ -18,13 +49,13 @@ import ( |
| 18 | 18 |
// may be returned if the daemon is in an unhealthy state, but returns errors |
| 19 | 19 |
// for other non-success status codes, failing to connect to the API, or failing |
| 20 | 20 |
// to parse the API response. |
| 21 |
-func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
|
| 21 |
+func (cli *Client) Ping(ctx context.Context, options PingOptions) (PingResult, error) {
|
|
| 22 | 22 |
// Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest() |
| 23 | 23 |
// because ping requests are used during API version negotiation, so we want |
| 24 | 24 |
// to hit the non-versioned /_ping endpoint, not /v1.xx/_ping |
| 25 | 25 |
req, err := cli.buildRequest(ctx, http.MethodHead, path.Join(cli.basePath, "/_ping"), nil, nil) |
| 26 | 26 |
if err != nil {
|
| 27 |
- return types.Ping{}, err
|
|
| 27 |
+ return PingResult{}, err
|
|
| 28 | 28 |
} |
| 29 | 29 |
resp, err := cli.doRequest(req) |
| 30 | 30 |
defer ensureReaderClosed(resp) |
| ... | ... |
@@ -33,7 +64,7 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
| 33 | 33 |
// we got a "OK" (200) status. For non-200 status-codes, we fall |
| 34 | 34 |
// back to doing a GET request, as a HEAD request won't have a |
| 35 | 35 |
// response-body to get error details from. |
| 36 |
- return newPingResponse(resp), nil |
|
| 36 |
+ return newPingResult(resp), nil |
|
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 | 39 |
// HEAD failed or returned a non-OK status; fallback to GET. |
| ... | ... |
@@ -42,29 +73,29 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
| 42 | 42 |
defer ensureReaderClosed(resp) |
| 43 | 43 |
if err != nil {
|
| 44 | 44 |
// Failed to connect. |
| 45 |
- return types.Ping{}, err
|
|
| 45 |
+ return PingResult{}, err
|
|
| 46 | 46 |
} |
| 47 | 47 |
|
| 48 | 48 |
// GET request succeeded but may have returned a non-200 status. |
| 49 | 49 |
// Return a Ping response, together with any error returned by |
| 50 | 50 |
// the API server. |
| 51 |
- return newPingResponse(resp), checkResponseErr(resp) |
|
| 51 |
+ return newPingResult(resp), checkResponseErr(resp) |
|
| 52 | 52 |
} |
| 53 | 53 |
|
| 54 |
-func newPingResponse(resp *http.Response) types.Ping {
|
|
| 54 |
+func newPingResult(resp *http.Response) PingResult {
|
|
| 55 | 55 |
if resp == nil {
|
| 56 |
- return types.Ping{}
|
|
| 56 |
+ return PingResult{}
|
|
| 57 | 57 |
} |
| 58 |
- var swarmStatus *swarm.Status |
|
| 58 |
+ var swarmStatus *SwarmStatus |
|
| 59 | 59 |
if si := resp.Header.Get("Swarm"); si != "" {
|
| 60 | 60 |
state, role, _ := strings.Cut(si, "/") |
| 61 |
- swarmStatus = &swarm.Status{
|
|
| 61 |
+ swarmStatus = &SwarmStatus{
|
|
| 62 | 62 |
NodeState: swarm.LocalNodeState(state), |
| 63 | 63 |
ControlAvailable: role == "manager", |
| 64 | 64 |
} |
| 65 | 65 |
} |
| 66 | 66 |
|
| 67 |
- return types.Ping{
|
|
| 67 |
+ return PingResult{
|
|
| 68 | 68 |
APIVersion: resp.Header.Get("Api-Version"),
|
| 69 | 69 |
OSType: resp.Header.Get("Ostype"),
|
| 70 | 70 |
Experimental: resp.Header.Get("Docker-Experimental") == "true",
|