vendor: github.com/docker/docker v28.0.0-rc.2

full diff: https://github.com/docker/docker/compare/v28.0.0-rc.1...v28.0.0-rc.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2025-02-14 19:39:59 +01:00
parent ef4e9fea83
commit 461bd9e5d1
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
95 changed files with 776 additions and 488 deletions

2
go.mod
View File

@ -19,7 +19,7 @@ require (
github.com/distribution/reference v0.6.0 github.com/distribution/reference v0.6.0
github.com/docker/cli v28.0.0-rc.1+incompatible github.com/docker/cli v28.0.0-rc.1+incompatible
github.com/docker/cli-docs-tool v0.9.0 github.com/docker/cli-docs-tool v0.9.0
github.com/docker/docker v28.0.0-rc.1+incompatible github.com/docker/docker v28.0.0-rc.2+incompatible
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
github.com/gofrs/flock v0.12.1 github.com/gofrs/flock v0.12.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510

4
go.sum
View File

@ -129,8 +129,8 @@ github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3T
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.0.0-rc.1+incompatible h1:xUbdsVxJIFvyZ+958MzyyIT7VuHO4Ecao9hKhl7kGUc= github.com/docker/docker v28.0.0-rc.2+incompatible h1:p+Ri+C0mmbPkhYVD9Sxnp/TnNnZoQWEj/EwOC465Uq4=
github.com/docker/docker v28.0.0-rc.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.0.0-rc.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=

View File

@ -7,7 +7,6 @@ import (
"github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types/image"
dockerclient "github.com/docker/docker/client" dockerclient "github.com/docker/docker/client"
) )
@ -53,7 +52,7 @@ func (c *Client) LoadImage(ctx context.Context, name string, status progress.Wri
w.mu.Unlock() w.mu.Unlock()
} }
resp, err := dapi.ImageLoad(ctx, pr, image.LoadOptions{}) resp, err := dapi.ImageLoad(ctx, pr)
defer close(done) defer close(done)
if err != nil { if err != nil {
handleErr(err) handleErr(err)

View File

@ -2,7 +2,9 @@
# This file lists all contributors to the repository. # This file lists all contributors to the repository.
# See hack/generate-authors.sh to make modifications. # See hack/generate-authors.sh to make modifications.
7sunarni <710720732@qq.com>
Aanand Prasad <aanand.prasad@gmail.com> Aanand Prasad <aanand.prasad@gmail.com>
Aarni Koskela <akx@iki.fi>
Aaron Davidson <aaron@databricks.com> Aaron Davidson <aaron@databricks.com>
Aaron Feng <aaron.feng@gmail.com> Aaron Feng <aaron.feng@gmail.com>
Aaron Hnatiw <aaron@griddio.com> Aaron Hnatiw <aaron@griddio.com>
@ -11,6 +13,7 @@ Aaron L. Xu <liker.xu@foxmail.com>
Aaron Lehmann <alehmann@netflix.com> Aaron Lehmann <alehmann@netflix.com>
Aaron Welch <welch@packet.net> Aaron Welch <welch@packet.net>
Aaron Yoshitake <airandfingers@gmail.com> Aaron Yoshitake <airandfingers@gmail.com>
Abdur Rehman <abdur_rehman@mentor.com>
Abel Muiño <amuino@gmail.com> Abel Muiño <amuino@gmail.com>
Abhijeet Kasurde <akasurde@redhat.com> Abhijeet Kasurde <akasurde@redhat.com>
Abhinandan Prativadi <aprativadi@gmail.com> Abhinandan Prativadi <aprativadi@gmail.com>
@ -24,9 +27,11 @@ Adam Avilla <aavilla@yp.com>
Adam Dobrawy <naczelnik@jawnosc.tk> Adam Dobrawy <naczelnik@jawnosc.tk>
Adam Eijdenberg <adam.eijdenberg@gmail.com> Adam Eijdenberg <adam.eijdenberg@gmail.com>
Adam Kunk <adam.kunk@tiaa-cref.org> Adam Kunk <adam.kunk@tiaa-cref.org>
Adam Lamers <adam.lamers@wmsdev.pl>
Adam Miller <admiller@redhat.com> Adam Miller <admiller@redhat.com>
Adam Mills <adam@armills.info> Adam Mills <adam@armills.info>
Adam Pointer <adam.pointer@skybettingandgaming.com> Adam Pointer <adam.pointer@skybettingandgaming.com>
Adam Simon <adamsimon85100@gmail.com>
Adam Singer <financeCoding@gmail.com> Adam Singer <financeCoding@gmail.com>
Adam Thornton <adam.thornton@maryville.com> Adam Thornton <adam.thornton@maryville.com>
Adam Walz <adam@adamwalz.net> Adam Walz <adam@adamwalz.net>
@ -119,6 +124,7 @@ amangoel <amangoel@gmail.com>
Amen Belayneh <amenbelayneh@gmail.com> Amen Belayneh <amenbelayneh@gmail.com>
Ameya Gawde <agawde@mirantis.com> Ameya Gawde <agawde@mirantis.com>
Amir Goldstein <amir73il@aquasec.com> Amir Goldstein <amir73il@aquasec.com>
AmirBuddy <badinlu.amirhossein@gmail.com>
Amit Bakshi <ambakshi@gmail.com> Amit Bakshi <ambakshi@gmail.com>
Amit Krishnan <amit.krishnan@oracle.com> Amit Krishnan <amit.krishnan@oracle.com>
Amit Shukla <amit.shukla@docker.com> Amit Shukla <amit.shukla@docker.com>
@ -168,6 +174,7 @@ Andrey Kolomentsev <andrey.kolomentsev@docker.com>
Andrey Petrov <andrey.petrov@shazow.net> Andrey Petrov <andrey.petrov@shazow.net>
Andrey Stolbovsky <andrey.stolbovsky@gmail.com> Andrey Stolbovsky <andrey.stolbovsky@gmail.com>
André Martins <aanm90@gmail.com> André Martins <aanm90@gmail.com>
Andrés Maldonado <maldonado@codelutin.com>
Andy Chambers <anchambers@paypal.com> Andy Chambers <anchambers@paypal.com>
andy diller <dillera@gmail.com> andy diller <dillera@gmail.com>
Andy Goldstein <agoldste@redhat.com> Andy Goldstein <agoldste@redhat.com>
@ -219,6 +226,7 @@ Artur Meyster <arthurfbi@yahoo.com>
Arun Gupta <arun.gupta@gmail.com> Arun Gupta <arun.gupta@gmail.com>
Asad Saeeduddin <masaeedu@gmail.com> Asad Saeeduddin <masaeedu@gmail.com>
Asbjørn Enge <asbjorn@hanafjedle.net> Asbjørn Enge <asbjorn@hanafjedle.net>
Ashly Mathew <ashly.mathew@sap.com>
Austin Vazquez <macedonv@amazon.com> Austin Vazquez <macedonv@amazon.com>
averagehuman <averagehuman@users.noreply.github.com> averagehuman <averagehuman@users.noreply.github.com>
Avi Das <andas222@gmail.com> Avi Das <andas222@gmail.com>
@ -345,6 +353,7 @@ Chance Zibolski <chance.zibolski@gmail.com>
Chander Govindarajan <chandergovind@gmail.com> Chander Govindarajan <chandergovind@gmail.com>
Chanhun Jeong <keyolk@gmail.com> Chanhun Jeong <keyolk@gmail.com>
Chao Wang <wangchao.fnst@cn.fujitsu.com> Chao Wang <wangchao.fnst@cn.fujitsu.com>
Charity Kathure <ckathure@microsoft.com>
Charles Chan <charleswhchan@users.noreply.github.com> Charles Chan <charleswhchan@users.noreply.github.com>
Charles Hooper <charles.hooper@dotcloud.com> Charles Hooper <charles.hooper@dotcloud.com>
Charles Law <claw@conduce.com> Charles Law <claw@conduce.com>
@ -480,6 +489,7 @@ Daniel Farrell <dfarrell@redhat.com>
Daniel Garcia <daniel@danielgarcia.info> Daniel Garcia <daniel@danielgarcia.info>
Daniel Gasienica <daniel@gasienica.ch> Daniel Gasienica <daniel@gasienica.ch>
Daniel Grunwell <mwgrunny@gmail.com> Daniel Grunwell <mwgrunny@gmail.com>
Daniel Guns <danbguns@gmail.com>
Daniel Helfand <helfand.4@gmail.com> Daniel Helfand <helfand.4@gmail.com>
Daniel Hiltgen <daniel.hiltgen@docker.com> Daniel Hiltgen <daniel.hiltgen@docker.com>
Daniel J Walsh <dwalsh@redhat.com> Daniel J Walsh <dwalsh@redhat.com>
@ -763,6 +773,7 @@ Frank Macreery <frank@macreery.com>
Frank Rosquin <frank.rosquin+github@gmail.com> Frank Rosquin <frank.rosquin+github@gmail.com>
Frank Villaro-Dixon <frank.villarodixon@merkle.com> Frank Villaro-Dixon <frank.villarodixon@merkle.com>
Frank Yang <yyb196@gmail.com> Frank Yang <yyb196@gmail.com>
François Scala <github@arcenik.net>
Fred Lifton <fred.lifton@docker.com> Fred Lifton <fred.lifton@docker.com>
Frederick F. Kautz IV <fkautz@redhat.com> Frederick F. Kautz IV <fkautz@redhat.com>
Frederico F. de Oliveira <FreddieOliveira@users.noreply.github.com> Frederico F. de Oliveira <FreddieOliveira@users.noreply.github.com>
@ -798,6 +809,7 @@ GennadySpb <lipenkov@gmail.com>
Geoff Levand <geoff@infradead.org> Geoff Levand <geoff@infradead.org>
Geoffrey Bachelet <grosfrais@gmail.com> Geoffrey Bachelet <grosfrais@gmail.com>
Geon Kim <geon0250@gmail.com> Geon Kim <geon0250@gmail.com>
George Adams <georgeadams1995@gmail.com>
George Kontridze <george@bugsnag.com> George Kontridze <george@bugsnag.com>
George Ma <mayangang@outlook.com> George Ma <mayangang@outlook.com>
George MacRorie <gmacr31@gmail.com> George MacRorie <gmacr31@gmail.com>
@ -826,6 +838,7 @@ Gopikannan Venugopalsamy <gopikannan.venugopalsamy@gmail.com>
Gosuke Miyashita <gosukenator@gmail.com> Gosuke Miyashita <gosukenator@gmail.com>
Gou Rao <gou@portworx.com> Gou Rao <gou@portworx.com>
Govinda Fichtner <govinda.fichtner@googlemail.com> Govinda Fichtner <govinda.fichtner@googlemail.com>
Grace Choi <grace.54109@gmail.com>
Grant Millar <rid@cylo.io> Grant Millar <rid@cylo.io>
Grant Reaber <grant.reaber@gmail.com> Grant Reaber <grant.reaber@gmail.com>
Graydon Hoare <graydon@pobox.com> Graydon Hoare <graydon@pobox.com>
@ -966,6 +979,7 @@ James Nugent <james@jen20.com>
James Sanders <james3sanders@gmail.com> James Sanders <james3sanders@gmail.com>
James Turnbull <james@lovedthanlost.net> James Turnbull <james@lovedthanlost.net>
James Watkins-Harvey <jwatkins@progi-media.com> James Watkins-Harvey <jwatkins@progi-media.com>
Jameson Hyde <jameson.hyde@docker.com>
Jamie Hannaford <jamie@limetree.org> Jamie Hannaford <jamie@limetree.org>
Jamshid Afshar <jafshar@yahoo.com> Jamshid Afshar <jafshar@yahoo.com>
Jan Breig <git@pygos.space> Jan Breig <git@pygos.space>
@ -1064,13 +1078,16 @@ Jim Perrin <jperrin@centos.org>
Jimmy Cuadra <jimmy@jimmycuadra.com> Jimmy Cuadra <jimmy@jimmycuadra.com>
Jimmy Puckett <jimmy.puckett@spinen.com> Jimmy Puckett <jimmy.puckett@spinen.com>
Jimmy Song <rootsongjc@gmail.com> Jimmy Song <rootsongjc@gmail.com>
jinjiadu <jinjiadu@aliyun.com>
Jinsoo Park <cellpjs@gmail.com> Jinsoo Park <cellpjs@gmail.com>
Jintao Zhang <zhangjintao9020@gmail.com> Jintao Zhang <zhangjintao9020@gmail.com>
Jiri Appl <jiria@microsoft.com> Jiri Appl <jiria@microsoft.com>
Jiri Popelka <jpopelka@redhat.com> Jiri Popelka <jpopelka@redhat.com>
Jiuyue Ma <majiuyue@huawei.com> Jiuyue Ma <majiuyue@huawei.com>
Jiří Župka <jzupka@redhat.com> Jiří Župka <jzupka@redhat.com>
jjimbo137 <115816493+jjimbo137@users.noreply.github.com>
Joakim Roubert <joakim.roubert@axis.com> Joakim Roubert <joakim.roubert@axis.com>
Joan Grau <grautxo.dev@proton.me>
Joao Fernandes <joao.fernandes@docker.com> Joao Fernandes <joao.fernandes@docker.com>
Joao Trindade <trindade.joao@gmail.com> Joao Trindade <trindade.joao@gmail.com>
Joe Beda <joe.github@bedafamily.com> Joe Beda <joe.github@bedafamily.com>
@ -1155,6 +1172,7 @@ Josiah Kiehl <jkiehl@riotgames.com>
José Tomás Albornoz <jojo@eljojo.net> José Tomás Albornoz <jojo@eljojo.net>
Joyce Jang <mail@joycejang.com> Joyce Jang <mail@joycejang.com>
JP <jpellerin@leapfrogonline.com> JP <jpellerin@leapfrogonline.com>
JSchltggr <jschltggr@gmail.com>
Julian Taylor <jtaylor.debian@googlemail.com> Julian Taylor <jtaylor.debian@googlemail.com>
Julien Barbier <write0@gmail.com> Julien Barbier <write0@gmail.com>
Julien Bisconti <veggiemonk@users.noreply.github.com> Julien Bisconti <veggiemonk@users.noreply.github.com>
@ -1289,6 +1307,7 @@ Laura Brehm <laurabrehm@hey.com>
Laura Frank <ljfrank@gmail.com> Laura Frank <ljfrank@gmail.com>
Laurent Bernaille <laurent.bernaille@datadoghq.com> Laurent Bernaille <laurent.bernaille@datadoghq.com>
Laurent Erignoux <lerignoux@gmail.com> Laurent Erignoux <lerignoux@gmail.com>
Laurent Goderre <laurent.goderre@docker.com>
Laurie Voss <github@seldo.com> Laurie Voss <github@seldo.com>
Leandro Motta Barros <lmb@stackedboxes.org> Leandro Motta Barros <lmb@stackedboxes.org>
Leandro Siqueira <leandro.siqueira@gmail.com> Leandro Siqueira <leandro.siqueira@gmail.com>
@ -1369,6 +1388,7 @@ Madhan Raj Mookkandy <MadhanRaj.Mookkandy@microsoft.com>
Madhav Puri <madhav.puri@gmail.com> Madhav Puri <madhav.puri@gmail.com>
Madhu Venugopal <mavenugo@gmail.com> Madhu Venugopal <mavenugo@gmail.com>
Mageee <fangpuyi@foxmail.com> Mageee <fangpuyi@foxmail.com>
maggie44 <64841595+maggie44@users.noreply.github.com>
Mahesh Tiyyagura <tmahesh@gmail.com> Mahesh Tiyyagura <tmahesh@gmail.com>
malnick <malnick@gmail..com> malnick <malnick@gmail..com>
Malte Janduda <mail@janduda.net> Malte Janduda <mail@janduda.net>
@ -1579,6 +1599,7 @@ Muayyad Alsadi <alsadi@gmail.com>
Muhammad Zohaib Aslam <zohaibse011@gmail.com> Muhammad Zohaib Aslam <zohaibse011@gmail.com>
Mustafa Akın <mustafa91@gmail.com> Mustafa Akın <mustafa91@gmail.com>
Muthukumar R <muthur@gmail.com> Muthukumar R <muthur@gmail.com>
Myeongjoon Kim <kimmj8409@gmail.com>
Máximo Cuadros <mcuadros@gmail.com> Máximo Cuadros <mcuadros@gmail.com>
Médi-Rémi Hashim <medimatrix@users.noreply.github.com> Médi-Rémi Hashim <medimatrix@users.noreply.github.com>
Nace Oroz <orkica@gmail.com> Nace Oroz <orkica@gmail.com>
@ -1593,6 +1614,7 @@ Natasha Jarus <linuxmercedes@gmail.com>
Nate Brennand <nate.brennand@clever.com> Nate Brennand <nate.brennand@clever.com>
Nate Eagleson <nate@nateeag.com> Nate Eagleson <nate@nateeag.com>
Nate Jones <nate@endot.org> Nate Jones <nate@endot.org>
Nathan Baulch <nathan.baulch@gmail.com>
Nathan Carlson <carl4403@umn.edu> Nathan Carlson <carl4403@umn.edu>
Nathan Herald <me@nathanherald.com> Nathan Herald <me@nathanherald.com>
Nathan Hsieh <hsieh.nathan@gmail.com> Nathan Hsieh <hsieh.nathan@gmail.com>
@ -1655,6 +1677,7 @@ Nuutti Kotivuori <naked@iki.fi>
nzwsch <hi@nzwsch.com> nzwsch <hi@nzwsch.com>
O.S. Tezer <ostezer@gmail.com> O.S. Tezer <ostezer@gmail.com>
objectified <objectified@gmail.com> objectified <objectified@gmail.com>
Octol1ttle <l1ttleofficial@outlook.com>
Odin Ugedal <odin@ugedal.com> Odin Ugedal <odin@ugedal.com>
Oguz Bilgic <fisyonet@gmail.com> Oguz Bilgic <fisyonet@gmail.com>
Oh Jinkyun <tintypemolly@gmail.com> Oh Jinkyun <tintypemolly@gmail.com>
@ -1763,6 +1786,7 @@ Pierre Carrier <pierre@meteor.com>
Pierre Dal-Pra <dalpra.pierre@gmail.com> Pierre Dal-Pra <dalpra.pierre@gmail.com>
Pierre Wacrenier <pierre.wacrenier@gmail.com> Pierre Wacrenier <pierre.wacrenier@gmail.com>
Pierre-Alain RIVIERE <pariviere@ippon.fr> Pierre-Alain RIVIERE <pariviere@ippon.fr>
pinglanlu <pinglanlu@outlook.com>
Piotr Bogdan <ppbogdan@gmail.com> Piotr Bogdan <ppbogdan@gmail.com>
Piotr Karbowski <piotr.karbowski@protonmail.ch> Piotr Karbowski <piotr.karbowski@protonmail.ch>
Porjo <porjo38@yahoo.com.au> Porjo <porjo38@yahoo.com.au>
@ -1790,6 +1814,7 @@ Quentin Tayssier <qtayssier@gmail.com>
r0n22 <cameron.regan@gmail.com> r0n22 <cameron.regan@gmail.com>
Rachit Sharma <rachitsharma613@gmail.com> Rachit Sharma <rachitsharma613@gmail.com>
Radostin Stoyanov <rstoyanov1@gmail.com> Radostin Stoyanov <rstoyanov1@gmail.com>
Rafael Fernández López <ereslibre@ereslibre.es>
Rafal Jeczalik <rjeczalik@gmail.com> Rafal Jeczalik <rjeczalik@gmail.com>
Rafe Colton <rafael.colton@gmail.com> Rafe Colton <rafael.colton@gmail.com>
Raghavendra K T <raghavendra.kt@linux.vnet.ibm.com> Raghavendra K T <raghavendra.kt@linux.vnet.ibm.com>
@ -1856,7 +1881,7 @@ Robin Speekenbrink <robin@kingsquare.nl>
Robin Thoni <robin@rthoni.com> Robin Thoni <robin@rthoni.com>
robpc <rpcann@gmail.com> robpc <rpcann@gmail.com>
Rodolfo Carvalho <rhcarvalho@gmail.com> Rodolfo Carvalho <rhcarvalho@gmail.com>
Rodrigo Campos <rodrigo@kinvolk.io> Rodrigo Campos <rodrigoca@microsoft.com>
Rodrigo Vaz <rodrigo.vaz@gmail.com> Rodrigo Vaz <rodrigo.vaz@gmail.com>
Roel Van Nyen <roel.vannyen@gmail.com> Roel Van Nyen <roel.vannyen@gmail.com>
Roger Peppe <rogpeppe@gmail.com> Roger Peppe <rogpeppe@gmail.com>
@ -1995,6 +2020,7 @@ Sevki Hasirci <s@sevki.org>
Shane Canon <scanon@lbl.gov> Shane Canon <scanon@lbl.gov>
Shane da Silva <shane@dasilva.io> Shane da Silva <shane@dasilva.io>
Shaun Kaasten <shaunk@gmail.com> Shaun Kaasten <shaunk@gmail.com>
Shaun Thompson <shaun.thompson@docker.com>
shaunol <shaunol@gmail.com> shaunol <shaunol@gmail.com>
Shawn Landden <shawn@churchofgit.com> Shawn Landden <shawn@churchofgit.com>
Shawn Siefkas <shawn.siefkas@meredith.com> Shawn Siefkas <shawn.siefkas@meredith.com>
@ -2013,6 +2039,7 @@ Shijun Qin <qinshijun16@mails.ucas.ac.cn>
Shishir Mahajan <shishir.mahajan@redhat.com> Shishir Mahajan <shishir.mahajan@redhat.com>
Shoubhik Bose <sbose78@gmail.com> Shoubhik Bose <sbose78@gmail.com>
Shourya Sarcar <shourya.sarcar@gmail.com> Shourya Sarcar <shourya.sarcar@gmail.com>
Shreenidhi Shedi <shreenidhi.shedi@broadcom.com>
Shu-Wai Chow <shu-wai.chow@seattlechildrens.org> Shu-Wai Chow <shu-wai.chow@seattlechildrens.org>
shuai-z <zs.broccoli@gmail.com> shuai-z <zs.broccoli@gmail.com>
Shukui Yang <yangshukui@huawei.com> Shukui Yang <yangshukui@huawei.com>
@ -2100,6 +2127,7 @@ Sébastien Stormacq <sebsto@users.noreply.github.com>
Sören Tempel <soeren+git@soeren-tempel.net> Sören Tempel <soeren+git@soeren-tempel.net>
Tabakhase <mail@tabakhase.com> Tabakhase <mail@tabakhase.com>
Tadej Janež <tadej.j@nez.si> Tadej Janež <tadej.j@nez.si>
Tadeusz Dudkiewicz <tadeusz.dudkiewicz@rtbhouse.com>
Takuto Sato <tockn.jp@gmail.com> Takuto Sato <tockn.jp@gmail.com>
tang0th <tang0th@gmx.com> tang0th <tang0th@gmx.com>
Tangi Colin <tangicolin@gmail.com> Tangi Colin <tangicolin@gmail.com>
@ -2107,6 +2135,7 @@ Tatsuki Sugiura <sugi@nemui.org>
Tatsushi Inagaki <e29253@jp.ibm.com> Tatsushi Inagaki <e29253@jp.ibm.com>
Taylan Isikdemir <taylani@google.com> Taylan Isikdemir <taylani@google.com>
Taylor Jones <monitorjbl@gmail.com> Taylor Jones <monitorjbl@gmail.com>
tcpdumppy <847462026@qq.com>
Ted M. Young <tedyoung@gmail.com> Ted M. Young <tedyoung@gmail.com>
Tehmasp Chaudhri <tehmasp@gmail.com> Tehmasp Chaudhri <tehmasp@gmail.com>
Tejaswini Duggaraju <naduggar@microsoft.com> Tejaswini Duggaraju <naduggar@microsoft.com>
@ -2391,6 +2420,7 @@ You-Sheng Yang (楊有勝) <vicamo@gmail.com>
youcai <omegacoleman@gmail.com> youcai <omegacoleman@gmail.com>
Youcef YEKHLEF <yyekhlef@gmail.com> Youcef YEKHLEF <yyekhlef@gmail.com>
Youfu Zhang <zhangyoufu@gmail.com> Youfu Zhang <zhangyoufu@gmail.com>
YR Chen <stevapple@icloud.com>
Yu Changchun <yuchangchun1@huawei.com> Yu Changchun <yuchangchun1@huawei.com>
Yu Chengxia <yuchengxia@huawei.com> Yu Chengxia <yuchengxia@huawei.com>
Yu Peng <yu.peng36@zte.com.cn> Yu Peng <yu.peng36@zte.com.cn>

View File

@ -2754,12 +2754,24 @@ definitions:
type: "string" type: "string"
error: error:
type: "string" type: "string"
x-nullable: true
description: |-
errors encountered during the operation.
> **Deprecated**: This field is deprecated since API v1.4, and will be omitted in a future API version. Use the information in errorDetail instead.
errorDetail: errorDetail:
$ref: "#/definitions/ErrorDetail" $ref: "#/definitions/ErrorDetail"
status: status:
type: "string" type: "string"
progress: progress:
type: "string" type: "string"
x-nullable: true
description: |-
Progress is a pre-formatted presentation of progressDetail.
> **Deprecated**: This field is deprecated since API v1.8, and will be omitted in a future API version. Use the information in progressDetail instead.
progressDetail: progressDetail:
$ref: "#/definitions/ProgressDetail" $ref: "#/definitions/ProgressDetail"
aux: aux:
@ -2859,12 +2871,24 @@ definitions:
type: "string" type: "string"
error: error:
type: "string" type: "string"
x-nullable: true
description: |-
errors encountered during the operation.
> **Deprecated**: This field is deprecated since API v1.4, and will be omitted in a future API version. Use the information in errorDetail instead.
errorDetail: errorDetail:
$ref: "#/definitions/ErrorDetail" $ref: "#/definitions/ErrorDetail"
status: status:
type: "string" type: "string"
progress: progress:
type: "string" type: "string"
x-nullable: true
description: |-
Progress is a pre-formatted presentation of progressDetail.
> **Deprecated**: This field is deprecated since API v1.8, and will be omitted in a future API version. Use the information in progressDetail instead.
progressDetail: progressDetail:
$ref: "#/definitions/ProgressDetail" $ref: "#/definitions/ProgressDetail"
@ -2873,10 +2897,24 @@ definitions:
properties: properties:
error: error:
type: "string" type: "string"
x-nullable: true
description: |-
errors encountered during the operation.
> **Deprecated**: This field is deprecated since API v1.4, and will be omitted in a future API version. Use the information in errorDetail instead.
errorDetail:
$ref: "#/definitions/ErrorDetail"
status: status:
type: "string" type: "string"
progress: progress:
type: "string" type: "string"
x-nullable: true
description: |-
Progress is a pre-formatted presentation of progressDetail.
> **Deprecated**: This field is deprecated since API v1.8, and will be omitted in a future API version. Use the information in progressDetail instead.
progressDetail: progressDetail:
$ref: "#/definitions/ProgressDetail" $ref: "#/definitions/ProgressDetail"
@ -2908,9 +2946,10 @@ definitions:
example: example:
message: "Something went wrong." message: "Something went wrong."
IdResponse: IDResponse:
description: "Response to an API call that returns just an Id" description: "Response to an API call that returns just an Id"
type: "object" type: "object"
x-go-name: "IDResponse"
required: ["Id"] required: ["Id"]
properties: properties:
Id: Id:
@ -5070,6 +5109,17 @@ definitions:
ImageID: ImageID:
description: "The ID of the image that this container was created from" description: "The ID of the image that this container was created from"
type: "string" type: "string"
ImageManifestDescriptor:
$ref: "#/definitions/OCIDescriptor"
x-nullable: true
description: |
OCI descriptor of the platform-specific manifest of the image
the container was created from.
Note: Only available if the daemon provides a multi-platform
image store.
This field is not populated in the `GET /system/df` endpoint.
Command: Command:
description: "Command to run when starting the container" description: "Command to run when starting the container"
type: "string" type: "string"
@ -5326,6 +5376,21 @@ definitions:
type: "string" type: "string"
example: [] example: []
ContainerUpdateResponse:
type: "object"
title: "ContainerUpdateResponse"
x-go-name: "UpdateResponse"
description: |-
Response for a successful container-update.
properties:
Warnings:
type: "array"
description: |-
Warnings encountered when updating the container.
items:
type: "string"
example: ["Published ports are discarded when using host network mode"]
ContainerStatsResponse: ContainerStatsResponse:
description: | description: |
Statistics sample for a container. Statistics sample for a container.
@ -5871,6 +5936,58 @@ definitions:
x-nullable: true x-nullable: true
example: 7593984 example: 7593984
ContainerTopResponse:
type: "object"
x-go-name: "TopResponse"
title: "ContainerTopResponse"
description: |-
Container "top" response.
properties:
Titles:
description: "The ps column titles"
type: "array"
items:
type: "string"
example:
Titles:
- "UID"
- "PID"
- "PPID"
- "C"
- "STIME"
- "TTY"
- "TIME"
- "CMD"
Processes:
description: |-
Each process running in the container, where each process
is an array of values corresponding to the titles.
type: "array"
items:
type: "array"
items:
type: "string"
example:
Processes:
-
- "root"
- "13642"
- "882"
- "0"
- "17:03"
- "pts/0"
- "00:00:00"
- "/bin/bash"
-
- "root"
- "13735"
- "13642"
- "0"
- "17:06"
- "pts/0"
- "00:00:00"
- "sleep 10"
ContainerWaitResponse: ContainerWaitResponse:
description: "OK response to ContainerWait operation" description: "OK response to ContainerWait operation"
type: "object" type: "object"
@ -8072,54 +8189,7 @@ paths:
200: 200:
description: "no error" description: "no error"
schema: schema:
type: "object" $ref: "#/definitions/ContainerTopResponse"
title: "ContainerTopResponse"
description: "OK response to ContainerTop operation"
properties:
Titles:
description: "The ps column titles"
type: "array"
items:
type: "string"
Processes:
description: |
Each process running in the container, where each is process
is an array of values corresponding to the titles.
type: "array"
items:
type: "array"
items:
type: "string"
examples:
application/json:
Titles:
- "UID"
- "PID"
- "PPID"
- "C"
- "STIME"
- "TTY"
- "TIME"
- "CMD"
Processes:
-
- "root"
- "13642"
- "882"
- "0"
- "17:03"
- "pts/0"
- "00:00:00"
- "/bin/bash"
-
- "root"
- "13735"
- "13642"
- "0"
- "17:06"
- "pts/0"
- "00:00:00"
- "sleep 10"
404: 404:
description: "no such container" description: "no such container"
schema: schema:
@ -8560,14 +8630,7 @@ paths:
200: 200:
description: "The container has been updated." description: "The container has been updated."
schema: schema:
type: "object" $ref: "#/definitions/ContainerUpdateResponse"
title: "ContainerUpdateResponse"
description: "OK response to ContainerUpdate operation"
properties:
Warnings:
type: "array"
items:
type: "string"
404: 404:
description: "no such container" description: "no such container"
schema: schema:
@ -10220,7 +10283,7 @@ paths:
201: 201:
description: "no error" description: "no error"
schema: schema:
$ref: "#/definitions/IdResponse" $ref: "#/definitions/IDResponse"
404: 404:
description: "no such container" description: "no such container"
schema: schema:
@ -10614,7 +10677,7 @@ paths:
201: 201:
description: "no error" description: "no error"
schema: schema:
$ref: "#/definitions/IdResponse" $ref: "#/definitions/IDResponse"
404: 404:
description: "no such container" description: "no such container"
schema: schema:
@ -13094,7 +13157,7 @@ paths:
201: 201:
description: "no error" description: "no error"
schema: schema:
$ref: "#/definitions/IdResponse" $ref: "#/definitions/IDResponse"
409: 409:
description: "name conflicts with an existing object" description: "name conflicts with an existing object"
schema: schema:
@ -13301,7 +13364,7 @@ paths:
201: 201:
description: "no error" description: "no error"
schema: schema:
$ref: "#/definitions/IdResponse" $ref: "#/definitions/IDResponse"
409: 409:
description: "name conflicts with an existing object" description: "name conflicts with an existing object"
schema: schema:

View File

@ -1,10 +1,10 @@
package types package common
// This file was generated by the swagger tool. // This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command // Editing this file might prove futile when you re-run the swagger generate command
// IDResponse Response to an API call that returns just an Id // IDResponse Response to an API call that returns just an Id
// swagger:model IdResponse // swagger:model IDResponse
type IDResponse struct { type IDResponse struct {
// The id of the newly created object. // The id of the newly created object.

View File

@ -0,0 +1,7 @@
package container
import "github.com/docker/docker/api/types/common"
// CommitResponse response for the commit API call, containing the ID of the
// image that was produced.
type CommitResponse = common.IDResponse

View File

@ -10,6 +10,16 @@ import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
// ContainerUpdateOKBody OK response to ContainerUpdate operation
//
// Deprecated: use [UpdateResponse]. This alias will be removed in the next release.
type ContainerUpdateOKBody = UpdateResponse
// ContainerTopOKBody OK response to ContainerTop operation
//
// Deprecated: use [TopResponse]. This alias will be removed in the next release.
type ContainerTopOKBody = TopResponse
// PruneReport contains the response for Engine API: // PruneReport contains the response for Engine API:
// POST "/containers/prune" // POST "/containers/prune"
type PruneReport struct { type PruneReport struct {
@ -111,19 +121,20 @@ type State struct {
// Summary contains response of Engine API: // Summary contains response of Engine API:
// GET "/containers/json" // GET "/containers/json"
type Summary struct { type Summary struct {
ID string `json:"Id"` ID string `json:"Id"`
Names []string Names []string
Image string Image string
ImageID string ImageID string
Command string ImageManifestDescriptor *ocispec.Descriptor `json:"ImageManifestDescriptor,omitempty"`
Created int64 Command string
Ports []Port Created int64
SizeRw int64 `json:",omitempty"` Ports []Port
SizeRootFs int64 `json:",omitempty"` SizeRw int64 `json:",omitempty"`
Labels map[string]string SizeRootFs int64 `json:",omitempty"`
State string Labels map[string]string
Status string State string
HostConfig struct { Status string
HostConfig struct {
NetworkMode string `json:",omitempty"` NetworkMode string `json:",omitempty"`
Annotations map[string]string `json:",omitempty"` Annotations map[string]string `json:",omitempty"`
} }
@ -173,5 +184,5 @@ type InspectResponse struct {
Config *Config Config *Config
NetworkSettings *NetworkSettings NetworkSettings *NetworkSettings
// ImageManifestDescriptor is the descriptor of a platform-specific manifest of the image used to create the container. // ImageManifestDescriptor is the descriptor of a platform-specific manifest of the image used to create the container.
ImageManifestDescriptor *ocispec.Descriptor `json:",omitempty"` ImageManifestDescriptor *ocispec.Descriptor `json:"ImageManifestDescriptor,omitempty"`
} }

View File

@ -1,22 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerTopOKBody OK response to ContainerTop operation
// swagger:model ContainerTopOKBody
type ContainerTopOKBody struct {
// Each process running in the container, where each is process
// is an array of values corresponding to the titles.
//
// Required: true
Processes [][]string `json:"Processes"`
// The ps column titles
// Required: true
Titles []string `json:"Titles"`
}

View File

@ -1,16 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerUpdateOKBody OK response to ContainerUpdate operation
// swagger:model ContainerUpdateOKBody
type ContainerUpdateOKBody struct {
// warnings
// Required: true
Warnings []string `json:"Warnings"`
}

View File

@ -1,5 +1,13 @@
package container package container
import "github.com/docker/docker/api/types/common"
// ExecCreateResponse is the response for a successful exec-create request.
// It holds the ID of the exec that was created.
//
// TODO(thaJeztah): make this a distinct type.
type ExecCreateResponse = common.IDResponse
// ExecOptions is a small subset of the Config struct that holds the configuration // ExecOptions is a small subset of the Config struct that holds the configuration
// for the exec feature of docker. // for the exec feature of docker.
type ExecOptions struct { type ExecOptions struct {

View File

@ -0,0 +1,18 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// TopResponse ContainerTopResponse
//
// Container "top" response.
// swagger:model TopResponse
type TopResponse struct {
// Each process running in the container, where each process
// is an array of values corresponding to the titles.
Processes [][]string `json:"Processes"`
// The ps column titles
Titles []string `json:"Titles"`
}

View File

@ -0,0 +1,14 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// UpdateResponse ContainerUpdateResponse
//
// Response for a successful container-update.
// swagger:model UpdateResponse
type UpdateResponse struct {
// Warnings encountered when updating the container.
Warnings []string `json:"Warnings"`
}

View File

@ -3,11 +3,17 @@ package types
import ( import (
"context" "context"
"github.com/docker/docker/api/types/common"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/storage" "github.com/docker/docker/api/types/storage"
) )
// IDResponse Response to an API call that returns just an Id.
//
// Deprecated: use either [container.CommitResponse] or [container.ExecCreateResponse]. It will be removed in the next release.
type IDResponse = common.IDResponse
// ContainerJSONBase contains response of Engine API GET "/containers/{name:.*}/json" // ContainerJSONBase contains response of Engine API GET "/containers/{name:.*}/json"
// for API version 1.18 and older. // for API version 1.18 and older.
// //

View File

@ -10,7 +10,7 @@ func (cli *Client) BuildCancel(ctx context.Context, id string) error {
query := url.Values{} query := url.Values{}
query.Set("id", id) query.Set("id", id)
serverResp, err := cli.post(ctx, "/build/cancel", query, nil, nil) resp, err := cli.post(ctx, "/build/cancel", query, nil, nil)
ensureReaderClosed(serverResp) ensureReaderClosed(resp)
return err return err
} }

View File

@ -40,15 +40,15 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru
} }
query.Set("filters", f) query.Set("filters", f)
serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil) resp, err := cli.post(ctx, "/build/prune", query, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
report := types.BuildCachePruneReport{} report := types.BuildCachePruneReport{}
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(resp.Body).Decode(&report); err != nil {
return nil, errors.Wrap(err, "error retrieving disk usage") return nil, errors.Wrap(err, "error retrieving disk usage")
} }

View File

@ -23,6 +23,6 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
return checkpoints, err return checkpoints, err
} }
err = json.NewDecoder(resp.body).Decode(&checkpoints) err = json.NewDecoder(resp.Body).Decode(&checkpoints)
return checkpoints, err return checkpoints, err
} }

View File

@ -59,7 +59,6 @@ import (
"github.com/docker/go-connections/sockets" "github.com/docker/go-connections/sockets"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/trace"
) )
// DummyHost is a hostname used for local communication. // DummyHost is a hostname used for local communication.
@ -141,7 +140,7 @@ type Client struct {
// negotiateLock is used to single-flight the version negotiation process // negotiateLock is used to single-flight the version negotiation process
negotiateLock sync.Mutex negotiateLock sync.Mutex
tp trace.TracerProvider traceOpts []otelhttp.Option
// When the client transport is an *http.Transport (default) we need to do some extra things (like closing idle connections). // When the client transport is an *http.Transport (default) we need to do some extra things (like closing idle connections).
// Store the original transport as the http.Client transport will be wrapped with tracing libs. // Store the original transport as the http.Client transport will be wrapped with tracing libs.
@ -203,6 +202,12 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
client: client, client: client,
proto: hostURL.Scheme, proto: hostURL.Scheme,
addr: hostURL.Host, addr: hostURL.Host,
traceOpts: []otelhttp.Option{
otelhttp.WithSpanNameFormatter(func(_ string, req *http.Request) string {
return req.Method + " " + req.URL.Path
}),
},
} }
for _, op := range ops { for _, op := range ops {
@ -230,13 +235,7 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
} }
} }
c.client.Transport = otelhttp.NewTransport( c.client.Transport = otelhttp.NewTransport(c.client.Transport, c.traceOpts...)
c.client.Transport,
otelhttp.WithTracerProvider(c.tp),
otelhttp.WithSpanNameFormatter(func(_ string, req *http.Request) string {
return req.Method + " " + req.URL.Path
}),
)
return c, nil return c, nil
} }

