diff --git a/client/buildkit/buildkit.go b/client/buildkit/buildkit.go index 70961bb26b40b..4c18ca5086fad 100644 --- a/client/buildkit/buildkit.go +++ b/client/buildkit/buildkit.go @@ -15,7 +15,7 @@ import ( // Example: // // bkclient.New(ctx, "", ClientOpts(c)...) -func ClientOpts(c client.CommonAPIClient) []bkclient.ClientOpt { +func ClientOpts(c client.HijackDialer) []bkclient.ClientOpt { return []bkclient.ClientOpt{ bkclient.WithSessionDialer(func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { return c.DialHijack(ctx, "/session", proto, meta) diff --git a/client/interface_experimental.go b/client/checkpoint.go similarity index 76% rename from client/interface_experimental.go rename to client/checkpoint.go index c585c104590fd..f690f7c9524e5 100644 --- a/client/interface_experimental.go +++ b/client/checkpoint.go @@ -6,11 +6,11 @@ import ( "github.com/docker/docker/api/types/checkpoint" ) -type apiClientExperimental interface { - CheckpointAPIClient -} - -// CheckpointAPIClient defines API client methods for the checkpoints +// CheckpointAPIClient defines API client methods for the checkpoints. +// +// Experimental: checkpoint and restore is still an experimental feature, +// and only available if the daemon is running with experimental features +// enabled. type CheckpointAPIClient interface { CheckpointCreate(ctx context.Context, container string, options checkpoint.CreateOptions) error CheckpointDelete(ctx context.Context, container string, options checkpoint.DeleteOptions) error diff --git a/client/client.go b/client/client.go index 97dbbf70b928c..c980b66a16aaf 100644 --- a/client/client.go +++ b/client/client.go @@ -99,6 +99,9 @@ const DummyHost = "api.moby.localhost" // recent version before negotiation was introduced. const fallbackAPIVersion = "1.24" +// Ensure that Client always implements APIClient. +var _ APIClient = &Client{} + // Client is the API client that performs all operations // against a docker server. type Client struct { @@ -449,6 +452,10 @@ func (cli *Client) dialerFromTransport() func(context.Context, string, string) ( // // ["docker dial-stdio"]: https://github.com/docker/cli/pull/1014 func (cli *Client) Dialer() func(context.Context) (net.Conn, error) { + return cli.dialer() +} + +func (cli *Client) dialer() func(context.Context) (net.Conn, error) { return func(ctx context.Context) (net.Conn, error) { if dialFn := cli.dialerFromTransport(); dialFn != nil { return dialFn(ctx, cli.proto, cli.addr) diff --git a/client/interface.go b/client/client_interfaces.go similarity index 94% rename from client/interface.go rename to client/client_interfaces.go index 470923a243d3c..a3ef622eb3e78 100644 --- a/client/interface.go +++ b/client/client_interfaces.go @@ -20,17 +20,23 @@ import ( ) // CommonAPIClient is the common methods between stable and experimental versions of APIClient. -type CommonAPIClient interface { +// +// Deprecated: use [APIClient] instead. This type will be an alias for [APIClient] in the next release, and removed after. +type CommonAPIClient = stableAPIClient + +// APIClient is an interface that clients that talk with a docker server must implement. +type APIClient interface { + stableAPIClient + CheckpointAPIClient // CheckpointAPIClient is still experimental. +} + +type stableAPIClient interface { ConfigAPIClient ContainerAPIClient DistributionAPIClient ImageAPIClient - NodeAPIClient NetworkAPIClient PluginAPIClient - ServiceAPIClient - SwarmAPIClient - SecretAPIClient SystemAPIClient VolumeAPIClient ClientVersion() string @@ -39,9 +45,25 @@ type CommonAPIClient interface { ServerVersion(ctx context.Context) (types.Version, error) NegotiateAPIVersion(ctx context.Context) NegotiateAPIVersionPing(types.Ping) - DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) + HijackDialer Dialer() func(context.Context) (net.Conn, error) Close() error + SwarmManagementAPIClient +} + +// SwarmManagementAPIClient defines all methods for managing Swarm-specific +// objects. +type SwarmManagementAPIClient interface { + SwarmAPIClient + NodeAPIClient + ServiceAPIClient + SecretAPIClient + ConfigAPIClient +} + +// HijackDialer defines methods for a hijack dialer. +type HijackDialer interface { + DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) } // ContainerAPIClient defines API client methods for the containers diff --git a/client/hijack.go b/client/hijack.go index 839d4c5cd6baa..2c78fad002d05 100644 --- a/client/hijack.go +++ b/client/hijack.go @@ -25,12 +25,17 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu if err != nil { return types.HijackedResponse{}, err } - conn, mediaType, err := cli.setupHijackConn(req, "tcp") + conn, mediaType, err := setupHijackConn(cli.dialer(), req, "tcp") if err != nil { return types.HijackedResponse{}, err } - return types.NewHijackedResponse(conn, mediaType), err + if versions.LessThan(cli.ClientVersion(), "1.42") { + // Prior to 1.42, Content-Type is always set to raw-stream and not relevant + mediaType = "" + } + + return types.NewHijackedResponse(conn, mediaType), nil } // DialHijack returns a hijacked connection with negotiated protocol proto. @@ -41,16 +46,15 @@ func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[s } req = cli.addHeaders(req, meta) - conn, _, err := cli.setupHijackConn(req, proto) + conn, _, err := setupHijackConn(cli.Dialer(), req, proto) return conn, err } -func (cli *Client) setupHijackConn(req *http.Request, proto string) (_ net.Conn, _ string, retErr error) { +func setupHijackConn(dialer func(context.Context) (net.Conn, error), req *http.Request, proto string) (_ net.Conn, _ string, retErr error) { ctx := req.Context() req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", proto) - dialer := cli.Dialer() conn, err := dialer(ctx) if err != nil { return nil, "", errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?") @@ -96,13 +100,7 @@ func (cli *Client) setupHijackConn(req *http.Request, proto string) (_ net.Conn, hc.r.Reset(nil) } - var mediaType string - if versions.GreaterThanOrEqualTo(cli.ClientVersion(), "1.42") { - // Prior to 1.42, Content-Type is always set to raw-stream and not relevant - mediaType = resp.Header.Get("Content-Type") - } - - return conn, mediaType, nil + return conn, resp.Header.Get("Content-Type"), nil } // hijackedConn wraps a net.Conn and is returned by setupHijackConn in the case diff --git a/client/interface_stable.go b/client/interface_stable.go deleted file mode 100644 index 5502cd7426614..0000000000000 --- a/client/interface_stable.go +++ /dev/null @@ -1,10 +0,0 @@ -package client // import "github.com/docker/docker/client" - -// APIClient is an interface that clients that talk with a docker server must implement. -type APIClient interface { - CommonAPIClient - apiClientExperimental -} - -// Ensure that Client always implements APIClient. -var _ APIClient = &Client{} diff --git a/integration/internal/swarm/states.go b/integration/internal/swarm/states.go index 0800a75c84e43..4e1822a3ae555 100644 --- a/integration/internal/swarm/states.go +++ b/integration/internal/swarm/states.go @@ -87,7 +87,7 @@ func RunningTasksCount(ctx context.Context, client client.ServiceAPIClient, serv // JobComplete is a poll function for determining that a ReplicatedJob is // completed additionally, while polling, it verifies that the job never // exceeds MaxConcurrent running tasks -func JobComplete(ctx context.Context, client client.CommonAPIClient, service swarmtypes.Service) func(log poll.LogT) poll.Result { +func JobComplete(ctx context.Context, client client.ServiceAPIClient, service swarmtypes.Service) func(log poll.LogT) poll.Result { filter := filters.NewArgs(filters.Arg("service", service.ID)) var jobIteration swarmtypes.Version