vendor: github.com/moby/moby/client v0.5.2-dev (b9b109e4d341)

full diff: https://github.com/moby/moby/compare/client/v0.5.1...b9b109e4d341

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2026-09-03 20:52:14 +02:00
parent cb6c7bc03f
commit 91fc8ad4a2
41 changed files with 143 additions and 137 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ require (
github.com/mattn/go-runewidth v0.0.28
github.com/moby/go-archive v0.3.3
github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341
github.com/moby/moby/client v0.5.1
github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341
github.com/moby/patternmatcher v0.6.1
github.com/moby/swarmkit/v2 v2.1.2
github.com/moby/sys/atomicwriter v0.1.0
+2 -2
View File
@@ -90,8 +90,8 @@ github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME
github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE=
github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341 h1:ooFZgSrQ/xRDfGves7imF3goa+32bEcJrcXIDlL/qpI=
github.com/moby/moby/api v1.55.1-0.20260903164743-b9b109e4d341/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.5.1 h1:tYNaJno4c0HXz12y5BiqEDy0rVTYkWzI26lGvnTMiJw=
github.com/moby/moby/client v0.5.1/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM=
github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341 h1:/XUuyJxH/HXe5MBcQo+YG9hAtZTQNpWFeJ5PcxL+hs8=
github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM=
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/swarmkit/v2 v2.1.2 h1:1WDZAI6HVYNKdCG4zlXnTAPyLsLwuhRGWlHoOUf5Z6I=
+2 -4
View File
@@ -24,13 +24,11 @@ func (cli *Client) CheckpointCreate(ctx context.Context, containerID string, opt
if err != nil {
return CheckpointCreateResult{}, err
}
requestBody := checkpoint.CreateRequest{
resp, err := cli.post(ctx, "/containers/"+containerID+"/checkpoints", nil, nil, checkpoint.CreateRequest{
CheckpointID: options.CheckpointID,
CheckpointDir: options.CheckpointDir,
Exit: options.Exit,
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/checkpoints", nil, requestBody, nil)
})
defer ensureReaderClosed(resp)
return CheckpointCreateResult{}, err
}
+1 -1
View File
@@ -109,7 +109,7 @@ const DummyHost = "api.moby.localhost"
// overriding the version and disable API-version negotiation.
//
// This version may be lower than the version of the api library module used.
const MaxAPIVersion = "1.55"
const MaxAPIVersion = "1.56"
// MinAPIVersion is the minimum API version supported by the client. API versions
// below this version are not considered when performing API-version negotiation.
+1 -1
View File
@@ -19,7 +19,7 @@ type ConfigCreateResult struct {
// ConfigCreate creates a new config.
func (cli *Client) ConfigCreate(ctx context.Context, options ConfigCreateOptions) (ConfigCreateResult, error) {
resp, err := cli.post(ctx, "/configs/create", nil, options.Spec, nil)
resp, err := cli.post(ctx, "/configs/create", nil, nil, options.Spec)
defer ensureReaderClosed(resp)
if err != nil {
return ConfigCreateResult{}, err
+1 -1
View File
@@ -24,7 +24,7 @@ func (cli *Client) ConfigUpdate(ctx context.Context, id string, options ConfigUp
}
query := url.Values{}
query.Set("version", options.Version.String())
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, options.Spec, nil)
resp, err := cli.post(ctx, "/configs/"+id+"/update", query, nil, options.Spec)
defer ensureReaderClosed(resp)
if err != nil {
return ConfigUpdateResult{}, err
+1 -1
View File
@@ -65,7 +65,7 @@ func (cli *Client) ContainerCommit(ctx context.Context, containerID string, opti
}
var response container.CommitResponse
resp, err := cli.post(ctx, "/commit", query, options.Config, nil)
resp, err := cli.post(ctx, "/commit", query, nil, options.Config)
defer ensureReaderClosed(resp)
if err != nil {
return ContainerCommitResult{}, err
+1 -1
View File
@@ -77,7 +77,7 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID string, opti
query.Set("copyUIDGID", "true")
}
response, err := cli.putRaw(ctx, "/containers/"+containerID+"/archive", query, options.Content, nil)
response, err := cli.putRaw(ctx, "/containers/"+containerID+"/archive", query, nil, options.Content)
defer ensureReaderClosed(response)
if err != nil {
return CopyToContainerResult{}, err
+30 -37
View File
@@ -5,7 +5,7 @@ import (
"encoding/json"
"net/url"
"path"
"sort"
"slices"
"strings"
cerrdefs "github.com/containerd/errdefs"
@@ -35,13 +35,6 @@ func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateO
return ContainerCreateResult{}, cerrdefs.ErrInvalidArgument.WithMessage("config.Image or Image is required")
}
var response container.CreateResponse
if options.HostConfig != nil {
options.HostConfig.CapAdd = normalizeCapabilities(options.HostConfig.CapAdd)
options.HostConfig.CapDrop = normalizeCapabilities(options.HostConfig.CapDrop)
}
query := url.Values{}
if options.Platform != nil {
if p := formatPlatform(*options.Platform); p != "unknown" {
@@ -53,18 +46,17 @@ func (cli *Client) ContainerCreate(ctx context.Context, options ContainerCreateO
query.Set("name", options.Name)
}
body := container.CreateRequest{
resp, err := cli.post(ctx, "/containers/create", query, nil, container.CreateRequest{
Config: cfg,
HostConfig: options.HostConfig,
HostConfig: normalizeHostConfig(options.HostConfig),
NetworkingConfig: options.NetworkingConfig,
}
resp, err := cli.post(ctx, "/containers/create", query, body, nil)
})
defer ensureReaderClosed(resp)
if err != nil {
return ContainerCreateResult{}, err
}
var response container.CreateResponse
err = json.NewDecoder(resp.Body).Decode(&response)
return ContainerCreateResult{ID: response.ID, Warnings: response.Warnings}, err
}
@@ -86,6 +78,17 @@ func formatPlatform(platform ocispec.Platform) string {
// allCapabilities is a magic value for "all capabilities"
const allCapabilities = "ALL"
// normalizeCap normalizes a capability to its canonical format by upper-casing
// and adding a "CAP_" prefix (if not yet present). It also accepts the "ALL"
// magic-value.
func normalizeCap(c string) string {
c = strings.ToUpper(c)
if c != allCapabilities && !strings.HasPrefix(c, "CAP_") {
c = "CAP_" + c
}
return c
}
// normalizeCapabilities normalizes capabilities to their canonical form,
// removes duplicates, and sorts the results.
//
@@ -94,32 +97,22 @@ const allCapabilities = "ALL"
//
// [caps.NormalizeLegacyCapabilities]: https://github.com/moby/moby/blob/v28.3.2/oci/caps/utils.go#L56
func normalizeCapabilities(caps []string) []string {
var normalized []string
unique := make(map[string]struct{})
for _, c := range caps {
c = normalizeCap(c)
if _, ok := unique[c]; ok {
continue
}
unique[c] = struct{}{}
normalized = append(normalized, c)
normalized := slices.Clone(caps)
for i, c := range normalized {
normalized[i] = normalizeCap(c)
}
sort.Strings(normalized)
return normalized
slices.Sort(normalized)
return slices.Compact(normalized)
}
// normalizeCap normalizes a capability to its canonical format by upper-casing
// and adding a "CAP_" prefix (if not yet present). It also accepts the "ALL"
// magic-value.
func normalizeCap(capability string) string {
capability = strings.ToUpper(capability)
if capability == allCapabilities {
return capability
// normalizeHostConfig returns a shallow copy of hostConfig with capabilities normalized.
func normalizeHostConfig(hostConfig *container.HostConfig) *container.HostConfig {
if hostConfig == nil {
return nil
}
if !strings.HasPrefix(capability, "CAP_") {
capability = "CAP_" + capability
}
return capability
normalized := *hostConfig
normalized.CapAdd = normalizeCapabilities(hostConfig.CapAdd)
normalized.CapDrop = normalizeCapabilities(hostConfig.CapDrop)
return &normalized
}
+4 -7
View File
@@ -42,7 +42,7 @@ func (cli *Client) ExecCreate(ctx context.Context, containerID string, options E
return ExecCreateResult{}, err
}
req := container.ExecCreateRequest{
resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, nil, container.ExecCreateRequest{
User: options.User,
Privileged: options.Privileged,
Tty: options.TTY,
@@ -54,9 +54,7 @@ func (cli *Client) ExecCreate(ctx context.Context, containerID string, options E
Env: options.Env,
WorkingDir: options.WorkingDir,
Cmd: options.Cmd,
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, req, nil)
})
defer ensureReaderClosed(resp)
if err != nil {
return ExecCreateResult{}, err
@@ -91,12 +89,11 @@ func (cli *Client) ExecStart(ctx context.Context, execID string, options ExecSta
return ExecStartResult{}, err
}
req := container.ExecStartRequest{
resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, nil, container.ExecStartRequest{
Detach: options.Detach,
Tty: options.TTY,
ConsoleSize: consoleSize,
}
resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, req, nil)
})
defer ensureReaderClosed(resp)
return ExecStartResult{}, err
}
+4 -9
View File
@@ -26,15 +26,10 @@ func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, opti
return ContainerUpdateResult{}, err
}
updateConfig := container.UpdateConfig{}
if options.Resources != nil {
updateConfig.Resources = *options.Resources
}
if options.RestartPolicy != nil {
updateConfig.RestartPolicy = *options.RestartPolicy
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)
resp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, nil, container.UpdateConfig{
Resources: valueOrZero(options.Resources),
RestartPolicy: valueOrZero(options.RestartPolicy),
})
defer ensureReaderClosed(resp)
if err != nil {
return ContainerUpdateResult{}, err
+1 -1
View File
@@ -18,7 +18,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
if err != nil {
return HijackedResponse{}, err
}
req, err := cli.buildRequest(ctx, http.MethodPost, cli.getAPIPath(ctx, path, query), jsonBody, headers)
req, err := cli.buildRequest(ctx, http.MethodPost, cli.getAPIPath(ctx, path, query), headers, jsonBody)
if err != nil {
return HijackedResponse{}, err
}
+1 -1
View File
@@ -32,7 +32,7 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
headers.Set("Content-Type", "application/x-tar")
resp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
resp, err := cli.postRaw(ctx, "/build", query, headers, buildContext)
if err != nil {
return ImageBuildResult{}, err
}
+1 -1
View File
@@ -46,7 +46,7 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re
query.Add("changes", change)
}
resp, err := cli.postRaw(ctx, "/images/create", query, source.Source, nil)
resp, err := cli.postRaw(ctx, "/images/create", query, nil, source.Source)
if err != nil {
return nil, err
}
+2 -3
View File
@@ -42,9 +42,8 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
query["platform"] = p
}
resp, err := cli.postRaw(ctx, "/images/load", query, input, http.Header{
"Content-Type": {"application/x-tar"},
})
headers := http.Header{"Content-Type": {"application/x-tar"}}
resp, err := cli.postRaw(ctx, "/images/load", query, headers, input)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -89,5 +89,5 @@ func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, resolve
hdr.Set(registry.AuthHeader, registryAuth)
}
}
return cli.post(ctx, "/images/create", query, nil, hdr)
return cli.post(ctx, "/images/create", query, hdr, nil)
}
+1 -1
View File
@@ -94,5 +94,5 @@ func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.V
// We use [http.NoBody], which gets marshaled to an empty JSON document.
//
// see: https://github.com/moby/moby/commit/ea29dffaa541289591aa44fa85d2a596ce860e16
return cli.post(ctx, "/images/"+imageID+"/push", query, http.NoBody, hdr)
return cli.post(ctx, "/images/"+imageID+"/push", query, hdr, http.NoBody)
}
+1 -1
View File
@@ -32,7 +32,7 @@ func (cli *Client) RegistryLogin(ctx context.Context, options RegistryLoginOptio
RegistryToken: options.RegistryToken,
}
resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
resp, err := cli.post(ctx, "/auth", url.Values{}, nil, auth)
defer ensureReaderClosed(resp)
if err != nil {
+1 -1
View File
@@ -34,7 +34,7 @@ func (cli *Client) NetworkConnect(ctx context.Context, networkID string, options
Container: containerID,
EndpointConfig: options.EndpointConfig,
}
resp, err := cli.post(ctx, "/networks/"+networkID+"/connect", nil, nc, nil)
resp, err := cli.post(ctx, "/networks/"+networkID+"/connect", nil, nil, nc)
defer ensureReaderClosed(resp)
return NetworkConnectResult{}, err
}
+1 -1
View File
@@ -51,7 +51,7 @@ func (cli *Client) NetworkCreate(ctx context.Context, name string, options Netwo
req.ConfigFrom = &network.ConfigReference{Network: options.ConfigFrom}
}
resp, err := cli.post(ctx, "/networks/create", nil, req, nil)
resp, err := cli.post(ctx, "/networks/create", nil, nil, req)
defer ensureReaderClosed(resp)
if err != nil {
return NetworkCreateResult{}, err
+2 -3
View File
@@ -30,11 +30,10 @@ func (cli *Client) NetworkDisconnect(ctx context.Context, networkID string, opti
return NetworkDisconnectResult{}, err
}
req := network.DisconnectRequest{
resp, err := cli.post(ctx, "/networks/"+networkID+"/disconnect", nil, nil, network.DisconnectRequest{
Container: containerID,
Force: options.Force,
}
resp, err := cli.post(ctx, "/networks/"+networkID+"/disconnect", nil, req, nil)
})
defer ensureReaderClosed(resp)
return NetworkDisconnectResult{}, err
}
+1 -1
View File
@@ -25,7 +25,7 @@ func (cli *Client) NodeUpdate(ctx context.Context, nodeID string, options NodeUp
query := url.Values{}
query.Set("version", options.Version.String())
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, options.Spec, nil)
resp, err := cli.post(ctx, "/nodes/"+nodeID+"/update", query, nil, options.Spec)
defer ensureReaderClosed(resp)
return NodeUpdateResult{}, err
}
+33 -7
View File
@@ -26,7 +26,8 @@ const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
type DisplayOpt func(*displayOpts) error
type displayOpts struct {
auxCallback func(jsonstream.Message)
auxCallback func(jsonstream.Message)
messagePrinter func(jsonstream.Message, io.Writer) error
}
// WithAuxCallback registers a callback that is invoked for auxiliary
@@ -39,6 +40,18 @@ func WithAuxCallback(fn func(jsonstream.Message)) DisplayOpt {
}
}
// WithMessagePrinter sets a custom printer for non-auxiliary messages.
// If set, [DisplayStream] and [DisplayMessages] pass each message to fn instead
// of using the default terminal-aware presentation. The caller is responsible
// for handling errors reported through [jsonstream.Message.Error] and any
// terminal handling, including progress rendering and terminal-width detection.
func WithMessagePrinter(fn func(jsonstream.Message, io.Writer) error) DisplayOpt {
return func(opts *displayOpts) error {
opts.messagePrinter = fn
return nil
}
}
func RenderTUIProgress(p jsonstream.Progress, width uint16) string {
var (
pbBox string
@@ -232,13 +245,19 @@ func displayJSONMessages(messages iter.Seq2[jsonstream.Message, error], out io.W
}
}
auxCallback := cfg.auxCallback
messagePrinter := cfg.messagePrinter
ids := make(map[string]uint)
var width uint16 = 200
if isTerminal {
ws, err := term.GetWinsize(terminalFd)
if err == nil {
width = ws.Width
var (
ids map[string]uint
width uint16 = 200
)
if messagePrinter == nil {
ids = make(map[string]uint)
if isTerminal {
ws, err := term.GetWinsize(terminalFd)
if err == nil {
width = ws.Width
}
}
}
@@ -255,6 +274,13 @@ func displayJSONMessages(messages iter.Seq2[jsonstream.Message, error], out io.W
continue
}
if messagePrinter != nil {
if err := messagePrinter(jm, out); err != nil {
return err
}
continue
}
if jm.ID != "" && jm.Progress != nil {
line, ok := ids[jm.ID]
if !ok {
+1 -1
View File
@@ -25,7 +25,7 @@ func (cli *Client) PluginCreate(ctx context.Context, createContext io.Reader, cr
query := url.Values{}
query.Set("name", createOptions.RepoName)
resp, err := cli.postRaw(ctx, "/plugins/create", query, createContext, headers)
resp, err := cli.postRaw(ctx, "/plugins/create", query, headers, createContext)
defer ensureReaderClosed(resp)
return PluginCreateResult{}, err
}
+5 -6
View File
@@ -99,13 +99,12 @@ func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, re
})
}
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges plugin.Privileges, registryAuth string) (*http.Response, error) {
return cli.post(ctx, "/plugins/pull", query, privileges, http.Header{
registry.AuthHeader: {registryAuth},
})
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges []plugin.Privilege, registryAuth string) (*http.Response, error) {
headers := http.Header{registry.AuthHeader: {registryAuth}}
return cli.post(ctx, "/plugins/pull", query, headers, privileges)
}
func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options pluginOptions) (plugin.Privileges, error) {
func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options pluginOptions) ([]plugin.Privilege, error) {
resp, err := cli.tryPluginPrivileges(ctx, query, options.getRegistryAuth())
if cerrdefs.IsUnauthorized(err) && options.getPrivilegeFunc() != nil {
// TODO: do inspect before to check existing name before checking privileges
@@ -122,7 +121,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values,
return nil, err
}
var privileges plugin.Privileges
var privileges []plugin.Privilege
if err := json.NewDecoder(resp.Body).Decode(&privileges); err != nil {
ensureReaderClosed(resp)
return nil, err
+2 -3
View File
@@ -24,9 +24,8 @@ func (cli *Client) PluginPush(ctx context.Context, name string, options PluginPu
if err != nil {
return PluginPushResult{}, err
}
resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, nil, http.Header{
registry.AuthHeader: {options.RegistryAuth},
})
headers := http.Header{registry.AuthHeader: {options.RegistryAuth}}
resp, err := cli.post(ctx, "/plugins/"+name+"/push", nil, headers, nil)
if err != nil {
return PluginPushResult{}, err
}
+1 -1
View File
@@ -21,7 +21,7 @@ func (cli *Client) PluginSet(ctx context.Context, name string, options PluginSet
return PluginSetResult{}, err
}
resp, err := cli.post(ctx, "/plugins/"+name+"/set", nil, options.Args, nil)
resp, err := cli.post(ctx, "/plugins/"+name+"/set", nil, nil, options.Args)
defer ensureReaderClosed(resp)
return PluginSetResult{}, err
}
+3 -4
View File
@@ -58,10 +58,9 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options Plugi
return resp.Body, nil
}
func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges plugin.Privileges, name, registryAuth string) (*http.Response, error) {
return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, http.Header{
registry.AuthHeader: {registryAuth},
})
func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges []plugin.Privilege, name, registryAuth string) (*http.Response, error) {
headers := http.Header{registry.AuthHeader: {registryAuth}}
return cli.post(ctx, "/plugins/"+name+"/upgrade", query, headers, privileges)
}
func (o *PluginUpgradeOptions) getRegistryAuth() string {
+14 -14
View File
@@ -19,49 +19,49 @@ import (
// head sends an http request to the docker API using the method HEAD.
func (cli *Client) head(ctx context.Context, path string, query url.Values, headers http.Header) (*http.Response, error) {
return cli.sendRequest(ctx, http.MethodHead, path, query, nil, headers)
return cli.sendRequest(ctx, http.MethodHead, path, query, headers, nil)
}
// get sends an http request to the docker API using the method GET with a specific Go context.
func (cli *Client) get(ctx context.Context, path string, query url.Values, headers http.Header) (*http.Response, error) {
return cli.sendRequest(ctx, http.MethodGet, path, query, nil, headers)
return cli.sendRequest(ctx, http.MethodGet, path, query, headers, nil)
}
// post sends an http POST request to the API.
func (cli *Client) post(ctx context.Context, path string, query url.Values, body any, headers http.Header) (*http.Response, error) {
func (cli *Client) post(ctx context.Context, path string, query url.Values, headers http.Header, body any) (*http.Response, error) {
jsonBody, headers, err := prepareJSONRequest(body, headers)
if err != nil {
return nil, err
}
return cli.sendRequest(ctx, http.MethodPost, path, query, jsonBody, headers)
return cli.sendRequest(ctx, http.MethodPost, path, query, headers, jsonBody)
}
func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers http.Header) (*http.Response, error) {
return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers)
func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, headers http.Header, body io.Reader) (*http.Response, error) {
return cli.sendRequest(ctx, http.MethodPost, path, query, headers, body)
}
func (cli *Client) put(ctx context.Context, path string, query url.Values, body any, headers http.Header) (*http.Response, error) {
func (cli *Client) put(ctx context.Context, path string, query url.Values, headers http.Header, body any) (*http.Response, error) {
jsonBody, headers, err := prepareJSONRequest(body, headers)
if err != nil {
return nil, err
}
return cli.putRaw(ctx, path, query, jsonBody, headers)
return cli.putRaw(ctx, path, query, headers, jsonBody)
}
// putRaw sends an http request to the docker API using the method PUT.
func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers http.Header) (*http.Response, error) {
func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, headers http.Header, body io.Reader) (*http.Response, error) {
// PUT requests are expected to always have a body (apparently)
// so explicitly pass an empty body to sendRequest to signal that
// it should set the Content-Type header if not already present.
if body == nil {
body = http.NoBody
}
return cli.sendRequest(ctx, http.MethodPut, path, query, body, headers)
return cli.sendRequest(ctx, http.MethodPut, path, query, headers, body)
}
// delete sends an http request to the docker API using the method DELETE.
func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers http.Header) (*http.Response, error) {
return cli.sendRequest(ctx, http.MethodDelete, path, query, nil, headers)
return cli.sendRequest(ctx, http.MethodDelete, path, query, headers, nil)
}
// prepareJSONRequest encodes the given body to JSON and returns it as an [io.Reader], and sets the Content-Type
@@ -87,7 +87,7 @@ func prepareJSONRequest(body any, headers http.Header) (io.Reader, http.Header,
return jsonBody, hdr, nil
}
func (cli *Client) buildRequest(ctx context.Context, method, path string, body io.Reader, headers http.Header) (*http.Request, error) {
func (cli *Client) buildRequest(ctx context.Context, method, path string, headers http.Header, body io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, path, body)
if err != nil {
return nil, err
@@ -104,8 +104,8 @@ func (cli *Client) buildRequest(ctx context.Context, method, path string, body i
return req, nil
}
func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers http.Header) (*http.Response, error) {
req, err := cli.buildRequest(ctx, method, cli.getAPIPath(ctx, path, query), body, headers)
func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, headers http.Header, body io.Reader) (*http.Response, error) {
req, err := cli.buildRequest(ctx, method, cli.getAPIPath(ctx, path, query), headers, body)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -19,7 +19,7 @@ type SecretCreateResult struct {
// SecretCreate creates a new secret.
func (cli *Client) SecretCreate(ctx context.Context, options SecretCreateOptions) (SecretCreateResult, error) {
resp, err := cli.post(ctx, "/secrets/create", nil, options.Spec, nil)
resp, err := cli.post(ctx, "/secrets/create", nil, nil, options.Spec)
defer ensureReaderClosed(resp)
if err != nil {
return SecretCreateResult{}, err
+1 -1
View File
@@ -24,7 +24,7 @@ func (cli *Client) SecretUpdate(ctx context.Context, id string, options SecretUp
}
query := url.Values{}
query.Set("version", options.Version.String())
resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, options.Spec, nil)
resp, err := cli.post(ctx, "/secrets/"+id+"/update", query, nil, options.Spec)
defer ensureReaderClosed(resp)
if err != nil {
return SecretUpdateResult{}, err
+1 -1
View File
@@ -78,7 +78,7 @@ func (cli *Client) ServiceCreate(ctx context.Context, options ServiceCreateOptio
if options.EncodedRegistryAuth != "" {
headers[registry.AuthHeader] = []string{options.EncodedRegistryAuth}
}
resp, err := cli.post(ctx, "/services/create", nil, options.Spec, headers)
resp, err := cli.post(ctx, "/services/create", nil, headers, options.Spec)
defer ensureReaderClosed(resp)
if err != nil {
return ServiceCreateResult{}, err
+1 -1
View File
@@ -101,7 +101,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, options
if options.EncodedRegistryAuth != "" {
headers.Set(registry.AuthHeader, options.EncodedRegistryAuth)
}
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, options.Spec, headers)
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, headers, options.Spec)
defer ensureReaderClosed(resp)
if err != nil {
return ServiceUpdateResult{}, err
+2 -4
View File
@@ -29,7 +29,7 @@ type SwarmInitResult struct {
// SwarmInit initializes the swarm.
func (cli *Client) SwarmInit(ctx context.Context, options SwarmInitOptions) (SwarmInitResult, error) {
req := swarm.InitRequest{
resp, err := cli.post(ctx, "/swarm/init", nil, nil, swarm.InitRequest{
ListenAddr: options.ListenAddr,
AdvertiseAddr: options.AdvertiseAddr,
DataPathAddr: options.DataPathAddr,
@@ -40,9 +40,7 @@ func (cli *Client) SwarmInit(ctx context.Context, options SwarmInitOptions) (Swa
Availability: options.Availability,
DefaultAddrPool: options.DefaultAddrPool,
SubnetSize: options.SubnetSize,
}
resp, err := cli.post(ctx, "/swarm/init", nil, req, nil)
})
defer ensureReaderClosed(resp)
if err != nil {
return SwarmInitResult{}, err
+2 -4
View File
@@ -23,16 +23,14 @@ type SwarmJoinResult struct {
// SwarmJoin joins the swarm.
func (cli *Client) SwarmJoin(ctx context.Context, options SwarmJoinOptions) (SwarmJoinResult, error) {
req := swarm.JoinRequest{
resp, err := cli.post(ctx, "/swarm/join", nil, nil, swarm.JoinRequest{
ListenAddr: options.ListenAddr,
AdvertiseAddr: options.AdvertiseAddr,
DataPathAddr: options.DataPathAddr,
RemoteAddrs: options.RemoteAddrs,
JoinToken: options.JoinToken,
Availability: options.Availability,
}
resp, err := cli.post(ctx, "/swarm/join", nil, req, nil)
})
defer ensureReaderClosed(resp)
return SwarmJoinResult{}, err
}
+2 -3
View File
@@ -16,10 +16,9 @@ type SwarmUnlockResult struct{}
// SwarmUnlock unlocks locked swarm.
func (cli *Client) SwarmUnlock(ctx context.Context, options SwarmUnlockOptions) (SwarmUnlockResult, error) {
req := &swarm.UnlockRequest{
resp, err := cli.post(ctx, "/swarm/unlock", nil, nil, swarm.UnlockRequest{
UnlockKey: options.Key,
}
resp, err := cli.post(ctx, "/swarm/unlock", nil, req, nil)
})
defer ensureReaderClosed(resp)
return SwarmUnlockResult{}, err
}
+1 -1
View File
@@ -27,7 +27,7 @@ func (cli *Client) SwarmUpdate(ctx context.Context, options SwarmUpdateOptions)
query.Set("rotateWorkerToken", strconv.FormatBool(options.RotateWorkerToken))
query.Set("rotateManagerToken", strconv.FormatBool(options.RotateManagerToken))
query.Set("rotateManagerUnlockKey", strconv.FormatBool(options.RotateManagerUnlockKey))
resp, err := cli.post(ctx, "/swarm/update", query, options.Spec, nil)
resp, err := cli.post(ctx, "/swarm/update", query, nil, options.Spec)
defer ensureReaderClosed(resp)
return SwarmUpdateResult{}, err
}
+8
View File
@@ -33,6 +33,14 @@ func trimID(objType, id string) (string, error) {
return id, nil
}
// valueOrZero returns the value pointed to by p, or the zero value of T if p is nil.
func valueOrZero[T any](p *T) (zero T) {
if p != nil {
return *p
}
return zero
}
// parseAPIVersion checks v to be a well-formed ("<major>.<minor>")
// API version. It returns an error if the value is empty or does not
// have the correct format, but does not validate if the API version is
+2 -3
View File
@@ -23,14 +23,13 @@ type VolumeCreateResult struct {
// VolumeCreate creates a volume in the docker host.
func (cli *Client) VolumeCreate(ctx context.Context, options VolumeCreateOptions) (VolumeCreateResult, error) {
createRequest := volume.CreateRequest{
resp, err := cli.post(ctx, "/volumes/create", nil, nil, volume.CreateRequest{
Name: options.Name,
Driver: options.Driver,
DriverOpts: options.DriverOpts,
Labels: options.Labels,
ClusterVolumeSpec: options.ClusterVolumeSpec,
}
resp, err := cli.post(ctx, "/volumes/create", nil, createRequest, nil)
})
defer ensureReaderClosed(resp)
if err != nil {
return VolumeCreateResult{}, err
+1 -1
View File
@@ -31,7 +31,7 @@ func (cli *Client) VolumeUpdate(ctx context.Context, volumeID string, options Vo
query := url.Values{}
query.Set("version", options.Version.String())
resp, err := cli.put(ctx, "/volumes/"+volumeID, query, options, nil)
resp, err := cli.put(ctx, "/volumes/"+volumeID, query, nil, options)
defer ensureReaderClosed(resp)
if err != nil {
return VolumeUpdateResult{}, err
+1 -1
View File
@@ -190,7 +190,7 @@ github.com/moby/moby/api/types/storage
github.com/moby/moby/api/types/swarm
github.com/moby/moby/api/types/system
github.com/moby/moby/api/types/volume
# github.com/moby/moby/client v0.5.1
# github.com/moby/moby/client v0.5.2-0.20260903164743-b9b109e4d341
## explicit; go 1.24
github.com/moby/moby/client
github.com/moby/moby/client/internal