View File

@ -69,11 +69,11 @@ type HijackDialer interface {
// ContainerAPIClient defines API client methods for the containers // ContainerAPIClient defines API client methods for the containers
type ContainerAPIClient interface { type ContainerAPIClient interface {
ContainerAttach(ctx context.Context, container string, options container.AttachOptions) (types.HijackedResponse, error) ContainerAttach(ctx context.Context, container string, options container.AttachOptions) (types.HijackedResponse, error)
ContainerCommit(ctx context.Context, container string, options container.CommitOptions) (types.IDResponse, error) ContainerCommit(ctx context.Context, container string, options container.CommitOptions) (container.CommitResponse, error)
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error) ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (types.HijackedResponse, error) ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (types.HijackedResponse, error)
ContainerExecCreate(ctx context.Context, container string, options container.ExecOptions) (types.IDResponse, error) ContainerExecCreate(ctx context.Context, container string, options container.ExecOptions) (container.ExecCreateResponse, error)
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error) ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
ContainerExecResize(ctx context.Context, execID string, options container.ResizeOptions) error ContainerExecResize(ctx context.Context, execID string, options container.ResizeOptions) error
ContainerExecStart(ctx context.Context, execID string, options container.ExecStartOptions) error ContainerExecStart(ctx context.Context, execID string, options container.ExecStartOptions) error
@ -93,9 +93,9 @@ type ContainerAPIClient interface {
ContainerStatsOneShot(ctx context.Context, container string) (container.StatsResponseReader, error) ContainerStatsOneShot(ctx context.Context, container string) (container.StatsResponseReader, error)
ContainerStart(ctx context.Context, container string, options container.StartOptions) error ContainerStart(ctx context.Context, container string, options container.StartOptions) error
ContainerStop(ctx context.Context, container string, options container.StopOptions) error ContainerStop(ctx context.Context, container string, options container.StopOptions) error
ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error) ContainerTop(ctx context.Context, container string, arguments []string) (container.TopResponse, error)
ContainerUnpause(ctx context.Context, container string) error ContainerUnpause(ctx context.Context, container string) error
ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.UpdateResponse, error)
ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error) ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error)
CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, container.PathStat, error) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, container.PathStat, error)
CopyToContainer(ctx context.Context, container, path string, content io.Reader, options container.CopyToContainerOptions) error CopyToContainer(ctx context.Context, container, path string, content io.Reader, options container.CopyToContainerOptions) error
@ -113,21 +113,30 @@ type ImageAPIClient interface {
BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error)
BuildCancel(ctx context.Context, id string) error BuildCancel(ctx context.Context, id string) error
ImageCreate(ctx context.Context, parentReference string, options image.CreateOptions) (io.ReadCloser, error) ImageCreate(ctx context.Context, parentReference string, options image.CreateOptions) (io.ReadCloser, error)
ImageHistory(ctx context.Context, image string, opts image.HistoryOptions) ([]image.HistoryResponseItem, error)
ImageImport(ctx context.Context, source image.ImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) ImageImport(ctx context.Context, source image.ImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error)
// Deprecated: Use [Client.ImageInspect] instead.
// Raw response can be obtained by [ImageInspectWithRawResponse] option.
ImageInspectWithRaw(ctx context.Context, image string) (image.InspectResponse, []byte, error)
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (image.InspectResponse, error)
ImageList(ctx context.Context, options image.ListOptions) ([]image.Summary, error) ImageList(ctx context.Context, options image.ListOptions) ([]image.Summary, error)
ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error)
ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error)
ImagePush(ctx context.Context, ref string, options image.PushOptions) (io.ReadCloser, error) ImagePush(ctx context.Context, ref string, options image.PushOptions) (io.ReadCloser, error)
ImageRemove(ctx context.Context, image string, options image.RemoveOptions) ([]image.DeleteResponse, error) ImageRemove(ctx context.Context, image string, options image.RemoveOptions) ([]image.DeleteResponse, error)
ImageSave(ctx context.Context, images []string, opts image.SaveOptions) (io.ReadCloser, error)
ImageSearch(ctx context.Context, term string, options registry.SearchOptions) ([]registry.SearchResult, error) ImageSearch(ctx context.Context, term string, options registry.SearchOptions) ([]registry.SearchResult, error)
ImageTag(ctx context.Context, image, ref string) error ImageTag(ctx context.Context, image, ref string) error
ImagesPrune(ctx context.Context, pruneFilter filters.Args) (image.PruneReport, error) ImagesPrune(ctx context.Context, pruneFilter filters.Args) (image.PruneReport, error)
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (image.InspectResponse, error)
ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) ([]image.HistoryResponseItem, error)
ImageLoad(ctx context.Context, input io.Reader, _ ...ImageLoadOption) (image.LoadResponse, error)
ImageSave(ctx context.Context, images []string, _ ...ImageSaveOption) (io.ReadCloser, error)
ImageAPIClientDeprecated
}
// ImageAPIClientDeprecated defines deprecated methods of the ImageAPIClient.
type ImageAPIClientDeprecated interface {
// ImageInspectWithRaw returns the image information and its raw representation.
//
// Deprecated: Use [Client.ImageInspect] instead. Raw response can be obtained using the [ImageInspectWithRawResponse] option.
ImageInspectWithRaw(ctx context.Context, image string) (image.InspectResponse, []byte, error)
} }
// NetworkAPIClient defines API client methods for the networks // NetworkAPIClient defines API client methods for the networks

