diff --git a/api/types/swarm/swarm.go b/api/types/swarm/swarm.go index 7d683b30ae..8421850310 100644 --- a/api/types/swarm/swarm.go +++ b/api/types/swarm/swarm.go @@ -214,16 +214,6 @@ type Info struct { Warnings []string `json:",omitempty"` } -// Status provides information about the current swarm status and role, -// obtained from the "Swarm" header in the API response. -type Status struct { - // NodeState represents the state of the node. - NodeState LocalNodeState - - // ControlAvailable indicates if the node is a swarm manager. - ControlAvailable bool -} - // Peer represents a peer. type Peer struct { NodeID string diff --git a/api/types/types.go b/api/types/types.go index 32fbcc639f..2844306c72 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -1,10 +1,5 @@ package types -import ( - "github.com/moby/moby/api/types/build" - "github.com/moby/moby/api/types/swarm" -) - const ( // MediaTypeRawStream is vendor specific MIME-Type set for raw TTY streams MediaTypeRawStream = "application/vnd.docker.raw-stream" @@ -22,24 +17,6 @@ const ( MediaTypeJSONSequence = "application/json-seq" ) -// Ping contains response of Engine API: -// GET "/_ping" -type Ping struct { - APIVersion string - OSType string - Experimental bool - BuilderVersion build.BuilderVersion - - // SwarmStatus provides information about the current swarm status of the - // engine, obtained from the "Swarm" header in the API response. - // - // It can be a nil struct if the API version does not provide this header - // in the ping response, or if an error occurred, in which case the client - // should use other ways to get the current swarm status, such as the /swarm - // endpoint. - SwarmStatus *swarm.Status -} - // ComponentVersion describes the version information for a specific component. type ComponentVersion struct { Name string diff --git a/client/client.go b/client/client.go index 3d004c30f5..8af2e0d242 100644 --- a/client/client.go +++ b/client/client.go @@ -56,7 +56,6 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/docker/go-connections/sockets" - "github.com/moby/moby/api/types" "github.com/moby/moby/api/types/versions" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) @@ -270,7 +269,7 @@ func (cli *Client) checkVersion(ctx context.Context) error { return nil } - ping, err := cli.Ping(ctx) + ping, err := cli.Ping(ctx, PingOptions{}) if err != nil { return err } @@ -317,7 +316,7 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) { cli.negotiateLock.Lock() defer cli.negotiateLock.Unlock() - ping, err := cli.Ping(ctx) + ping, err := cli.Ping(ctx, PingOptions{}) if err != nil { // FIXME(thaJeztah): Ping returns an error when failing to connect to the API; we should not swallow the error here, and instead returning it. return @@ -338,7 +337,8 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) { // // If the API server's ping response does not contain an API version, it falls // back to the oldest API version supported. -func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) { +func (cli *Client) NegotiateAPIVersionPing(pingResponse PingResult) { + // TODO(thaJeztah): should this take a "Ping" option? It only consumes the version. This method should be removed overall and not be exported. if !cli.manualOverride { // Avoid concurrent modification of version-related fields cli.negotiateLock.Lock() diff --git a/client/client_interfaces.go b/client/client_interfaces.go index f7bd8b62ce..62f8720d99 100644 --- a/client/client_interfaces.go +++ b/client/client_interfaces.go @@ -35,7 +35,7 @@ type stableAPIClient interface { DaemonHost() string ServerVersion(ctx context.Context) (types.Version, error) NegotiateAPIVersion(ctx context.Context) - NegotiateAPIVersionPing(types.Ping) + NegotiateAPIVersionPing(PingResult) HijackDialer Dialer() func(context.Context) (net.Conn, error) Close() error @@ -187,7 +187,7 @@ type SystemAPIClient interface { Info(ctx context.Context) (system.Info, error) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) - Ping(ctx context.Context) (types.Ping, error) + Ping(ctx context.Context, options PingOptions) (PingResult, error) } // VolumeAPIClient defines API client methods for the volumes diff --git a/client/client_test.go b/client/client_test.go index 4d4d979386..20a8ba7404 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -11,7 +11,6 @@ import ( "strings" "testing" - "github.com/moby/moby/api/types" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/skip" @@ -267,7 +266,7 @@ func TestNegotiateAPIVersionEmpty(t *testing.T) { const expected = fallbackAPIVersion // test downgrade - client.NegotiateAPIVersionPing(types.Ping{}) + client.NegotiateAPIVersionPing(PingResult{}) assert.Check(t, is.Equal(client.ClientVersion(), expected)) } @@ -330,7 +329,7 @@ func TestNegotiateAPIVersion(t *testing.T) { } client, err := NewClientWithOpts(opts...) assert.NilError(t, err) - client.NegotiateAPIVersionPing(types.Ping{APIVersion: tc.pingVersion}) + client.NegotiateAPIVersionPing(PingResult{APIVersion: tc.pingVersion}) assert.Check(t, is.Equal(tc.expectedVersion, client.ClientVersion())) }) } @@ -346,7 +345,7 @@ func TestNegotiateAPIVersionOverride(t *testing.T) { assert.NilError(t, err) // test that we honored the env var - client.NegotiateAPIVersionPing(types.Ping{APIVersion: "1.24"}) + client.NegotiateAPIVersionPing(PingResult{APIVersion: "1.24"}) assert.Check(t, is.Equal(client.ClientVersion(), expected)) } @@ -404,7 +403,7 @@ func TestNegotiateAPIVersionWithEmptyVersion(t *testing.T) { assert.NilError(t, err) const expected = "1.50" - client.NegotiateAPIVersionPing(types.Ping{APIVersion: expected}) + client.NegotiateAPIVersionPing(PingResult{APIVersion: expected}) assert.Check(t, is.Equal(client.ClientVersion(), expected)) } @@ -415,7 +414,7 @@ func TestNegotiateAPIVersionWithFixedVersion(t *testing.T) { client, err := NewClientWithOpts(WithVersion(customVersion)) assert.NilError(t, err) - client.NegotiateAPIVersionPing(types.Ping{APIVersion: "1.49"}) + client.NegotiateAPIVersionPing(PingResult{APIVersion: "1.49"}) assert.Check(t, is.Equal(client.ClientVersion(), customVersion)) } diff --git a/client/options_test.go b/client/options_test.go index 5866a92291..4de6523182 100644 --- a/client/options_test.go +++ b/client/options_test.go @@ -1,7 +1,6 @@ package client import ( - "context" "net/http" "runtime" "testing" @@ -72,7 +71,7 @@ func TestWithUserAgent(t *testing.T) { }), ) assert.NilError(t, err) - _, err = c.Ping(context.Background()) + _, err = c.Ping(t.Context(), PingOptions{}) assert.NilError(t, err) assert.NilError(t, c.Close()) }) @@ -87,7 +86,7 @@ func TestWithUserAgent(t *testing.T) { }), ) assert.NilError(t, err) - _, err = c.Ping(context.Background()) + _, err = c.Ping(t.Context(), PingOptions{}) assert.NilError(t, err) assert.NilError(t, c.Close()) }) @@ -101,7 +100,7 @@ func TestWithUserAgent(t *testing.T) { }), ) assert.NilError(t, err) - _, err = c.Ping(context.Background()) + _, err = c.Ping(t.Context(), PingOptions{}) assert.NilError(t, err) assert.NilError(t, c.Close()) }) @@ -115,7 +114,7 @@ func TestWithUserAgent(t *testing.T) { }), ) assert.NilError(t, err) - _, err = c.Ping(context.Background()) + _, err = c.Ping(t.Context(), PingOptions{}) assert.NilError(t, err) assert.NilError(t, c.Close()) }) @@ -130,7 +129,7 @@ func TestWithUserAgent(t *testing.T) { }), ) assert.NilError(t, err) - _, err = c.Ping(context.Background()) + _, err = c.Ping(t.Context(), PingOptions{}) assert.NilError(t, err) assert.NilError(t, c.Close()) }) diff --git a/client/ping.go b/client/ping.go index 96ee7bef66..80359fb457 100644 --- a/client/ping.go +++ b/client/ping.go @@ -6,11 +6,42 @@ import ( "path" "strings" - "github.com/moby/moby/api/types" "github.com/moby/moby/api/types/build" "github.com/moby/moby/api/types/swarm" ) +// PingOptions holds options for [client.Ping]. +type PingOptions struct { + // Add future optional parameters here +} + +// PingResult holds the result of a [Client.Ping] API call. +type PingResult struct { + APIVersion string + OSType string + Experimental bool + BuilderVersion build.BuilderVersion + + // SwarmStatus provides information about the current swarm status of the + // engine, obtained from the "Swarm" header in the API response. + // + // It can be a nil struct if the API version does not provide this header + // in the ping response, or if an error occurred, in which case the client + // should use other ways to get the current swarm status, such as the /swarm + // endpoint. + SwarmStatus *SwarmStatus +} + +// SwarmStatus provides information about the current swarm status and role, +// obtained from the "Swarm" header in the API response. +type SwarmStatus struct { + // NodeState represents the state of the node. + NodeState swarm.LocalNodeState + + // ControlAvailable indicates if the node is a swarm manager. + ControlAvailable bool +} + // Ping pings the server and returns the value of the "Docker-Experimental", // "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use // a HEAD request on the endpoint, but falls back to GET if HEAD is not supported @@ -18,13 +49,13 @@ import ( // may be returned if the daemon is in an unhealthy state, but returns errors // for other non-success status codes, failing to connect to the API, or failing // to parse the API response. -func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { +func (cli *Client) Ping(ctx context.Context, options PingOptions) (PingResult, error) { // Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest() // because ping requests are used during API version negotiation, so we want // to hit the non-versioned /_ping endpoint, not /v1.xx/_ping req, err := cli.buildRequest(ctx, http.MethodHead, path.Join(cli.basePath, "/_ping"), nil, nil) if err != nil { - return types.Ping{}, err + return PingResult{}, err } resp, err := cli.doRequest(req) defer ensureReaderClosed(resp) @@ -33,7 +64,7 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { // we got a "OK" (200) status. For non-200 status-codes, we fall // back to doing a GET request, as a HEAD request won't have a // response-body to get error details from. - return newPingResponse(resp), nil + return newPingResult(resp), nil } // 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) { defer ensureReaderClosed(resp) if err != nil { // Failed to connect. - return types.Ping{}, err + return PingResult{}, err } // GET request succeeded but may have returned a non-200 status. // Return a Ping response, together with any error returned by // the API server. - return newPingResponse(resp), checkResponseErr(resp) + return newPingResult(resp), checkResponseErr(resp) } -func newPingResponse(resp *http.Response) types.Ping { +func newPingResult(resp *http.Response) PingResult { if resp == nil { - return types.Ping{} + return PingResult{} } - var swarmStatus *swarm.Status + var swarmStatus *SwarmStatus if si := resp.Header.Get("Swarm"); si != "" { state, role, _ := strings.Cut(si, "/") - swarmStatus = &swarm.Status{ + swarmStatus = &SwarmStatus{ NodeState: swarm.LocalNodeState(state), ControlAvailable: role == "manager", } } - return types.Ping{ + return PingResult{ APIVersion: resp.Header.Get("Api-Version"), OSType: resp.Header.Get("Ostype"), Experimental: resp.Header.Get("Docker-Experimental") == "true", diff --git a/client/ping_test.go b/client/ping_test.go index bb72ff6ba2..d1f3cb1a05 100644 --- a/client/ping_test.go +++ b/client/ping_test.go @@ -1,7 +1,6 @@ package client import ( - "context" "errors" "fmt" "io" @@ -10,7 +9,6 @@ import ( "strings" "testing" - "github.com/moby/moby/api/types/swarm" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) @@ -34,19 +32,19 @@ func TestPingFail(t *testing.T) { })) assert.NilError(t, err) - ping, err := client.Ping(context.Background()) + ping, err := client.Ping(t.Context(), PingOptions{}) assert.Check(t, is.ErrorContains(err, "some error with the server")) assert.Check(t, is.Equal(false, ping.Experimental)) assert.Check(t, is.Equal("", ping.APIVersion)) - var si *swarm.Status + var si *SwarmStatus assert.Check(t, is.Equal(si, ping.SwarmStatus)) withHeader = true - ping2, err := client.Ping(context.Background()) + ping2, err := client.Ping(t.Context(), PingOptions{}) assert.Check(t, is.ErrorContains(err, "some error with the server")) assert.Check(t, is.Equal(true, ping2.Experimental)) assert.Check(t, is.Equal("awesome", ping2.APIVersion)) - assert.Check(t, is.Equal(swarm.Status{NodeState: "inactive"}, *ping2.SwarmStatus)) + assert.Check(t, is.Equal(SwarmStatus{NodeState: "inactive"}, *ping2.SwarmStatus)) } // TestPingWithError tests the case where there is a protocol error in the ping. @@ -57,11 +55,11 @@ func TestPingWithError(t *testing.T) { })) assert.NilError(t, err) - ping, err := client.Ping(context.Background()) + ping, err := client.Ping(t.Context(), PingOptions{}) assert.Check(t, is.ErrorContains(err, "some connection error")) assert.Check(t, is.Equal(false, ping.Experimental)) assert.Check(t, is.Equal("", ping.APIVersion)) - var si *swarm.Status + var si *SwarmStatus assert.Check(t, is.Equal(si, ping.SwarmStatus)) } @@ -78,11 +76,11 @@ func TestPingSuccess(t *testing.T) { return resp, nil })) assert.NilError(t, err) - ping, err := client.Ping(context.Background()) + ping, err := client.Ping(t.Context(), PingOptions{}) assert.NilError(t, err) assert.Check(t, is.Equal(true, ping.Experimental)) assert.Check(t, is.Equal("awesome", ping.APIVersion)) - assert.Check(t, is.Equal(swarm.Status{NodeState: "active", ControlAvailable: true}, *ping.SwarmStatus)) + assert.Check(t, is.Equal(SwarmStatus{NodeState: "active", ControlAvailable: true}, *ping.SwarmStatus)) } // TestPingHeadFallback tests that the client falls back to GET if HEAD fails. @@ -131,7 +129,7 @@ func TestPingHeadFallback(t *testing.T) { return resp, nil })) assert.NilError(t, err) - ping, _ := client.Ping(context.Background()) + ping, _ := client.Ping(t.Context(), PingOptions{}) assert.Check(t, is.Equal(ping.APIVersion, "1.2.3")) assert.Check(t, is.DeepEqual(reqs, tc.expected)) }) diff --git a/client/request_test.go b/client/request_test.go index 08e816dd1c..9b3b30e56b 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -210,7 +210,7 @@ func TestResponseErrors(t *testing.T) { client, err = NewClientWithOpts(WithHTTPClient(client.client), WithVersion(tc.apiVersion)) } assert.NilError(t, err) - _, err = client.Ping(context.Background()) + _, err = client.Ping(t.Context(), PingOptions{}) assert.Check(t, is.Error(err, tc.expected)) assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) }) @@ -229,7 +229,7 @@ func TestInfiniteError(t *testing.T) { })) assert.NilError(t, err) - _, err = client.Ping(context.Background()) + _, err = client.Ping(t.Context(), PingOptions{}) assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) assert.Check(t, is.ErrorContains(err, "request returned Internal Server Error")) } diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index f8f8d155b8..a847b00b5e 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -2235,7 +2235,7 @@ func (s *DockerDaemonSuite) TestFailedPluginRemove(c *testing.T) { d.Restart(c) ctx, cancel = context.WithTimeout(testutil.GetContext(c), 30*time.Second) defer cancel() - _, err = apiClient.Ping(ctx) + _, err = apiClient.Ping(ctx, client.PingOptions{}) assert.NilError(c, err) _, err = apiClient.PluginInspect(ctx, name, client.PluginInspectOptions{}) diff --git a/integration/system/ping_test.go b/integration/system/ping_test.go index 5e47761012..0a1c7d2e4c 100644 --- a/integration/system/ping_test.go +++ b/integration/system/ping_test.go @@ -68,7 +68,7 @@ func TestPingSwarmHeader(t *testing.T) { t.Run("before swarm init", func(t *testing.T) { ctx := testutil.StartSpan(ctx, t) - p, err := apiClient.Ping(ctx) + p, err := apiClient.Ping(ctx, client.PingOptions{}) assert.NilError(t, err) assert.Equal(t, p.SwarmStatus.NodeState, swarm.LocalNodeStateInactive) assert.Equal(t, p.SwarmStatus.ControlAvailable, false) @@ -79,7 +79,7 @@ func TestPingSwarmHeader(t *testing.T) { t.Run("after swarm init", func(t *testing.T) { ctx := testutil.StartSpan(ctx, t) - p, err := apiClient.Ping(ctx) + p, err := apiClient.Ping(ctx, client.PingOptions{}) assert.NilError(t, err) assert.Equal(t, p.SwarmStatus.NodeState, swarm.LocalNodeStateActive) assert.Equal(t, p.SwarmStatus.ControlAvailable, true) @@ -90,7 +90,7 @@ func TestPingSwarmHeader(t *testing.T) { t.Run("after swarm leave", func(t *testing.T) { ctx := testutil.StartSpan(ctx, t) - p, err := apiClient.Ping(ctx) + p, err := apiClient.Ping(ctx, client.PingOptions{}) assert.NilError(t, err) assert.Equal(t, p.SwarmStatus.NodeState, swarm.LocalNodeStateInactive) assert.Equal(t, p.SwarmStatus.ControlAvailable, false) @@ -116,7 +116,7 @@ func TestPingBuilderHeader(t *testing.T) { expected = build.BuilderV1 } - p, err := apiClient.Ping(ctx) + p, err := apiClient.Ping(ctx, client.PingOptions{}) assert.NilError(t, err) assert.Equal(t, p.BuilderVersion, expected) }) @@ -130,7 +130,7 @@ func TestPingBuilderHeader(t *testing.T) { defer d.Stop(t) expected := build.BuilderV1 - p, err := apiClient.Ping(ctx) + p, err := apiClient.Ping(ctx, client.PingOptions{}) assert.NilError(t, err) assert.Equal(t, p.BuilderVersion, expected) }) diff --git a/vendor/github.com/moby/moby/api/types/swarm/swarm.go b/vendor/github.com/moby/moby/api/types/swarm/swarm.go index 7d683b30ae..8421850310 100644 --- a/vendor/github.com/moby/moby/api/types/swarm/swarm.go +++ b/vendor/github.com/moby/moby/api/types/swarm/swarm.go @@ -214,16 +214,6 @@ type Info struct { Warnings []string `json:",omitempty"` } -// Status provides information about the current swarm status and role, -// obtained from the "Swarm" header in the API response. -type Status struct { - // NodeState represents the state of the node. - NodeState LocalNodeState - - // ControlAvailable indicates if the node is a swarm manager. - ControlAvailable bool -} - // Peer represents a peer. type Peer struct { NodeID string diff --git a/vendor/github.com/moby/moby/api/types/types.go b/vendor/github.com/moby/moby/api/types/types.go index 32fbcc639f..2844306c72 100644 --- a/vendor/github.com/moby/moby/api/types/types.go +++ b/vendor/github.com/moby/moby/api/types/types.go @@ -1,10 +1,5 @@ package types -import ( - "github.com/moby/moby/api/types/build" - "github.com/moby/moby/api/types/swarm" -) - const ( // MediaTypeRawStream is vendor specific MIME-Type set for raw TTY streams MediaTypeRawStream = "application/vnd.docker.raw-stream" @@ -22,24 +17,6 @@ const ( MediaTypeJSONSequence = "application/json-seq" ) -// Ping contains response of Engine API: -// GET "/_ping" -type Ping struct { - APIVersion string - OSType string - Experimental bool - BuilderVersion build.BuilderVersion - - // SwarmStatus provides information about the current swarm status of the - // engine, obtained from the "Swarm" header in the API response. - // - // It can be a nil struct if the API version does not provide this header - // in the ping response, or if an error occurred, in which case the client - // should use other ways to get the current swarm status, such as the /swarm - // endpoint. - SwarmStatus *swarm.Status -} - // ComponentVersion describes the version information for a specific component. type ComponentVersion struct { Name string diff --git a/vendor/github.com/moby/moby/client/client.go b/vendor/github.com/moby/moby/client/client.go index 3d004c30f5..8af2e0d242 100644 --- a/vendor/github.com/moby/moby/client/client.go +++ b/vendor/github.com/moby/moby/client/client.go @@ -56,7 +56,6 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/docker/go-connections/sockets" - "github.com/moby/moby/api/types" "github.com/moby/moby/api/types/versions" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) @@ -270,7 +269,7 @@ func (cli *Client) checkVersion(ctx context.Context) error { return nil } - ping, err := cli.Ping(ctx) + ping, err := cli.Ping(ctx, PingOptions{}) if err != nil { return err } @@ -317,7 +316,7 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) { cli.negotiateLock.Lock() defer cli.negotiateLock.Unlock() - ping, err := cli.Ping(ctx) + ping, err := cli.Ping(ctx, PingOptions{}) if err != nil { // FIXME(thaJeztah): Ping returns an error when failing to connect to the API; we should not swallow the error here, and instead returning it. return @@ -338,7 +337,8 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) { // // If the API server's ping response does not contain an API version, it falls // back to the oldest API version supported. -func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) { +func (cli *Client) NegotiateAPIVersionPing(pingResponse PingResult) { + // TODO(thaJeztah): should this take a "Ping" option? It only consumes the version. This method should be removed overall and not be exported. if !cli.manualOverride { // Avoid concurrent modification of version-related fields cli.negotiateLock.Lock() diff --git a/vendor/github.com/moby/moby/client/client_interfaces.go b/vendor/github.com/moby/moby/client/client_interfaces.go index f7bd8b62ce..62f8720d99 100644 --- a/vendor/github.com/moby/moby/client/client_interfaces.go +++ b/vendor/github.com/moby/moby/client/client_interfaces.go @@ -35,7 +35,7 @@ type stableAPIClient interface { DaemonHost() string ServerVersion(ctx context.Context) (types.Version, error) NegotiateAPIVersion(ctx context.Context) - NegotiateAPIVersionPing(types.Ping) + NegotiateAPIVersionPing(PingResult) HijackDialer Dialer() func(context.Context) (net.Conn, error) Close() error @@ -187,7 +187,7 @@ type SystemAPIClient interface { Info(ctx context.Context) (system.Info, error) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) - Ping(ctx context.Context) (types.Ping, error) + Ping(ctx context.Context, options PingOptions) (PingResult, error) } // VolumeAPIClient defines API client methods for the volumes diff --git a/vendor/github.com/moby/moby/client/ping.go b/vendor/github.com/moby/moby/client/ping.go index 96ee7bef66..80359fb457 100644 --- a/vendor/github.com/moby/moby/client/ping.go +++ b/vendor/github.com/moby/moby/client/ping.go @@ -6,11 +6,42 @@ import ( "path" "strings" - "github.com/moby/moby/api/types" "github.com/moby/moby/api/types/build" "github.com/moby/moby/api/types/swarm" ) +// PingOptions holds options for [client.Ping]. +type PingOptions struct { + // Add future optional parameters here +} + +// PingResult holds the result of a [Client.Ping] API call. +type PingResult struct { + APIVersion string + OSType string + Experimental bool + BuilderVersion build.BuilderVersion + + // SwarmStatus provides information about the current swarm status of the + // engine, obtained from the "Swarm" header in the API response. + // + // It can be a nil struct if the API version does not provide this header + // in the ping response, or if an error occurred, in which case the client + // should use other ways to get the current swarm status, such as the /swarm + // endpoint. + SwarmStatus *SwarmStatus +} + +// SwarmStatus provides information about the current swarm status and role, +// obtained from the "Swarm" header in the API response. +type SwarmStatus struct { + // NodeState represents the state of the node. + NodeState swarm.LocalNodeState + + // ControlAvailable indicates if the node is a swarm manager. + ControlAvailable bool +} + // Ping pings the server and returns the value of the "Docker-Experimental", // "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use // a HEAD request on the endpoint, but falls back to GET if HEAD is not supported @@ -18,13 +49,13 @@ import ( // may be returned if the daemon is in an unhealthy state, but returns errors // for other non-success status codes, failing to connect to the API, or failing // to parse the API response. -func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { +func (cli *Client) Ping(ctx context.Context, options PingOptions) (PingResult, error) { // Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest() // because ping requests are used during API version negotiation, so we want // to hit the non-versioned /_ping endpoint, not /v1.xx/_ping req, err := cli.buildRequest(ctx, http.MethodHead, path.Join(cli.basePath, "/_ping"), nil, nil) if err != nil { - return types.Ping{}, err + return PingResult{}, err } resp, err := cli.doRequest(req) defer ensureReaderClosed(resp) @@ -33,7 +64,7 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { // we got a "OK" (200) status. For non-200 status-codes, we fall // back to doing a GET request, as a HEAD request won't have a // response-body to get error details from. - return newPingResponse(resp), nil + return newPingResult(resp), nil } // 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) { defer ensureReaderClosed(resp) if err != nil { // Failed to connect. - return types.Ping{}, err + return PingResult{}, err } // GET request succeeded but may have returned a non-200 status. // Return a Ping response, together with any error returned by // the API server. - return newPingResponse(resp), checkResponseErr(resp) + return newPingResult(resp), checkResponseErr(resp) } -func newPingResponse(resp *http.Response) types.Ping { +func newPingResult(resp *http.Response) PingResult { if resp == nil { - return types.Ping{} + return PingResult{} } - var swarmStatus *swarm.Status + var swarmStatus *SwarmStatus if si := resp.Header.Get("Swarm"); si != "" { state, role, _ := strings.Cut(si, "/") - swarmStatus = &swarm.Status{ + swarmStatus = &SwarmStatus{ NodeState: swarm.LocalNodeState(state), ControlAvailable: role == "manager", } } - return types.Ping{ + return PingResult{ APIVersion: resp.Header.Get("Api-Version"), OSType: resp.Header.Get("Ostype"), Experimental: resp.Header.Get("Docker-Experimental") == "true",