View File

@ -20,6 +20,6 @@ func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (t
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -24,7 +24,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
return swarm.Config{}, nil, err return swarm.Config{}, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return swarm.Config{}, nil, err return swarm.Config{}, nil, err
} }

View File

@ -33,6 +33,6 @@ func (cli *Client) ConfigList(ctx context.Context, options types.ConfigListOptio
} }
var configs []swarm.Config var configs []swarm.Config
err = json.NewDecoder(resp.body).Decode(&configs) err = json.NewDecoder(resp.Body).Decode(&configs)
return configs, err return configs, err
} }

View File

@ -7,26 +7,25 @@ import (
"net/url" "net/url"
"github.com/distribution/reference" "github.com/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
) )
// ContainerCommit applies changes to a container and creates a new tagged image. // ContainerCommit applies changes to a container and creates a new tagged image.
func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options container.CommitOptions) (types.IDResponse, error) { func (cli *Client) ContainerCommit(ctx context.Context, containerID string, options container.CommitOptions) (container.CommitResponse, error) {
containerID, err := trimID("container", containerID) containerID, err := trimID("container", containerID)
if err != nil { if err != nil {
return types.IDResponse{}, err return container.CommitResponse{}, err
} }
var repository, tag string var repository, tag string
if options.Reference != "" { if options.Reference != "" {
ref, err := reference.ParseNormalizedNamed(options.Reference) ref, err := reference.ParseNormalizedNamed(options.Reference)
if err != nil { if err != nil {
return types.IDResponse{}, err return container.CommitResponse{}, err
} }
if _, isCanonical := ref.(reference.Canonical); isCanonical { if _, isCanonical := ref.(reference.Canonical); isCanonical {
return types.IDResponse{}, errors.New("refusing to create a tag with a digest reference") return container.CommitResponse{}, errors.New("refusing to create a tag with a digest reference")
} }
ref = reference.TagNameOnly(ref) ref = reference.TagNameOnly(ref)
@ -49,13 +48,13 @@ func (cli *Client) ContainerCommit(ctx context.Context, containerID string, opti
query.Set("pause", "0") query.Set("pause", "0")
} }
var response types.IDResponse var response container.CommitResponse
resp, err := cli.post(ctx, "/commit", query, options.Config, nil) resp, err := cli.post(ctx, "/commit", query, options.Config, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -24,12 +24,12 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
query := url.Values{} query := url.Values{}
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API. query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
response, err := cli.head(ctx, "/containers/"+containerID+"/archive", query, nil) resp, err := cli.head(ctx, "/containers/"+containerID+"/archive", query, nil)
defer ensureReaderClosed(response) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return container.PathStat{}, err return container.PathStat{}, err
} }
return getContainerPathStatFromHeader(response.header) return getContainerPathStatFromHeader(resp.Header)
} }
// CopyToContainer copies content into the container filesystem. // CopyToContainer copies content into the container filesystem.
@ -71,7 +71,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
query := make(url.Values, 1) query := make(url.Values, 1)
query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API. query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
response, err := cli.get(ctx, "/containers/"+containerID+"/archive", query, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/archive", query, nil)
if err != nil { if err != nil {
return nil, container.PathStat{}, err return nil, container.PathStat{}, err
} }
@ -82,11 +82,11 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
// copy it locally. Along with the stat info about the local destination, // copy it locally. Along with the stat info about the local destination,
// we have everything we need to handle the multiple possibilities there // we have everything we need to handle the multiple possibilities there
// can be when copying a file/dir from one location to another file/dir. // can be when copying a file/dir from one location to another file/dir.
stat, err := getContainerPathStatFromHeader(response.header) stat, err := getContainerPathStatFromHeader(resp.Header)
if err != nil { if err != nil {
return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err) return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err)
} }
return response.body, stat, err return resp.Body, stat, err
} }
func getContainerPathStatFromHeader(header http.Header) (container.PathStat, error) { func getContainerPathStatFromHeader(header http.Header) (container.PathStat, error) {

View File

@ -79,13 +79,13 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
NetworkingConfig: networkingConfig, NetworkingConfig: networkingConfig,
} }
serverResp, err := cli.post(ctx, "/containers/create", query, body, nil) resp, err := cli.post(ctx, "/containers/create", query, body, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return response, err
} }
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -15,14 +15,14 @@ func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]con
return nil, err return nil, err
} }
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var changes []container.FilesystemChange var changes []container.FilesystemChange
err = json.NewDecoder(serverResp.body).Decode(&changes) err = json.NewDecoder(resp.Body).Decode(&changes)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -11,10 +11,10 @@ import (
) )
// ContainerExecCreate creates a new exec configuration to run an exec process. // ContainerExecCreate creates a new exec configuration to run an exec process.
func (cli *Client) ContainerExecCreate(ctx context.Context, containerID string, options container.ExecOptions) (types.IDResponse, error) { func (cli *Client) ContainerExecCreate(ctx context.Context, containerID string, options container.ExecOptions) (container.ExecCreateResponse, error) {
containerID, err := trimID("container", containerID) containerID, err := trimID("container", containerID)
if err != nil { if err != nil {
return types.IDResponse{}, err return container.ExecCreateResponse{}, err
} }
// Make sure we negotiated (if the client is configured to do so), // Make sure we negotiated (if the client is configured to do so),
@ -23,11 +23,11 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, containerID string,
// Normally, version-negotiation (if enabled) would not happen until // Normally, version-negotiation (if enabled) would not happen until
// the API request is made. // the API request is made.
if err := cli.checkVersion(ctx); err != nil { if err := cli.checkVersion(ctx); err != nil {
return types.IDResponse{}, err return container.ExecCreateResponse{}, err
} }
if err := cli.NewVersionError(ctx, "1.25", "env"); len(options.Env) != 0 && err != nil { if err := cli.NewVersionError(ctx, "1.25", "env"); len(options.Env) != 0 && err != nil {
return types.IDResponse{}, err return container.ExecCreateResponse{}, err
} }
if versions.LessThan(cli.ClientVersion(), "1.42") { if versions.LessThan(cli.ClientVersion(), "1.42") {
options.ConsoleSize = nil options.ConsoleSize = nil
@ -36,11 +36,11 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, containerID string,
resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, options, nil) resp, err := cli.post(ctx, "/containers/"+containerID+"/exec", nil, options, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return types.IDResponse{}, err return container.ExecCreateResponse{}, err
} }
var response types.IDResponse var response container.ExecCreateResponse
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }
@ -75,7 +75,7 @@ func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (con
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
ensureReaderClosed(resp) ensureReaderClosed(resp)
return response, err return response, err
} }

View File

@ -15,10 +15,10 @@ func (cli *Client) ContainerExport(ctx context.Context, containerID string) (io.
return nil, err return nil, err
} }
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/export", url.Values{}, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/export", url.Values{}, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return serverResp.body, nil return resp.Body, nil
} }

View File

@ -17,14 +17,14 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (co
return container.InspectResponse{}, err return container.InspectResponse{}, err
} }
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return container.InspectResponse{}, err return container.InspectResponse{}, err
} }
var response container.InspectResponse var response container.InspectResponse
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }
@ -39,13 +39,13 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
if getSize { if getSize {
query.Set("size", "1") query.Set("size", "1")
} }
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return container.InspectResponse{}, nil, err return container.InspectResponse{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return container.InspectResponse{}, nil, err return container.InspectResponse{}, nil, err
} }

View File

@ -51,6 +51,6 @@ func (cli *Client) ContainerList(ctx context.Context, options container.ListOpti
} }
var containers []container.Summary var containers []container.Summary
err = json.NewDecoder(resp.body).Decode(&containers) err = json.NewDecoder(resp.Body).Decode(&containers)
return containers, err return containers, err
} }

View File

@ -81,5 +81,5 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }

View File

@ -20,14 +20,14 @@ func (cli *Client) ContainersPrune(ctx context.Context, pruneFilters filters.Arg
return container.PruneReport{}, err return container.PruneReport{}, err
} }
serverResp, err := cli.post(ctx, "/containers/prune", query, nil, nil) resp, err := cli.post(ctx, "/containers/prune", query, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return container.PruneReport{}, err return container.PruneReport{}, err
} }
var report container.PruneReport var report container.PruneReport
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(resp.Body).Decode(&report); err != nil {
return container.PruneReport{}, fmt.Errorf("Error retrieving disk usage: %v", err) return container.PruneReport{}, fmt.Errorf("Error retrieving disk usage: %v", err)
} }

View File

@ -27,8 +27,8 @@ func (cli *Client) ContainerStats(ctx context.Context, containerID string, strea
} }
return container.StatsResponseReader{ return container.StatsResponseReader{
Body: resp.body, Body: resp.Body,
OSType: getDockerOS(resp.header.Get("Server")), OSType: getDockerOS(resp.Header.Get("Server")),
}, nil }, nil
} }
@ -50,7 +50,7 @@ func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string
} }
return container.StatsResponseReader{ return container.StatsResponseReader{
Body: resp.body, Body: resp.Body,
OSType: getDockerOS(resp.header.Get("Server")), OSType: getDockerOS(resp.Header.Get("Server")),
}, nil }, nil
} }

View File

@ -10,10 +10,10 @@ import (
) )
// ContainerTop shows process information from within a container. // ContainerTop shows process information from within a container.
func (cli *Client) ContainerTop(ctx context.Context, containerID string, arguments []string) (container.ContainerTopOKBody, error) { func (cli *Client) ContainerTop(ctx context.Context, containerID string, arguments []string) (container.TopResponse, error) {
containerID, err := trimID("container", containerID) containerID, err := trimID("container", containerID)
if err != nil { if err != nil {
return container.ContainerTopOKBody{}, err return container.TopResponse{}, err
} }
query := url.Values{} query := url.Values{}
@ -24,10 +24,10 @@ func (cli *Client) ContainerTop(ctx context.Context, containerID string, argumen
resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return container.ContainerTopOKBody{}, err return container.TopResponse{}, err
} }
var response container.ContainerTopOKBody var response container.TopResponse
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -8,19 +8,19 @@ import (
) )
// ContainerUpdate updates the resources of a container. // ContainerUpdate updates the resources of a container.
func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) { func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.UpdateResponse, error) {
containerID, err := trimID("container", containerID) containerID, err := trimID("container", containerID)
if err != nil { if err != nil {
return container.ContainerUpdateOKBody{}, err return container.UpdateResponse{}, err
} }
serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil) resp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return container.ContainerUpdateOKBody{}, err return container.UpdateResponse{}, err
} }
var response container.ContainerUpdateOKBody var response container.UpdateResponse
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -67,9 +67,8 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit
go func() { go func() {
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
body := resp.body
responseText := bytes.NewBuffer(nil) responseText := bytes.NewBuffer(nil)
stream := io.TeeReader(body, responseText) stream := io.TeeReader(resp.Body, responseText)
var res container.WaitResponse var res container.WaitResponse
if err := json.NewDecoder(stream).Decode(&res); err != nil { if err := json.NewDecoder(stream).Decode(&res); err != nil {
@ -111,7 +110,7 @@ func (cli *Client) legacyContainerWait(ctx context.Context, containerID string)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
var res container.WaitResponse var res container.WaitResponse
if err := json.NewDecoder(resp.body).Decode(&res); err != nil { if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
errC <- err errC <- err
return return
} }

View File

@ -19,14 +19,14 @@ func (cli *Client) DiskUsage(ctx context.Context, options types.DiskUsageOptions
} }
} }
serverResp, err := cli.get(ctx, "/system/df", query, nil) resp, err := cli.get(ctx, "/system/df", query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return types.DiskUsage{}, err return types.DiskUsage{}, err
} }
var du types.DiskUsage var du types.DiskUsage
if err := json.NewDecoder(serverResp.body).Decode(&du); err != nil { if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
return types.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err) return types.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err)
} }
return du, nil return du, nil

View File

@ -11,14 +11,12 @@ import (
// DistributionInspect returns the image digest with the full manifest. // DistributionInspect returns the image digest with the full manifest.
func (cli *Client) DistributionInspect(ctx context.Context, imageRef, encodedRegistryAuth string) (registry.DistributionInspect, error) { func (cli *Client) DistributionInspect(ctx context.Context, imageRef, encodedRegistryAuth string) (registry.DistributionInspect, error) {
// Contact the registry to retrieve digest and platform information
var distributionInspect registry.DistributionInspect
if imageRef == "" { if imageRef == "" {
return distributionInspect, objectNotFoundError{object: "distribution", id: imageRef} return registry.DistributionInspect{}, objectNotFoundError{object: "distribution", id: imageRef}
} }
if err := cli.NewVersionError(ctx, "1.30", "distribution inspect"); err != nil { if err := cli.NewVersionError(ctx, "1.30", "distribution inspect"); err != nil {
return distributionInspect, err return registry.DistributionInspect{}, err
} }
var headers http.Header var headers http.Header
@ -28,12 +26,14 @@ func (cli *Client) DistributionInspect(ctx context.Context, imageRef, encodedReg
} }
} }
// Contact the registry to retrieve digest and platform information
resp, err := cli.get(ctx, "/distribution/"+imageRef+"/json", url.Values{}, headers) resp, err := cli.get(ctx, "/distribution/"+imageRef+"/json", url.Values{}, headers)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return distributionInspect, err return registry.DistributionInspect{}, err
} }
err = json.NewDecoder(resp.body).Decode(&distributionInspect) var distributionInspect registry.DistributionInspect
err = json.NewDecoder(resp.Body).Decode(&distributionInspect)
return distributionInspect, err return distributionInspect, err
} }

View File

@ -36,9 +36,9 @@ func (cli *Client) Events(ctx context.Context, options events.ListOptions) (<-ch
errs <- err errs <- err
return return
} }
defer resp.body.Close() defer resp.Body.Close()
decoder := json.NewDecoder(resp.body) decoder := json.NewDecoder(resp.Body)
close(started) close(started)
for { for {

View File

@ -33,14 +33,14 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
headers.Set("Content-Type", "application/x-tar") headers.Set("Content-Type", "application/x-tar")
serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers) resp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
if err != nil { if err != nil {
return types.ImageBuildResponse{}, err return types.ImageBuildResponse{}, err
} }
return types.ImageBuildResponse{ return types.ImageBuildResponse{
Body: serverResp.body, Body: resp.Body,
OSType: getDockerOS(serverResp.header.Get("Server")), OSType: getDockerOS(resp.Header.Get("Server")),
}, nil }, nil
} }

View File

@ -30,10 +30,10 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) {
return cli.post(ctx, "/images/create", query, nil, http.Header{ return cli.post(ctx, "/images/create", query, nil, http.Header{
registry.AuthHeader: {registryAuth}, registry.AuthHeader: {registryAuth},
}) })

View File

@ -3,33 +3,54 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"net/url" "net/url"
"github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
// ImageHistoryWithPlatform sets the platform for the image history operation.
func ImageHistoryWithPlatform(platform ocispec.Platform) ImageHistoryOption {
return imageHistoryOptionFunc(func(opt *imageHistoryOpts) error {
if opt.apiOptions.Platform != nil {
return fmt.Errorf("platform already set to %s", *opt.apiOptions.Platform)
}
opt.apiOptions.Platform = &platform
return nil
})
}
// ImageHistory returns the changes in an image in history format. // ImageHistory returns the changes in an image in history format.
func (cli *Client) ImageHistory(ctx context.Context, imageID string, opts image.HistoryOptions) ([]image.HistoryResponseItem, error) { func (cli *Client) ImageHistory(ctx context.Context, imageID string, historyOpts ...ImageHistoryOption) ([]image.HistoryResponseItem, error) {
query := url.Values{} query := url.Values{}
if opts.Platform != nil {
var opts imageHistoryOpts
for _, o := range historyOpts {
if err := o.Apply(&opts); err != nil {
return nil, err
}
}
if opts.apiOptions.Platform != nil {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil { if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return nil, err return nil, err
} }
p, err := encodePlatform(opts.Platform) p, err := encodePlatform(opts.apiOptions.Platform)
if err != nil { if err != nil {
return nil, err return nil, err
} }
query.Set("platform", p) query.Set("platform", p)
} }
serverResp, err := cli.get(ctx, "/images/"+imageID+"/history", query, nil) resp, err := cli.get(ctx, "/images/"+imageID+"/history", query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var history []image.HistoryResponseItem var history []image.HistoryResponseItem
err = json.NewDecoder(serverResp.body).Decode(&history) err = json.NewDecoder(resp.Body).Decode(&history)
return history, err return history, err
} }

View File

@ -0,0 +1,19 @@
package client
import (
"github.com/docker/docker/api/types/image"
)
// ImageHistoryOption is a type representing functional options for the image history operation.
type ImageHistoryOption interface {
Apply(*imageHistoryOpts) error
}
type imageHistoryOptionFunc func(opt *imageHistoryOpts) error
func (f imageHistoryOptionFunc) Apply(o *imageHistoryOpts) error {
return f(o)
}
type imageHistoryOpts struct {
apiOptions image.HistoryOptions
}

View File

@ -44,5 +44,5 @@ func (cli *Client) ImageImport(ctx context.Context, source image.ImportSource, r
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }

View File

@ -11,49 +11,6 @@ import (
"github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/image"
) )
// ImageInspectOption is a type representing functional options for the image inspect operation.
type ImageInspectOption interface {
Apply(*imageInspectOpts) error
}
type imageInspectOptionFunc func(opt *imageInspectOpts) error
func (f imageInspectOptionFunc) Apply(o *imageInspectOpts) error {
return f(o)
}
// ImageInspectWithRawResponse instructs the client to additionally store the
// raw inspect response in the provided buffer.
func ImageInspectWithRawResponse(raw *bytes.Buffer) ImageInspectOption {
return imageInspectOptionFunc(func(opts *imageInspectOpts) error {
opts.raw = raw
return nil
})
}
// ImageInspectWithManifests sets manifests API option for the image inspect operation.
// This option is only available for API version 1.48 and up.
// With this option set, the image inspect operation response will have the
// [image.InspectResponse.Manifests] field populated if the server is multi-platform capable.
func ImageInspectWithManifests(manifests bool) ImageInspectOption {
return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
clientOpts.apiOptions.Manifests = manifests
return nil
})
}
// ImageInspectWithAPIOpts sets the API options for the image inspect operation.
func ImageInspectWithAPIOpts(opts image.InspectOptions) ImageInspectOption {
return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
clientOpts.apiOptions = opts
return nil
})
}
type imageInspectOpts struct {
raw *bytes.Buffer
apiOptions image.InspectOptions
}
// ImageInspect returns the image information. // ImageInspect returns the image information.
func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts ...ImageInspectOption) (image.InspectResponse, error) { func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts ...ImageInspectOption) (image.InspectResponse, error) {
if imageID == "" { if imageID == "" {
@ -75,8 +32,8 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
query.Set("manifests", "1") query.Set("manifests", "1")
} }
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", query, nil) resp, err := cli.get(ctx, "/images/"+imageID+"/json", query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return image.InspectResponse{}, err return image.InspectResponse{}, err
} }
@ -86,7 +43,7 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
buf = &bytes.Buffer{} buf = &bytes.Buffer{}
} }
if _, err := io.Copy(buf, serverResp.body); err != nil { if _, err := io.Copy(buf, resp.Body); err != nil {
return image.InspectResponse{}, err return image.InspectResponse{}, err
} }
@ -97,8 +54,7 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
// ImageInspectWithRaw returns the image information and its raw representation. // ImageInspectWithRaw returns the image information and its raw representation.
// //
// Deprecated: Use [Client.ImageInspect] instead. // Deprecated: Use [Client.ImageInspect] instead. Raw response can be obtained using the [ImageInspectWithRawResponse] option.
// Raw response can be obtained by [ImageInspectWithRawResponse] option.
func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (image.InspectResponse, []byte, error) { func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (image.InspectResponse, []byte, error) {
var buf bytes.Buffer var buf bytes.Buffer
resp, err := cli.ImageInspect(ctx, imageID, ImageInspectWithRawResponse(&buf)) resp, err := cli.ImageInspect(ctx, imageID, ImageInspectWithRawResponse(&buf))

View File

@ -0,0 +1,50 @@
package client
import (
"bytes"
"github.com/docker/docker/api/types/image"
)
// ImageInspectOption is a type representing functional options for the image inspect operation.
type ImageInspectOption interface {
Apply(*imageInspectOpts) error
}
type imageInspectOptionFunc func(opt *imageInspectOpts) error
func (f imageInspectOptionFunc) Apply(o *imageInspectOpts) error {
return f(o)
}
// ImageInspectWithRawResponse instructs the client to additionally store the
// raw inspect response in the provided buffer.
func ImageInspectWithRawResponse(raw *bytes.Buffer) ImageInspectOption {
return imageInspectOptionFunc(func(opts *imageInspectOpts) error {
opts.raw = raw
return nil
})
}
// ImageInspectWithManifests sets manifests API option for the image inspect operation.
// This option is only available for API version 1.48 and up.
// With this option set, the image inspect operation response will have the
// [image.InspectResponse.Manifests] field populated if the server is multi-platform capable.
func ImageInspectWithManifests(manifests bool) ImageInspectOption {
return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
clientOpts.apiOptions.Manifests = manifests
return nil
})
}
// ImageInspectWithAPIOpts sets the API options for the image inspect operation.
func ImageInspectWithAPIOpts(opts image.InspectOptions) ImageInspectOption {
return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
clientOpts.apiOptions = opts
return nil
})
}
type imageInspectOpts struct {
raw *bytes.Buffer
apiOptions image.InspectOptions
}

View File

@ -56,12 +56,12 @@ func (cli *Client) ImageList(ctx context.Context, options image.ListOptions) ([]
query.Set("manifests", "1") query.Set("manifests", "1")
} }
serverResp, err := cli.get(ctx, "/images/json", query, nil) resp, err := cli.get(ctx, "/images/json", query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return images, err return images, err
} }
err = json.NewDecoder(serverResp.body).Decode(&images) err = json.NewDecoder(resp.Body).Decode(&images)
return images, err return images, err
} }

View File

@ -16,18 +16,25 @@ import (
// Platform is an optional parameter that specifies the platform to load from // Platform is an optional parameter that specifies the platform to load from
// the provided multi-platform image. This is only has effect if the input image // the provided multi-platform image. This is only has effect if the input image
// is a multi-platform image. // is a multi-platform image.
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error) { func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (image.LoadResponse, error) {
var opts imageLoadOpts
for _, opt := range loadOpts {
if err := opt.Apply(&opts); err != nil {
return image.LoadResponse{}, err
}
}
query := url.Values{} query := url.Values{}
query.Set("quiet", "0") query.Set("quiet", "0")
if opts.Quiet { if opts.apiOptions.Quiet {
query.Set("quiet", "1") query.Set("quiet", "1")
} }
if len(opts.Platforms) > 0 { if len(opts.apiOptions.Platforms) > 0 {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil { if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return image.LoadResponse{}, err return image.LoadResponse{}, err
} }
p, err := encodePlatforms(opts.Platforms...) p, err := encodePlatforms(opts.apiOptions.Platforms...)
if err != nil { if err != nil {
return image.LoadResponse{}, err return image.LoadResponse{}, err
} }
@ -41,7 +48,7 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.Lo
return image.LoadResponse{}, err return image.LoadResponse{}, err
} }
return image.LoadResponse{ return image.LoadResponse{
Body: resp.body, Body: resp.Body,
JSON: resp.header.Get("Content-Type") == "application/json", JSON: resp.Header.Get("Content-Type") == "application/json",
}, nil }, nil
} }

View File

@ -0,0 +1,41 @@
package client
import (
"fmt"
"github.com/docker/docker/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ImageLoadOption is a type representing functional options for the image load operation.
type ImageLoadOption interface {
Apply(*imageLoadOpts) error
}
type imageLoadOptionFunc func(opt *imageLoadOpts) error
func (f imageLoadOptionFunc) Apply(o *imageLoadOpts) error {
return f(o)
}
type imageLoadOpts struct {
apiOptions image.LoadOptions
}
// ImageLoadWithQuiet sets the quiet option for the image load operation.
func ImageLoadWithQuiet(quiet bool) ImageLoadOption {
return imageLoadOptionFunc(func(opt *imageLoadOpts) error {
opt.apiOptions.Quiet = quiet
return nil
})
}
// ImageLoadWithPlatforms sets the platforms to be loaded from the image.
func ImageLoadWithPlatforms(platforms ...ocispec.Platform) ImageLoadOption {
return imageLoadOptionFunc(func(opt *imageLoadOpts) error {
if opt.apiOptions.Platforms != nil {
return fmt.Errorf("platforms already set to %v", opt.apiOptions.Platforms)
}
opt.apiOptions.Platforms = platforms
return nil
})
}

View File

@ -20,14 +20,14 @@ func (cli *Client) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (
return image.PruneReport{}, err return image.PruneReport{}, err
} }
serverResp, err := cli.post(ctx, "/images/prune", query, nil, nil) resp, err := cli.post(ctx, "/images/prune", query, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return image.PruneReport{}, err return image.PruneReport{}, err
} }
var report image.PruneReport var report image.PruneReport
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(resp.Body).Decode(&report); err != nil {
return image.PruneReport{}, fmt.Errorf("Error retrieving disk usage: %v", err) return image.PruneReport{}, fmt.Errorf("Error retrieving disk usage: %v", err)
} }

View File

@ -45,7 +45,7 @@ func (cli *Client) ImagePull(ctx context.Context, refStr string, options image.P
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }
// getAPITagFromNamedRef returns a tag from the specified reference. // getAPITagFromNamedRef returns a tag from the specified reference.

View File

@ -63,10 +63,10 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options image.Pu
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }
func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (serverResponse, error) { func (cli *Client) tryImagePush(ctx context.Context, imageID string, query url.Values, registryAuth string) (*http.Response, error) {
return cli.post(ctx, "/images/"+imageID+"/push", query, nil, http.Header{ return cli.post(ctx, "/images/"+imageID+"/push", query, nil, http.Header{
registry.AuthHeader: {registryAuth}, registry.AuthHeader: {registryAuth},
}) })

View File

@ -19,13 +19,13 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options imag
query.Set("noprune", "1") query.Set("noprune", "1")
} }
var dels []image.DeleteResponse
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil) resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return dels, err return nil, err
} }
err = json.NewDecoder(resp.body).Decode(&dels) var dels []image.DeleteResponse
err = json.NewDecoder(resp.Body).Decode(&dels)
return dels, err return dels, err
} }

View File

@ -4,22 +4,29 @@ import (
"context" "context"
"io" "io"
"net/url" "net/url"
"github.com/docker/docker/api/types/image"
) )
// ImageSave retrieves one or more images from the docker host as an io.ReadCloser. // ImageSave retrieves one or more images from the docker host as an io.ReadCloser.
// It's up to the caller to store the images and close the stream. //
func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, opts image.SaveOptions) (io.ReadCloser, error) { // Platforms is an optional parameter that specifies the platforms to save from the image.
// This is only has effect if the input image is a multi-platform image.
func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, saveOpts ...ImageSaveOption) (io.ReadCloser, error) {
var opts imageSaveOpts
for _, opt := range saveOpts {
if err := opt.Apply(&opts); err != nil {
return nil, err
}
}
query := url.Values{ query := url.Values{
"names": imageIDs, "names": imageIDs,
} }
if len(opts.Platforms) > 0 { if len(opts.apiOptions.Platforms) > 0 {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil { if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return nil, err return nil, err
} }
p, err := encodePlatforms(opts.Platforms...) p, err := encodePlatforms(opts.apiOptions.Platforms...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -30,5 +37,5 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, opts image.
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }

View File

@ -0,0 +1,33 @@
package client
import (
"fmt"
"github.com/docker/docker/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
type ImageSaveOption interface {
Apply(*imageSaveOpts) error
}
type imageSaveOptionFunc func(opt *imageSaveOpts) error
func (f imageSaveOptionFunc) Apply(o *imageSaveOpts) error {
return f(o)
}
// ImageSaveWithPlatforms sets the platforms to be saved from the image.
func ImageSaveWithPlatforms(platforms ...ocispec.Platform) ImageSaveOption {
return imageSaveOptionFunc(func(opt *imageSaveOpts) error {
if opt.apiOptions.Platforms != nil {
return fmt.Errorf("platforms already set to %v", opt.apiOptions.Platforms)
}
opt.apiOptions.Platforms = platforms
return nil
})
}
type imageSaveOpts struct {
apiOptions image.SaveOptions
}

View File

@ -43,11 +43,11 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options registr
return results, err return results, err
} }
err = json.NewDecoder(resp.body).Decode(&results) err = json.NewDecoder(resp.Body).Decode(&results)
return results, err return results, err
} }
func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) {
return cli.get(ctx, "/images/search", query, http.Header{ return cli.get(ctx, "/images/search", query, http.Header{
registry.AuthHeader: {registryAuth}, registry.AuthHeader: {registryAuth},
}) })

View File

@ -12,13 +12,13 @@ import (
// Info returns information about the docker server. // Info returns information about the docker server.
func (cli *Client) Info(ctx context.Context) (system.Info, error) { func (cli *Client) Info(ctx context.Context) (system.Info, error) {
var info system.Info var info system.Info
serverResp, err := cli.get(ctx, "/info", url.Values{}, nil) resp, err := cli.get(ctx, "/info", url.Values{}, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return info, err return info, err
} }
if err := json.NewDecoder(serverResp.body).Decode(&info); err != nil { if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
return info, fmt.Errorf("Error reading remote info: %v", err) return info, fmt.Errorf("Error reading remote info: %v", err)
} }

View File

@ -19,6 +19,6 @@ func (cli *Client) RegistryLogin(ctx context.Context, auth registry.AuthConfig)
} }
var response registry.AuthenticateOKBody var response registry.AuthenticateOKBody
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -10,15 +10,13 @@ import (
// NetworkCreate creates a new network in the docker host. // NetworkCreate creates a new network in the docker host.
func (cli *Client) NetworkCreate(ctx context.Context, name string, options network.CreateOptions) (network.CreateResponse, error) { func (cli *Client) NetworkCreate(ctx context.Context, name string, options network.CreateOptions) (network.CreateResponse, error) {
var response network.CreateResponse
// Make sure we negotiated (if the client is configured to do so), // Make sure we negotiated (if the client is configured to do so),
// as code below contains API-version specific handling of options. // as code below contains API-version specific handling of options.
// //
// Normally, version-negotiation (if enabled) would not happen until // Normally, version-negotiation (if enabled) would not happen until
// the API request is made. // the API request is made.
if err := cli.checkVersion(ctx); err != nil { if err := cli.checkVersion(ctx); err != nil {
return response, err return network.CreateResponse{}, err
} }
networkCreateRequest := network.CreateRequest{ networkCreateRequest := network.CreateRequest{
@ -30,12 +28,13 @@ func (cli *Client) NetworkCreate(ctx context.Context, name string, options netwo
networkCreateRequest.CheckDuplicate = &enabled //nolint:staticcheck // ignore SA1019: CheckDuplicate is deprecated since API v1.44. networkCreateRequest.CheckDuplicate = &enabled //nolint:staticcheck // ignore SA1019: CheckDuplicate is deprecated since API v1.44.
} }
serverResp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil) resp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return network.CreateResponse{}, err
} }
err = json.NewDecoder(serverResp.body).Decode(&response) var response network.CreateResponse
err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -36,7 +36,7 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
return network.Inspect{}, nil, err return network.Inspect{}, nil, err
} }
raw, err := io.ReadAll(resp.body) raw, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return network.Inspect{}, nil, err return network.Inspect{}, nil, err
} }

View File

@ -27,6 +27,6 @@ func (cli *Client) NetworkList(ctx context.Context, options network.ListOptions)
if err != nil { if err != nil {
return networkResources, err return networkResources, err
} }
err = json.NewDecoder(resp.body).Decode(&networkResources) err = json.NewDecoder(resp.Body).Decode(&networkResources)
return networkResources, err return networkResources, err
} }

View File

@ -20,14 +20,14 @@ func (cli *Client) NetworksPrune(ctx context.Context, pruneFilters filters.Args)
return network.PruneReport{}, err return network.PruneReport{}, err
} }
serverResp, err := cli.post(ctx, "/networks/prune", query, nil, nil) resp, err := cli.post(ctx, "/networks/prune", query, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return network.PruneReport{}, err return network.PruneReport{}, err
} }
var report network.PruneReport var report network.PruneReport
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(resp.Body).Decode(&report); err != nil {
return network.PruneReport{}, fmt.Errorf("Error retrieving network prune report: %v", err) return network.PruneReport{}, fmt.Errorf("Error retrieving network prune report: %v", err)
} }

View File

@ -15,13 +15,13 @@ func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm
if err != nil { if err != nil {
return swarm.Node{}, nil, err return swarm.Node{}, nil, err
} }
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil) resp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return swarm.Node{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return swarm.Node{}, nil, err return swarm.Node{}, nil, err
} }

View File

@ -30,6 +30,6 @@ func (cli *Client) NodeList(ctx context.Context, options types.NodeListOptions)
} }
var nodes []swarm.Node var nodes []swarm.Node
err = json.NewDecoder(resp.body).Decode(&nodes) err = json.NewDecoder(resp.Body).Decode(&nodes)
return nodes, err return nodes, err
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/docker/go-connections/sockets" "github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
) )
@ -227,8 +228,13 @@ func WithAPIVersionNegotiation() Opt {
// WithTraceProvider sets the trace provider for the client. // WithTraceProvider sets the trace provider for the client.
// If this is not set then the global trace provider will be used. // If this is not set then the global trace provider will be used.
func WithTraceProvider(provider trace.TracerProvider) Opt { func WithTraceProvider(provider trace.TracerProvider) Opt {
return WithTraceOptions(otelhttp.WithTracerProvider(provider))
}
// WithTraceOptions sets tracing span options for the client.
func WithTraceOptions(opts ...otelhttp.Option) Opt {
return func(c *Client) error { return func(c *Client) error {
c.tp = provider c.traceOpts = append(c.traceOpts, opts...)
return nil return nil
} }
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/errdefs"
) )
// Ping pings the server and returns the value of the "Docker-Experimental", // Ping pings the server and returns the value of the "Docker-Experimental",
@ -28,49 +27,54 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
if err != nil { if err != nil {
return ping, err return ping, err
} }
serverResp, err := cli.doRequest(req) resp, err := cli.doRequest(req)
if err == nil { if err != nil {
defer ensureReaderClosed(serverResp) if IsErrConnectionFailed(err) {
switch serverResp.statusCode { return ping, err
}
// We managed to connect, but got some error; continue and try GET request.
} else {
defer ensureReaderClosed(resp)
switch resp.StatusCode {
case http.StatusOK, http.StatusInternalServerError: case http.StatusOK, http.StatusInternalServerError:
// Server handled the request, so parse the response // Server handled the request, so parse the response
return parsePingResponse(cli, serverResp) return parsePingResponse(cli, resp)
} }
} else if IsErrConnectionFailed(err) {
return ping, err
} }
// HEAD failed; fallback to GET. // HEAD failed; fallback to GET.
req.Method = http.MethodGet req.Method = http.MethodGet
serverResp, err = cli.doRequest(req) resp, err = cli.doRequest(req)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return ping, err return ping, err
} }
return parsePingResponse(cli, serverResp) return parsePingResponse(cli, resp)
} }
func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) { func parsePingResponse(cli *Client, resp *http.Response) (types.Ping, error) {
var ping types.Ping if resp == nil {
if resp.header == nil { return types.Ping{}, nil
err := cli.checkResponseErr(resp)
return ping, errdefs.FromStatusCode(err, resp.statusCode)
} }
ping.APIVersion = resp.header.Get("Api-Version")
ping.OSType = resp.header.Get("Ostype") var ping types.Ping
if resp.header.Get("Docker-Experimental") == "true" { if resp.Header == nil {
return ping, cli.checkResponseErr(resp)
}
ping.APIVersion = resp.Header.Get("Api-Version")
ping.OSType = resp.Header.Get("Ostype")
if resp.Header.Get("Docker-Experimental") == "true" {
ping.Experimental = true ping.Experimental = true
} }
if bv := resp.header.Get("Builder-Version"); bv != "" { if bv := resp.Header.Get("Builder-Version"); bv != "" {
ping.BuilderVersion = types.BuilderVersion(bv) ping.BuilderVersion = types.BuilderVersion(bv)
} }
if si := resp.header.Get("Swarm"); si != "" { if si := resp.Header.Get("Swarm"); si != "" {
state, role, _ := strings.Cut(si, "/") state, role, _ := strings.Cut(si, "/")
ping.SwarmStatus = &swarm.Status{ ping.SwarmStatus = &swarm.Status{
NodeState: swarm.LocalNodeState(state), NodeState: swarm.LocalNodeState(state),
ControlAvailable: role == "manager", ControlAvailable: role == "manager",
} }
} }
err := cli.checkResponseErr(resp) return ping, cli.checkResponseErr(resp)
return ping, errdefs.FromStatusCode(err, resp.statusCode)
} }

View File

@ -21,7 +21,7 @@ func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*type
return nil, nil, err return nil, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -35,13 +35,13 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
return nil, err return nil, err
} }
name = resp.header.Get("Docker-Plugin-Name") name = resp.Header.Get("Docker-Plugin-Name")
pr, pw := io.Pipe() pr, pw := io.Pipe()
go func() { // todo: the client should probably be designed more around the actual api go func() { // todo: the client should probably be designed more around the actual api
_, err := io.Copy(pw, resp.body) _, err := io.Copy(pw, resp.Body)
if err != nil { if err != nil {
pw.CloseWithError(err) _ = pw.CloseWithError(err)
return return
} }
defer func() { defer func() {
@ -52,29 +52,29 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
}() }()
if len(options.Args) > 0 { if len(options.Args) > 0 {
if err := cli.PluginSet(ctx, name, options.Args); err != nil { if err := cli.PluginSet(ctx, name, options.Args); err != nil {
pw.CloseWithError(err) _ = pw.CloseWithError(err)
return return
} }
} }
if options.Disabled { if options.Disabled {
pw.Close() _ = pw.Close()
return return
} }
enableErr := cli.PluginEnable(ctx, name, types.PluginEnableOptions{Timeout: 0}) enableErr := cli.PluginEnable(ctx, name, types.PluginEnableOptions{Timeout: 0})
pw.CloseWithError(enableErr) _ = pw.CloseWithError(enableErr)
}() }()
return pr, nil return pr, nil
} }
func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) {
return cli.get(ctx, "/plugins/privileges", query, http.Header{ return cli.get(ctx, "/plugins/privileges", query, http.Header{
registry.AuthHeader: {registryAuth}, registry.AuthHeader: {registryAuth},
}) })
} }
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges types.PluginPrivileges, registryAuth string) (serverResponse, error) { func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges types.PluginPrivileges, registryAuth string) (*http.Response, error) {
return cli.post(ctx, "/plugins/pull", query, privileges, http.Header{ return cli.post(ctx, "/plugins/pull", query, privileges, http.Header{
registry.AuthHeader: {registryAuth}, registry.AuthHeader: {registryAuth},
}) })
@ -98,7 +98,7 @@ func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values,
} }
var privileges types.PluginPrivileges var privileges types.PluginPrivileges
if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil { if err := json.NewDecoder(resp.Body).Decode(&privileges); err != nil {
ensureReaderClosed(resp) ensureReaderClosed(resp)
return nil, err return nil, err
} }

View File

@ -28,6 +28,6 @@ func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.P
return plugins, err return plugins, err
} }
err = json.NewDecoder(resp.body).Decode(&plugins) err = json.NewDecoder(resp.Body).Decode(&plugins)
return plugins, err return plugins, err
} }

View File

@ -20,5 +20,5 @@ func (cli *Client) PluginPush(ctx context.Context, name string, registryAuth str
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }

View File

@ -37,10 +37,10 @@ func (cli *Client) PluginUpgrade(ctx context.Context, name string, options types
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }
func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges types.PluginPrivileges, name, registryAuth string) (serverResponse, error) { func (cli *Client) tryPluginUpgrade(ctx context.Context, query url.Values, privileges types.PluginPrivileges, name, registryAuth string) (*http.Response, error) {
return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, http.Header{ return cli.post(ctx, "/plugins/"+name+"/upgrade", query, privileges, http.Header{
registry.AuthHeader: {registryAuth}, registry.AuthHeader: {registryAuth},
}) })

View File

@ -19,47 +19,39 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// serverResponse is a wrapper for http API responses.
type serverResponse struct {
body io.ReadCloser
header http.Header
statusCode int
reqURL *url.URL
}
// head sends an http request to the docker API using the method HEAD. // 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) (serverResponse, error) { 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, nil, headers)
} }
// get sends an http request to the docker API using the method GET with a specific Go context. // 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) (serverResponse, error) { 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, nil, headers)
} }
// post sends an http request to the docker API using the method POST with a specific Go context. // post sends an http request to the docker API using the method POST with a specific Go context.
func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers http.Header) (serverResponse, error) { func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers http.Header) (*http.Response, error) {
body, headers, err := encodeBody(obj, headers) body, headers, err := encodeBody(obj, headers)
if err != nil { if err != nil {
return serverResponse{}, err return nil, err
} }
return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers) return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers)
} }
func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers http.Header) (serverResponse, error) { 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) return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers)
} }
func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers http.Header) (serverResponse, error) { func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers http.Header) (*http.Response, error) {
body, headers, err := encodeBody(obj, headers) body, headers, err := encodeBody(obj, headers)
if err != nil { if err != nil {
return serverResponse{}, err return nil, err
} }
return cli.putRaw(ctx, path, query, body, headers) return cli.putRaw(ctx, path, query, body, headers)
} }
// putRaw sends an http request to the docker API using the method PUT. // 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) (serverResponse, error) { func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers http.Header) (*http.Response, error) {
// PUT requests are expected to always have a body (apparently) // PUT requests are expected to always have a body (apparently)
// so explicitly pass an empty body to sendRequest to signal that // so explicitly pass an empty body to sendRequest to signal that
// it should set the Content-Type header if not already present. // it should set the Content-Type header if not already present.
@ -70,7 +62,7 @@ func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, bo
} }
// delete sends an http request to the docker API using the method DELETE. // 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) (serverResponse, error) { 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, nil, headers)
} }
@ -116,42 +108,40 @@ func (cli *Client) buildRequest(ctx context.Context, method, path string, body i
return req, nil return req, nil
} }
func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers http.Header) (serverResponse, error) { 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) req, err := cli.buildRequest(ctx, method, cli.getAPIPath(ctx, path, query), body, headers)
if err != nil { if err != nil {
return serverResponse{}, err return nil, err
} }
resp, err := cli.doRequest(req) resp, err := cli.doRequest(req)
switch { switch {
case errors.Is(err, context.Canceled): case errors.Is(err, context.Canceled):
return serverResponse{}, errdefs.Cancelled(err) return nil, errdefs.Cancelled(err)
case errors.Is(err, context.DeadlineExceeded): case errors.Is(err, context.DeadlineExceeded):
return serverResponse{}, errdefs.Deadline(err) return nil, errdefs.Deadline(err)
case err == nil: case err == nil:
err = cli.checkResponseErr(resp) return resp, cli.checkResponseErr(resp)
default:
return resp, err
} }
return resp, errdefs.FromStatusCode(err, resp.statusCode)
} }
// FIXME(thaJeztah): Should this actually return a serverResp when a connection error occurred? func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
func (cli *Client) doRequest(req *http.Request) (serverResponse, error) {
serverResp := serverResponse{statusCode: -1, reqURL: req.URL}
resp, err := cli.client.Do(req) resp, err := cli.client.Do(req)
if err != nil { if err != nil {
if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") { if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") {
return serverResp, errConnectionFailed{fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)} return nil, errConnectionFailed{fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)}
} }
if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") { if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") {
return serverResp, errConnectionFailed{errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings")} return nil, errConnectionFailed{errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings")}
} }
// Don't decorate context sentinel errors; users may be comparing to // Don't decorate context sentinel errors; users may be comparing to
// them directly. // them directly.
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return serverResp, err return nil, err
} }
var uErr *url.Error var uErr *url.Error
@ -159,7 +149,7 @@ func (cli *Client) doRequest(req *http.Request) (serverResponse, error) {
var nErr *net.OpError var nErr *net.OpError
if errors.As(uErr.Err, &nErr) { if errors.As(uErr.Err, &nErr) {
if os.IsPermission(nErr.Err) { if os.IsPermission(nErr.Err) {
return serverResp, errConnectionFailed{errors.Wrapf(err, "permission denied while trying to connect to the Docker daemon socket at %v", cli.host)} return nil, errConnectionFailed{errors.Wrapf(err, "permission denied while trying to connect to the Docker daemon socket at %v", cli.host)}
} }
} }
} }
@ -168,10 +158,10 @@ func (cli *Client) doRequest(req *http.Request) (serverResponse, error) {
if errors.As(err, &nErr) { if errors.As(err, &nErr) {
// FIXME(thaJeztah): any net.Error should be considered a connection error (but we should include the original error)? // FIXME(thaJeztah): any net.Error should be considered a connection error (but we should include the original error)?
if nErr.Timeout() { if nErr.Timeout() {
return serverResp, connectionFailed(cli.host) return nil, connectionFailed(cli.host)
} }
if strings.Contains(nErr.Error(), "connection refused") || strings.Contains(nErr.Error(), "dial unix") { if strings.Contains(nErr.Error(), "connection refused") || strings.Contains(nErr.Error(), "dial unix") {
return serverResp, connectionFailed(cli.host) return nil, connectionFailed(cli.host)
} }
} }
@ -195,28 +185,37 @@ func (cli *Client) doRequest(req *http.Request) (serverResponse, error) {
} }
} }
return serverResp, errConnectionFailed{errors.Wrap(err, "error during connect")} return nil, errConnectionFailed{errors.Wrap(err, "error during connect")}
} }
if resp != nil { return resp, nil
serverResp.statusCode = resp.StatusCode
serverResp.body = resp.Body
serverResp.header = resp.Header
}
return serverResp, nil
} }
func (cli *Client) checkResponseErr(serverResp serverResponse) error { func (cli *Client) checkResponseErr(serverResp *http.Response) (retErr error) {
if serverResp.statusCode >= 200 && serverResp.statusCode < 400 { if serverResp == nil {
return nil return nil
} }
if serverResp.StatusCode >= 200 && serverResp.StatusCode < 400 {
return nil
}
defer func() {
retErr = errdefs.FromStatusCode(retErr, serverResp.StatusCode)
}()
var body []byte var body []byte
var err error var err error
if serverResp.body != nil { var reqURL string
if serverResp.Request != nil {
reqURL = serverResp.Request.URL.String()
}
statusMsg := serverResp.Status
if statusMsg == "" {
statusMsg = http.StatusText(serverResp.StatusCode)
}
if serverResp.Body != nil {
bodyMax := 1 * 1024 * 1024 // 1 MiB bodyMax := 1 * 1024 * 1024 // 1 MiB
bodyR := &io.LimitedReader{ bodyR := &io.LimitedReader{
R: serverResp.body, R: serverResp.Body,
N: int64(bodyMax), N: int64(bodyMax),
} }
body, err = io.ReadAll(bodyR) body, err = io.ReadAll(bodyR)
@ -224,15 +223,21 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error {
return err return err
} }
if bodyR.N == 0 { if bodyR.N == 0 {
return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL) if reqURL != "" {
return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", statusMsg, bodyMax, reqURL)
}
return fmt.Errorf("request returned %s with a message (> %d bytes); check if the server supports the requested API version", statusMsg, bodyMax)
} }
} }
if len(body) == 0 { if len(body) == 0 {
return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL) if reqURL != "" {
return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", statusMsg, reqURL)
}
return fmt.Errorf("request returned %s; check if the server supports the requested API version", statusMsg)
} }
var daemonErr error var daemonErr error
if serverResp.header.Get("Content-Type") == "application/json" && (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) { if serverResp.Header.Get("Content-Type") == "application/json" && (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) {
var errorResponse types.ErrorResponse var errorResponse types.ErrorResponse
if err := json.Unmarshal(body, &errorResponse); err != nil { if err := json.Unmarshal(body, &errorResponse); err != nil {
return errors.Wrap(err, "Error reading JSON") return errors.Wrap(err, "Error reading JSON")
@ -255,8 +260,8 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error {
// //
// TODO(thaJeztah): consider adding a log.Debug to allow clients to debug the actual response when enabling debug logging. // TODO(thaJeztah): consider adding a log.Debug to allow clients to debug the actual response when enabling debug logging.
daemonErr = fmt.Errorf(`API returned a %d (%s) but provided no error-message`, daemonErr = fmt.Errorf(`API returned a %d (%s) but provided no error-message`,
serverResp.statusCode, serverResp.StatusCode,
http.StatusText(serverResp.statusCode), http.StatusText(serverResp.StatusCode),
) )
} else { } else {
daemonErr = errors.New(strings.TrimSpace(errorResponse.Message)) daemonErr = errors.New(strings.TrimSpace(errorResponse.Message))
@ -305,10 +310,16 @@ func encodeData(data interface{}) (*bytes.Buffer, error) {
return params, nil return params, nil
} }
func ensureReaderClosed(response serverResponse) { func ensureReaderClosed(response *http.Response) {
if response.body != nil { if response != nil && response.Body != nil {
// Drain up to 512 bytes and close the body to let the Transport reuse the connection // Drain up to 512 bytes and close the body to let the Transport reuse the connection
_, _ = io.CopyN(io.Discard, response.body, 512) // see https://github.com/google/go-github/pull/317/files#r57536827
_ = response.body.Close() //
// TODO(thaJeztah): see if this optimization is still needed, or already implemented in stdlib,
// and check if context-cancellation should handle this as well. If still needed, consider
// wrapping response.Body, or returning a "closer()" from [Client.sendRequest] and related
// methods.
_, _ = io.CopyN(io.Discard, response.Body, 512)
_ = response.Body.Close()
} }
} }

View File

@ -10,16 +10,16 @@ import (
// SecretCreate creates a new secret. // SecretCreate creates a new secret.
func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) { func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) {
var response types.SecretCreateResponse
if err := cli.NewVersionError(ctx, "1.25", "secret create"); err != nil { if err := cli.NewVersionError(ctx, "1.25", "secret create"); err != nil {
return response, err return types.SecretCreateResponse{}, err
} }
resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil) resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return response, err return types.SecretCreateResponse{}, err
} }
err = json.NewDecoder(resp.body).Decode(&response) var response types.SecretCreateResponse
err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -24,7 +24,7 @@ func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.S
return swarm.Secret{}, nil, err return swarm.Secret{}, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return swarm.Secret{}, nil, err return swarm.Secret{}, nil, err
} }

View File

@ -33,6 +33,6 @@ func (cli *Client) SecretList(ctx context.Context, options types.SecretListOptio
} }
var secrets []swarm.Secret var secrets []swarm.Secret
err = json.NewDecoder(resp.body).Decode(&secrets) err = json.NewDecoder(resp.Body).Decode(&secrets)
return secrets, err return secrets, err
} }

View File

@ -73,7 +73,7 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
return response, err return response, err
} }
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
if resolveWarning != "" { if resolveWarning != "" {
response.Warnings = append(response.Warnings, resolveWarning) response.Warnings = append(response.Warnings, resolveWarning)
} }

View File

@ -21,13 +21,13 @@ func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string,
query := url.Values{} query := url.Values{}
query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults)) query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil) resp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Service{}, nil, err return swarm.Service{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return swarm.Service{}, nil, err return swarm.Service{}, nil, err
} }

View File

@ -34,6 +34,6 @@ func (cli *Client) ServiceList(ctx context.Context, options types.ServiceListOpt
} }
var services []swarm.Service var services []swarm.Service
err = json.NewDecoder(resp.body).Decode(&services) err = json.NewDecoder(resp.Body).Decode(&services)
return services, err return services, err
} }

View File

@ -53,5 +53,5 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options co
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }

View File

@ -80,8 +80,8 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
return swarm.ServiceUpdateResponse{}, err return swarm.ServiceUpdateResponse{}, err
} }
response := swarm.ServiceUpdateResponse{} var response swarm.ServiceUpdateResponse
err = json.NewDecoder(resp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
if resolveWarning != "" { if resolveWarning != "" {
response.Warnings = append(response.Warnings, resolveWarning) response.Warnings = append(response.Warnings, resolveWarning)
} }

View File

@ -9,13 +9,13 @@ import (
// SwarmGetUnlockKey retrieves the swarm's unlock key. // SwarmGetUnlockKey retrieves the swarm's unlock key.
func (cli *Client) SwarmGetUnlockKey(ctx context.Context) (types.SwarmUnlockKeyResponse, error) { func (cli *Client) SwarmGetUnlockKey(ctx context.Context) (types.SwarmUnlockKeyResponse, error) {
serverResp, err := cli.get(ctx, "/swarm/unlockkey", nil, nil) resp, err := cli.get(ctx, "/swarm/unlockkey", nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return types.SwarmUnlockKeyResponse{}, err return types.SwarmUnlockKeyResponse{}, err
} }
var response types.SwarmUnlockKeyResponse var response types.SwarmUnlockKeyResponse
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -9,13 +9,13 @@ import (
// SwarmInit initializes the swarm. // SwarmInit initializes the swarm.
func (cli *Client) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) { func (cli *Client) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) {
serverResp, err := cli.post(ctx, "/swarm/init", nil, req, nil) resp, err := cli.post(ctx, "/swarm/init", nil, req, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return "", err return "", err
} }
var response string var response string
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -9,13 +9,13 @@ import (
// SwarmInspect inspects the swarm. // SwarmInspect inspects the swarm.
func (cli *Client) SwarmInspect(ctx context.Context) (swarm.Swarm, error) { func (cli *Client) SwarmInspect(ctx context.Context) (swarm.Swarm, error) {
serverResp, err := cli.get(ctx, "/swarm", nil, nil) resp, err := cli.get(ctx, "/swarm", nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Swarm{}, err return swarm.Swarm{}, err
} }
var response swarm.Swarm var response swarm.Swarm
err = json.NewDecoder(serverResp.body).Decode(&response) err = json.NewDecoder(resp.Body).Decode(&response)
return response, err return response, err
} }

View File

@ -8,7 +8,7 @@ import (
// SwarmUnlock unlocks locked swarm. // SwarmUnlock unlocks locked swarm.
func (cli *Client) SwarmUnlock(ctx context.Context, req swarm.UnlockRequest) error { func (cli *Client) SwarmUnlock(ctx context.Context, req swarm.UnlockRequest) error {
serverResp, err := cli.post(ctx, "/swarm/unlock", nil, req, nil) resp, err := cli.post(ctx, "/swarm/unlock", nil, req, nil)
ensureReaderClosed(serverResp) ensureReaderClosed(resp)
return err return err
} }

View File

@ -16,13 +16,13 @@ func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm
return swarm.Task{}, nil, err return swarm.Task{}, nil, err
} }
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil) resp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Task{}, nil, err return swarm.Task{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return swarm.Task{}, nil, err return swarm.Task{}, nil, err
} }

View File

@ -30,6 +30,6 @@ func (cli *Client) TaskList(ctx context.Context, options types.TaskListOptions)
} }
var tasks []swarm.Task var tasks []swarm.Task
err = json.NewDecoder(resp.body).Decode(&tasks) err = json.NewDecoder(resp.Body).Decode(&tasks)
return tasks, err return tasks, err
} }

View File

@ -47,5 +47,5 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options containe
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.Body, nil
} }

View File

@ -16,6 +16,6 @@ func (cli *Client) ServerVersion(ctx context.Context) (types.Version, error) {
} }
var server types.Version var server types.Version
err = json.NewDecoder(resp.body).Decode(&server) err = json.NewDecoder(resp.Body).Decode(&server)
return server, err return server, err
} }

View File

@ -9,12 +9,13 @@ import (
// VolumeCreate creates a volume in the docker host. // VolumeCreate creates a volume in the docker host.
func (cli *Client) VolumeCreate(ctx context.Context, options volume.CreateOptions) (volume.Volume, error) { func (cli *Client) VolumeCreate(ctx context.Context, options volume.CreateOptions) (volume.Volume, error) {
var vol volume.Volume
resp, err := cli.post(ctx, "/volumes/create", nil, options, nil) resp, err := cli.post(ctx, "/volumes/create", nil, options, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return vol, err return volume.Volume{}, err
} }
err = json.NewDecoder(resp.body).Decode(&vol)
var vol volume.Volume
err = json.NewDecoder(resp.Body).Decode(&vol)
return vol, err return vol, err
} }

View File

@ -22,17 +22,18 @@ func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (v
return volume.Volume{}, nil, err return volume.Volume{}, nil, err
} }
var vol volume.Volume
resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil) resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return vol, nil, err return volume.Volume{}, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return vol, nil, err return volume.Volume{}, nil, err
} }
var vol volume.Volume
rdr := bytes.NewReader(body) rdr := bytes.NewReader(body)
err = json.NewDecoder(rdr).Decode(&vol) err = json.NewDecoder(rdr).Decode(&vol)
return vol, body, err return vol, body, err

View File

@ -11,23 +11,23 @@ import (
// VolumeList returns the volumes configured in the docker host. // VolumeList returns the volumes configured in the docker host.
func (cli *Client) VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) { func (cli *Client) VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) {
var volumes volume.ListResponse
query := url.Values{} query := url.Values{}
if options.Filters.Len() > 0 { if options.Filters.Len() > 0 {
//nolint:staticcheck // ignore SA1019 for old code //nolint:staticcheck // ignore SA1019 for old code
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters) filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
if err != nil { if err != nil {
return volumes, err return volume.ListResponse{}, err
} }
query.Set("filters", filterJSON) query.Set("filters", filterJSON)
} }
resp, err := cli.get(ctx, "/volumes", query, nil) resp, err := cli.get(ctx, "/volumes", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return volumes, err return volume.ListResponse{}, err
} }
err = json.NewDecoder(resp.body).Decode(&volumes) var volumes volume.ListResponse
err = json.NewDecoder(resp.Body).Decode(&volumes)
return volumes, err return volumes, err
} }

View File

@ -20,14 +20,14 @@ func (cli *Client) VolumesPrune(ctx context.Context, pruneFilters filters.Args)
return volume.PruneReport{}, err return volume.PruneReport{}, err
} }
serverResp, err := cli.post(ctx, "/volumes/prune", query, nil, nil) resp, err := cli.post(ctx, "/volumes/prune", query, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return volume.PruneReport{}, err return volume.PruneReport{}, err
} }
var report volume.PruneReport var report volume.PruneReport
if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { if err := json.NewDecoder(resp.Body).Decode(&report); err != nil {
return volume.PruneReport{}, fmt.Errorf("Error retrieving volume prune report: %v", err) return volume.PruneReport{}, fmt.Errorf("Error retrieving volume prune report: %v", err)
} }

View File

@ -1,11 +1,11 @@
package archive package archive
import ( import (
"bytes"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings"
"syscall" "syscall"
"unsafe" "unsafe"
@ -143,7 +143,7 @@ func (w *walker) walk(path string, i1, i2 os.FileInfo) (err error) {
ni1 := names1[ix1] ni1 := names1[ix1]
ni2 := names2[ix2] ni2 := names2[ix2]
switch bytes.Compare([]byte(ni1.name), []byte(ni2.name)) { switch strings.Compare(ni1.name, ni2.name) {
case -1: // ni1 < ni2 -- advance ni1 case -1: // ni1 < ni2 -- advance ni1
// we will not encounter ni1 in names2 // we will not encounter ni1 in names2
names = append(names, ni1.name) names = append(names, ni1.name)

View File

@ -53,10 +53,9 @@ type JSONProgress struct {
func (p *JSONProgress) String() string { func (p *JSONProgress) String() string {
var ( var (
width = p.width() width = p.width()
pbBox string pbBox string
numbersBox string numbersBox string
timeLeftBox string
) )
if p.Current <= 0 && p.Total <= 0 { if p.Current <= 0 && p.Total <= 0 {
return "" return ""
@ -104,14 +103,14 @@ func (p *JSONProgress) String() string {
} }
} }
if p.Current > 0 && p.Start > 0 && percentage < 50 { // Show approximation of remaining time if there's enough width.
fromStart := p.now().Sub(time.Unix(p.Start, 0)) var timeLeftBox string
perEntry := fromStart / time.Duration(p.Current) if width > 50 {
left := time.Duration(p.Total-p.Current) * perEntry if p.Current > 0 && p.Start > 0 && percentage < 50 {
left = (left / time.Second) * time.Second fromStart := p.now().Sub(time.Unix(p.Start, 0))
perEntry := fromStart / time.Duration(p.Current)
if width > 50 { left := time.Duration(p.Total-p.Current) * perEntry
timeLeftBox = " " + left.String() timeLeftBox = " " + left.Round(time.Second).String()
} }
} }
return pbBox + numbersBox + timeLeftBox return pbBox + numbersBox + timeLeftBox
@ -143,16 +142,24 @@ func (p *JSONProgress) width() int {
// the created time, where it from, status, ID of the // the created time, where it from, status, ID of the
// message. It's used for docker events. // message. It's used for docker events.
type JSONMessage struct { type JSONMessage struct {
Stream string `json:"stream,omitempty"` Stream string `json:"stream,omitempty"`
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
Progress *JSONProgress `json:"progressDetail,omitempty"` Progress *JSONProgress `json:"progressDetail,omitempty"`
ProgressMessage string `json:"progress,omitempty"` // deprecated
ID string `json:"id,omitempty"` // ProgressMessage is a pre-formatted presentation of [Progress].
From string `json:"from,omitempty"` //
Time int64 `json:"time,omitempty"` // Deprecated: this field is deprecated since docker v0.7.1 / API v1.8. Use the information in [Progress] instead. This field will be omitted in a future release.
TimeNano int64 `json:"timeNano,omitempty"` ProgressMessage string `json:"progress,omitempty"`
Error *JSONError `json:"errorDetail,omitempty"` ID string `json:"id,omitempty"`
ErrorMessage string `json:"error,omitempty"` // deprecated From string `json:"from,omitempty"`
Time int64 `json:"time,omitempty"`
TimeNano int64 `json:"timeNano,omitempty"`
Error *JSONError `json:"errorDetail,omitempty"`
// ErrorMessage contains errors encountered during the operation.
//
// Deprecated: this field is deprecated since docker v0.6.0 / API v1.4. Use [Error.Message] instead. This field will be omitted in a future release.
ErrorMessage string `json:"error,omitempty"` // deprecated
// Aux contains out-of-band data, such as digests for push signing and image id after building. // Aux contains out-of-band data, such as digests for push signing and image id after building.
Aux *json.RawMessage `json:"aux,omitempty"` Aux *json.RawMessage `json:"aux,omitempty"`
} }

3
vendor/modules.txt vendored
View File

@ -283,12 +283,13 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid github.com/docker/distribution/uuid
# github.com/docker/docker v28.0.0-rc.1+incompatible # github.com/docker/docker v28.0.0-rc.2+incompatible
## explicit ## explicit
github.com/docker/docker/api github.com/docker/docker/api
github.com/docker/docker/api/types github.com/docker/docker/api/types
github.com/docker/docker/api/types/blkiodev github.com/docker/docker/api/types/blkiodev
github.com/docker/docker/api/types/checkpoint github.com/docker/docker/api/types/checkpoint
github.com/docker/docker/api/types/common
github.com/docker/docker/api/types/container github.com/docker/docker/api/types/container
github.com/docker/docker/api/types/events github.com/docker/docker/api/types/events
github.com/docker/docker/api/types/filters github.com/docker/docker/api/types/filters