mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Fnlb was moved to its own repo: fnproject/lb (#702)
* Fnlb was moved to its own repo: fnproject/lb * Clean up fnlb leftovers * Newer deps
This commit is contained in:
committed by
Reed Allman
parent
4ffa3d5005
commit
d3be603e54
2
vendor/github.com/docker/docker/.github/CODEOWNERS
generated
vendored
2
vendor/github.com/docker/docker/.github/CODEOWNERS
generated
vendored
@@ -1,4 +1,4 @@
|
||||
# Github code owners
|
||||
# GitHub code owners
|
||||
# See https://help.github.com/articles/about-codeowners/
|
||||
#
|
||||
# KEEP THIS FILE SORTED. Order is important. Last match takes precedence.
|
||||
|
||||
12
vendor/github.com/docker/docker/.github/ISSUE_TEMPLATE.md
generated
vendored
12
vendor/github.com/docker/docker/.github/ISSUE_TEMPLATE.md
generated
vendored
@@ -10,19 +10,25 @@ information within 7 days, we cannot debug your issue and will close it. We
|
||||
will, however, reopen it if you later provide the information.
|
||||
|
||||
For more information about reporting issues, see
|
||||
https://github.com/docker/docker/blob/master/CONTRIBUTING.md#reporting-other-issues
|
||||
https://github.com/moby/moby/blob/master/CONTRIBUTING.md#reporting-other-issues
|
||||
|
||||
---------------------------------------------------
|
||||
GENERAL SUPPORT INFORMATION
|
||||
---------------------------------------------------
|
||||
|
||||
The GitHub issue tracker is for bug reports and feature requests.
|
||||
General support can be found at the following locations:
|
||||
General support for **docker** can be found at the following locations:
|
||||
|
||||
- Docker Support Forums - https://forums.docker.com
|
||||
- IRC - irc.freenode.net #docker channel
|
||||
- Slack - community.docker.com #general channel
|
||||
- Post a question on StackOverflow, using the Docker tag
|
||||
|
||||
General support for **moby** can be found at the following locations:
|
||||
|
||||
- Moby Project Forums - https://forums.mobyproject.org
|
||||
- Slack - community.docker.com #moby-project channel
|
||||
- Post a question on StackOverflow, using the Moby tag
|
||||
|
||||
---------------------------------------------------
|
||||
BUG REPORT INFORMATION
|
||||
---------------------------------------------------
|
||||
|
||||
2
vendor/github.com/docker/docker/.github/PULL_REQUEST_TEMPLATE.md
generated
vendored
2
vendor/github.com/docker/docker/.github/PULL_REQUEST_TEMPLATE.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
Please make sure you've read and understood our contributing guidelines;
|
||||
https://github.com/docker/docker/blob/master/CONTRIBUTING.md
|
||||
https://github.com/moby/moby/blob/master/CONTRIBUTING.md
|
||||
|
||||
** Make sure all your commits include a signature generated with `git commit -s` **
|
||||
|
||||
|
||||
44
vendor/github.com/docker/docker/.mailmap
generated
vendored
44
vendor/github.com/docker/docker/.mailmap
generated
vendored
@@ -10,6 +10,7 @@ Patrick Stapleton <github@gdi2290.com>
|
||||
Shishir Mahajan <shishir.mahajan@redhat.com> <smahajan@redhat.com>
|
||||
Erwin van der Koogh <info@erronis.nl>
|
||||
Ahmed Kamal <email.ahmedkamal@googlemail.com>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com> <aboch@docker.com>
|
||||
Tejesh Mehta <tejesh.mehta@gmail.com> <tj@init.me>
|
||||
Cristian Staretu <cristian.staretu@gmail.com>
|
||||
Cristian Staretu <cristian.staretu@gmail.com> <unclejacksons@gmail.com>
|
||||
@@ -38,9 +39,14 @@ Thatcher Peskens <thatcher@docker.com> <thatcher@dotcloud.com>
|
||||
Thatcher Peskens <thatcher@docker.com> dhrp <thatcher@gmx.net>
|
||||
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@dotcloud.com>
|
||||
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jp@enix.org>
|
||||
Jérôme Petazzoni <jerome.petazzoni@docker.com> <jerome.petazzoni@gmail.com>
|
||||
Joffrey F <joffrey@docker.com>
|
||||
Joffrey F <joffrey@docker.com> <joffrey@dotcloud.com>
|
||||
Joffrey F <joffrey@docker.com> <f.joffrey@gmail.com>
|
||||
Kir Kolyshkin <kolyshkin@gmail.com> <kir@openvz.org>
|
||||
Kir Kolyshkin <kolyshkin@gmail.com>
|
||||
Lorenzo Fontana <lo@linux.com> <fontanalorenzo@me.com>
|
||||
<mr.wrfly@gmail.com> <wrfly@users.noreply.github.com>
|
||||
Tim Terhorst <mynamewastaken+git@gmail.com>
|
||||
Andy Smith <github@anarkystic.com>
|
||||
<kalessin@kalessin.fr> <louis@dotcloud.com>
|
||||
@@ -93,6 +99,7 @@ Sven Dowideit <SvenDowideit@home.org.au> <¨SvenDowideit@home.org.au¨>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@home.org.au>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <SvenDowideit@users.noreply.github.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> <sven@t440s.home.gateway>
|
||||
<info@fortinux.com> <fortinux@users.noreply.github.com>
|
||||
Akihiro Matsushima <amatsusbit@gmail.com> <amatsus@users.noreply.github.com>
|
||||
<alexl@redhat.com> <alexander.larsson@gmail.com>
|
||||
Alexander Morozov <lk4d4@docker.com> <lk4d4math@gmail.com>
|
||||
@@ -166,6 +173,7 @@ Deshi Xiao <dxiao@redhat.com> <dsxiao@dataman-inc.com>
|
||||
Deshi Xiao <dxiao@redhat.com> <xiaods@gmail.com>
|
||||
Doug Davis <dug@us.ibm.com> <duglin@users.noreply.github.com>
|
||||
Giampaolo Mancini <giampaolo@trampolineup.com>
|
||||
Hakan Özler <hakan.ozler@kodcu.com>
|
||||
K. Heller <pestophagous@gmail.com> <pestophagous@users.noreply.github.com>
|
||||
Jacob Atzen <jacob@jacobatzen.dk> <jatzen@gmail.com>
|
||||
Jeff Nickoloff <jeff.nickoloff@gmail.com> <jeff@allingeek.com>
|
||||
@@ -184,15 +192,21 @@ Madhu Venugopal <madhu@socketplane.io> <madhu@docker.com>
|
||||
Mageee <fangpuyi@foxmail.com> <21521230.zju.edu.cn>
|
||||
Mansi Nahar <mmn4185@rit.edu> <mansinahar@users.noreply.github.com>
|
||||
Mansi Nahar <mmn4185@rit.edu> <mansi.nahar@macbookpro-mansinahar.local>
|
||||
Markus Kortlang <hyp3rdino@googlemail.com> <markus.kortlang@lhsystems.com>
|
||||
Mary Anthony <mary.anthony@docker.com> <mary@docker.com>
|
||||
Mary Anthony <mary.anthony@docker.com> moxiegirl <mary@docker.com>
|
||||
Mary Anthony <mary.anthony@docker.com> <moxieandmore@gmail.com>
|
||||
mattyw <mattyw@me.com> <gh@mattyw.net>
|
||||
Matt Schurenko <matt.schurenko@gmail.com>
|
||||
Matt Williams <mattyw@me.com> <gh@mattyw.net>
|
||||
Matt Williams <mattyw@me.com>
|
||||
Michael Spetsiotis <michael_spets@hotmail.com>
|
||||
Nik Nyby <nikolas@gnu.org> <nnyby@columbia.edu>
|
||||
Ouyang Liduo <oyld0210@163.com>
|
||||
Paul Liljenberg <liljenberg.paul@gmail.com> <letters@paulnotcom.se>
|
||||
Pawel Konczalski <mail@konczalski.de>
|
||||
Philip Alexander Etling <paetling@gmail.com>
|
||||
Peter Jaffe <pjaffe@nevo.com>
|
||||
resouer <resouer@163.com> <resouer@gmail.com>
|
||||
AJ Bowen <aj@gandi.net> soulshake <amy@gandi.net>
|
||||
AJ Bowen <aj@gandi.net> soulshake <amy@gandi.net>
|
||||
AJ Bowen <aj@gandi.net> soulshake <aj@gandi.net>
|
||||
Tibor Vass <teabee89@gmail.com> <tibor@docker.com>
|
||||
Tibor Vass <teabee89@gmail.com> <tiborvass@users.noreply.github.com>
|
||||
@@ -202,7 +216,8 @@ bin liu <liubin0329@users.noreply.github.com> <liubin0329@gmail.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> jhowardmsft <jhoward@microsoft.com>
|
||||
Ankush Agarwal <ankushagarwal11@gmail.com> <ankushagarwal@users.noreply.github.com>
|
||||
Tangi COLIN <tangicolin@gmail.com> tangicolin <tangicolin@gmail.com>
|
||||
Allen Sun <allen.sun@daocloud.io>
|
||||
Allen Sun <allensun.shl@alibaba-inc.com> <allen.sun@daocloud.io>
|
||||
Allen Sun <allensun.shl@alibaba-inc.com> <shlallen1990@gmail.com>
|
||||
Adrien Gallouët <adrien@gallouet.fr> <angt@users.noreply.github.com>
|
||||
<aanm90@gmail.com> <martins@noironetworks.com>
|
||||
Anuj Bahuguna <anujbahuguna.dev@gmail.com>
|
||||
@@ -218,7 +233,9 @@ Daniel, Dao Quang Minh <dqminh@cloudflare.com>
|
||||
Daniel Nephin <dnephin@docker.com> <dnephin@gmail.com>
|
||||
Dave Tucker <dt@docker.com> <dave@dtucker.co.uk>
|
||||
Doug Tangren <d.tangren@gmail.com>
|
||||
Euan Kemp <euan.kemp@coreos.com> <euank@amazon.com>
|
||||
Frederick F. Kautz IV <fkautz@redhat.com> <fkautz@alumni.cmu.edu>
|
||||
Fengtu Wang <wangfengtu@huawei.com> <wangfengtu@huawei.com>
|
||||
Ben Golub <ben.golub@dotcloud.com>
|
||||
Harold Cooper <hrldcpr@gmail.com>
|
||||
hsinko <21551195@zju.edu.cn> <hsinko@users.noreply.github.com>
|
||||
@@ -247,6 +264,7 @@ Soshi Katsuta <soshi.katsuta@gmail.com>
|
||||
<soshi.katsuta@gmail.com> <katsuta_soshi@cyberagent.co.jp>
|
||||
Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
<stefanb@linux.vnet.ibm.com> <stefanb@us.ibm.com>
|
||||
Stefan J. Wernli <swernli@microsoft.com> <swernli@ntdev.microsoft.com>
|
||||
Stephen Day <stephen.day@docker.com>
|
||||
<stephen.day@docker.com> <stevvooe@users.noreply.github.com>
|
||||
Toli Kuznets <toli@docker.com>
|
||||
@@ -292,9 +310,13 @@ Kenfe-Mickaël Laventure <mickael.laventure@gmail.com>
|
||||
<nicholasjamesrusso@gmail.com> <nicholasrusso@icloud.com>
|
||||
Runshen Zhu <runshen.zhu@gmail.com>
|
||||
Tom Barlow <tomwbarlow@gmail.com>
|
||||
Tom Sweeney <tsweeney@redhat.com>
|
||||
Xianlu Bird <xianlubird@gmail.com>
|
||||
Dan Feldman <danf@jfrog.com>
|
||||
Harry Zhang <harryz@hyper.sh> <resouer@gmail.com>
|
||||
Harry Zhang <harryz@hyper.sh> <harryzhang@zju.edu.cn>
|
||||
Harry Zhang <harryz@hyper.sh> <resouer@163.com>
|
||||
Harry Zhang <resouer@163.com>
|
||||
Alex Chen <alexchenunix@gmail.com> alexchen <root@localhost.localdomain>
|
||||
Alex Ellis <alexellis2@gmail.com>
|
||||
Alicia Lauerman <alicia@eta.im> <allydevour@me.com>
|
||||
@@ -307,6 +329,7 @@ Chen Qiu <cheney-90@hotmail.com>
|
||||
Chen Qiu <cheney-90@hotmail.com> <21321229@zju.edu.cn>
|
||||
Chris Dias <cdias@microsoft.com>
|
||||
Chris McKinnel <chris.mckinnel@tangentlabs.co.uk>
|
||||
Christopher Biscardi <biscarch@sketcht.com>
|
||||
CUI Wei <ghostplant@qq.com> cuiwei13 <cuiwei13@pku.edu.cn>
|
||||
Daniel Grunwell <mwgrunny@gmail.com>
|
||||
Daniel J Walsh <dwalsh@redhat.com>
|
||||
@@ -316,6 +339,8 @@ Diego Siqueira <dieg0@live.com>
|
||||
Elan Ruusamäe <glen@pld-linux.org> <glen@delfi.ee>
|
||||
Elan Ruusamäe <glen@pld-linux.org>
|
||||
Eric G. Noriega <enoriega@vizuri.com> <egnoriega@users.noreply.github.com>
|
||||
Eugen Krizo <eugen.krizo@gmail.com>
|
||||
Evgeny Shmarnev <shmarnev@gmail.com>
|
||||
Evelyn Xu <evelynhsu21@gmail.com>
|
||||
Felix Ruess <felix.ruess@gmail.com> <felix.ruess@roboception.de>
|
||||
Gabriel Nicolas Avellaneda <avellaneda.gabriel@gmail.com>
|
||||
@@ -323,6 +348,7 @@ Gang Qiao <qiaohai8866@gmail.com> <1373319223@qq.com>
|
||||
George Kontridze <george@bugsnag.com>
|
||||
Gopikannan Venugopalsamy <gopikannan.venugopalsamy@gmail.com>
|
||||
Gou Rao <gou@portworx.com> <gourao@users.noreply.github.com>
|
||||
Greg Stephens <greg@udon.org>
|
||||
Gustav Sinder <gustav.sinder@gmail.com>
|
||||
Harshal Patil <harshal.patil@in.ibm.com> <harche@users.noreply.github.com>
|
||||
Helen Xie <chenjg@harmonycloud.cn>
|
||||
@@ -340,8 +366,10 @@ Kevin Kern <kaiwentan@harmonycloud.cn>
|
||||
Konstantin Gribov <grossws@gmail.com>
|
||||
Kunal Kushwaha <kushwaha_kunal_v7@lab.ntt.co.jp> <kunal.kushwaha@gmail.com>
|
||||
Lajos Papp <lajos.papp@sequenceiq.com> <lalyos@yahoo.com>
|
||||
Liang Mingqiang <mqliang.zju@gmail.com>
|
||||
Lyn <energylyn@zju.edu.cn>
|
||||
Markan Patel <mpatel678@gmail.com>
|
||||
Matthew Mosesohn <raytrac3r@gmail.com>
|
||||
Michael Käufl <docker@c.michael-kaeufl.de> <michael-k@users.noreply.github.com>
|
||||
Michal Minář <miminar@redhat.com>
|
||||
Michael Hudson-Doyle <michael.hudson@canonical.com> <michael.hudson@linaro.org>
|
||||
@@ -349,6 +377,7 @@ Mike Casas <mkcsas0@gmail.com> <mikecasas@users.noreply.github.com>
|
||||
Milind Chawre <milindchawre@gmail.com>
|
||||
Ma Müller <mueller-ma@users.noreply.github.com>
|
||||
Moorthy RS <rsmoorthy@gmail.com> <rsmoorthy@users.noreply.github.com>
|
||||
Nace Oroz <orkica@gmail.com>
|
||||
Neil Horman <nhorman@tuxdriver.com> <nhorman@hmswarspite.think-freely.org>
|
||||
Pavel Tikhomirov <ptikhomirov@virtuozzo.com> <ptikhomirov@parallels.com>
|
||||
Peter Choi <phkchoi89@gmail.com> <reikani@Peters-MacBook-Pro.local>
|
||||
@@ -358,7 +387,9 @@ Robert Terhaar <rterhaar@atlanticdynamic.com> <robbyt@users.noreply.github.com>
|
||||
Roberto Muñoz Fernández <robertomf@gmail.com> <roberto.munoz.fernandez.contractor@bbva.com>
|
||||
Roman Dudin <katrmr@gmail.com> <decadent@users.noreply.github.com>
|
||||
Sandeep Bansal <sabansal@microsoft.com> <msabansal@microsoft.com>
|
||||
Sandeep Bansal <sabansal@microsoft.com>
|
||||
Sean Lee <seanlee@tw.ibm.com> <scaleoutsean@users.noreply.github.com>
|
||||
Shaun Kaasten <shaunk@gmail.com>
|
||||
Shukui Yang <yangshukui@huawei.com>
|
||||
Srinivasan Srivatsan <srinivasan.srivatsan@hpe.com> <srinsriv@users.noreply.github.com>
|
||||
Stefan S. <tronicum@user.github.com>
|
||||
@@ -367,13 +398,17 @@ Sun Gengze <690388648@qq.com>
|
||||
Tim Bart <tim@fewagainstmany.com>
|
||||
Tim Zju <21651152@zju.edu.cn>
|
||||
Tõnis Tiigi <tonistiigi@gmail.com>
|
||||
Trishna Guha <trishnaguha17@gmail.com>
|
||||
Wayne Song <wsong@docker.com> <wsong@users.noreply.github.com>
|
||||
Wang Guoliang <liangcszzu@163.com>
|
||||
Wang Jie <wangjie5@chinaskycloud.com>
|
||||
Wang Ping <present.wp@icloud.com>
|
||||
Wang Yuexiao <wang.yuexiao@zte.com.cn>
|
||||
Wenjun Tang <tangwj2@lenovo.com> <dodia@163.com>
|
||||
Wewang Xiaorenfine <wang.xiaoren@zte.com.cn>
|
||||
Wei Wu <wuwei4455@gmail.com> cizixs <cizixs@163.com>
|
||||
Xiaoyu Zhang <zhang.xiaoyu33@zte.com.cn>
|
||||
Xuecong Liao <satorulogic@gmail.com>
|
||||
Yamasaki Masahide <masahide.y@gmail.com>
|
||||
Yassine Tijani <yasstij11@gmail.com>
|
||||
Ying Li <ying.li@docker.com> <cyli@twistedmatrix.com>
|
||||
@@ -382,5 +417,6 @@ Yu Chengxia <yuchengxia@huawei.com>
|
||||
Yu Peng <yu.peng36@zte.com.cn>
|
||||
Yu Peng <yu.peng36@zte.com.cn> <yupeng36@zte.com.cn>
|
||||
Yao Zaiyong <yaozaiyong@hotmail.com>
|
||||
ZhangHang <stevezhang2014@gmail.com>
|
||||
Zhenkun Bi <bi.zhenkun@zte.com.cn>
|
||||
Zhu Kunjia <zhu.kunjia@zte.com.cn>
|
||||
|
||||
78
vendor/github.com/docker/docker/AUTHORS
generated
vendored
78
vendor/github.com/docker/docker/AUTHORS
generated
vendored
@@ -40,6 +40,7 @@ Aidan Hobson Sayers <aidanhs@cantab.net>
|
||||
AJ Bowen <aj@gandi.net>
|
||||
Ajey Charantimath <ajey.charantimath@gmail.com>
|
||||
ajneu <ajneu@users.noreply.github.com>
|
||||
Akash Gupta <akagup@microsoft.com>
|
||||
Akihiro Matsushima <amatsusbit@gmail.com>
|
||||
Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
|
||||
Akira Koyasu <mail@akirakoyasu.net>
|
||||
@@ -53,7 +54,7 @@ Albert Zhang <zhgwenming@gmail.com>
|
||||
Aleksa Sarai <asarai@suse.de>
|
||||
Aleksandrs Fadins <aleks@s-ko.net>
|
||||
Alena Prokharchyk <alena@rancher.com>
|
||||
Alessandro Boch <aboch@docker.com>
|
||||
Alessandro Boch <aboch@tetrationanalytics.com>
|
||||
Alessio Biancalana <dottorblaster@gmail.com>
|
||||
Alex Chan <alex@alexwlchan.net>
|
||||
Alex Chen <alexchenunix@gmail.com>
|
||||
@@ -83,7 +84,7 @@ Ali Dehghani <ali.dehghani.g@gmail.com>
|
||||
Alicia Lauerman <alicia@eta.im>
|
||||
Alihan Demir <alihan_6153@hotmail.com>
|
||||
Allen Madsen <blatyo@gmail.com>
|
||||
Allen Sun <allen.sun@daocloud.io>
|
||||
Allen Sun <allensun.shl@alibaba-inc.com>
|
||||
almoehi <almoehi@users.noreply.github.com>
|
||||
Alvaro Saurin <alvaro.saurin@gmail.com>
|
||||
Alvin Deng <alvin.q.deng@utexas.edu>
|
||||
@@ -114,6 +115,7 @@ Andrew Duckworth <grillopress@gmail.com>
|
||||
Andrew France <andrew@avito.co.uk>
|
||||
Andrew Gerrand <adg@golang.org>
|
||||
Andrew Guenther <guenther.andrew.j@gmail.com>
|
||||
Andrew He <he.andrew.mail@gmail.com>
|
||||
Andrew Hsu <andrewhsu@docker.com>
|
||||
Andrew Kuklewicz <kookster@gmail.com>
|
||||
Andrew Macgregor <andrew.macgregor@agworld.com.au>
|
||||
@@ -190,6 +192,7 @@ Benjamin Atkin <ben@benatkin.com>
|
||||
Benjamin Boudreau <boudreau.benjamin@gmail.com>
|
||||
Benoit Chesneau <bchesneau@gmail.com>
|
||||
Bernerd Schaefer <bj.schaefer@gmail.com>
|
||||
Bernhard M. Wiedemann <bwiedemann@suse.de>
|
||||
Bert Goethals <bert@bertg.be>
|
||||
Bharath Thiruveedula <bharath_ves@hotmail.com>
|
||||
Bhiraj Butala <abhiraj.butala@gmail.com>
|
||||
@@ -252,6 +255,7 @@ Cao Weiwei <cao.weiwei30@zte.com.cn>
|
||||
Carl Henrik Lunde <chlunde@ping.uio.no>
|
||||
Carl Loa Odin <carlodin@gmail.com>
|
||||
Carl X. Su <bcbcarl@gmail.com>
|
||||
Carlo Mion <mion00@gmail.com>
|
||||
Carlos Alexandro Becker <caarlos0@gmail.com>
|
||||
Carlos Sanchez <carlos@apache.org>
|
||||
Carol Fager-Higgins <carol.fager-higgins@docker.com>
|
||||
@@ -307,9 +311,11 @@ Christian Persson <saser@live.se>
|
||||
Christian Rotzoll <ch.rotzoll@gmail.com>
|
||||
Christian Simon <simon@swine.de>
|
||||
Christian Stefanescu <st.chris@gmail.com>
|
||||
ChristoperBiscardi <biscarch@sketcht.com>
|
||||
Christophe Mehay <cmehay@online.net>
|
||||
Christophe Troestler <christophe.Troestler@umons.ac.be>
|
||||
Christophe Vidal <kriss@krizalys.com>
|
||||
Christopher Biscardi <biscarch@sketcht.com>
|
||||
Christopher Crone <christopher.crone@docker.com>
|
||||
Christopher Currie <codemonkey+github@gmail.com>
|
||||
Christopher Jones <tophj@linux.vnet.ibm.com>
|
||||
Christopher Latham <sudosurootdev@gmail.com>
|
||||
@@ -473,6 +479,7 @@ Doron Podoleanu <doronp@il.ibm.com>
|
||||
Doug Davis <dug@us.ibm.com>
|
||||
Doug MacEachern <dougm@vmware.com>
|
||||
Doug Tangren <d.tangren@gmail.com>
|
||||
Douglas Curtis <dougcurtis1@gmail.com>
|
||||
Dr Nic Williams <drnicwilliams@gmail.com>
|
||||
dragon788 <dragon788@users.noreply.github.com>
|
||||
Dražen Lučanin <kermit666@gmail.com>
|
||||
@@ -486,6 +493,7 @@ Eivin Giske Skaaren <eivinsn@axis.com>
|
||||
Eivind Uggedal <eivind@uggedal.com>
|
||||
Elan Ruusamäe <glen@pld-linux.org>
|
||||
Elena Morozova <lelenanam@gmail.com>
|
||||
Eli Uriegas <eli.uriegas@docker.com>
|
||||
Elias Faxö <elias.faxo@tre.se>
|
||||
Elias Probst <mail@eliasprobst.eu>
|
||||
Elijah Zupancic <elijah@zupancic.name>
|
||||
@@ -520,10 +528,9 @@ Erik St. Martin <alakriti@gmail.com>
|
||||
Erik Weathers <erikdw@gmail.com>
|
||||
Erno Hopearuoho <erno.hopearuoho@gmail.com>
|
||||
Erwin van der Koogh <info@erronis.nl>
|
||||
Euan <euank@amazon.com>
|
||||
Euan Kemp <euan.kemp@coreos.com>
|
||||
Eugen Krizo <eugen.krizo@gmail.com>
|
||||
Eugene Yakubovich <eugene.yakubovich@coreos.com>
|
||||
eugenkrizo <eugen.krizo@gmail.com>
|
||||
evalle <shmarnev@gmail.com>
|
||||
Evan Allrich <evan@unguku.com>
|
||||
Evan Carmi <carmi@users.noreply.github.com>
|
||||
Evan Hazlett <ehazlett@users.noreply.github.com>
|
||||
@@ -533,6 +540,7 @@ Evan Phoenix <evan@fallingsnow.net>
|
||||
Evan Wies <evan@neomantra.net>
|
||||
Evelyn Xu <evelynhsu21@gmail.com>
|
||||
Everett Toews <everett.toews@rackspace.com>
|
||||
Evgeny Shmarnev <shmarnev@gmail.com>
|
||||
Evgeny Vereshchagin <evvers@ya.ru>
|
||||
Ewa Czechowska <ewa@ai-traders.com>
|
||||
Eystein Måløy Stenberg <eystein.maloy.stenberg@cfengine.com>
|
||||
@@ -573,10 +581,12 @@ FLGMwt <ryan.stelly@live.com>
|
||||
Florian <FWirtz@users.noreply.github.com>
|
||||
Florian Klein <florian.klein@free.fr>
|
||||
Florian Maier <marsmensch@users.noreply.github.com>
|
||||
Florian Noeding <noeding@adobe.com>
|
||||
Florian Weingarten <flo@hackvalue.de>
|
||||
Florin Asavoaie <florin.asavoaie@gmail.com>
|
||||
Florin Patan <florinpatan@gmail.com>
|
||||
fonglh <fonglh@gmail.com>
|
||||
fortinux <fortinux@users.noreply.github.com>
|
||||
fortinux <info@fortinux.com>
|
||||
Foysal Iqbal <foysal.iqbal.fb@gmail.com>
|
||||
Francesc Campoy <campoy@google.com>
|
||||
Francis Chuang <francis.chuang@boostport.com>
|
||||
@@ -638,6 +648,7 @@ Grant Reaber <grant.reaber@gmail.com>
|
||||
Graydon Hoare <graydon@pobox.com>
|
||||
Greg Fausak <greg@tacodata.com>
|
||||
Greg Pflaum <gpflaum@users.noreply.github.com>
|
||||
Greg Stephens <greg@udon.org>
|
||||
Greg Thornton <xdissent@me.com>
|
||||
Grzegorz Jaśkiewicz <gj.jaskiewicz@gmail.com>
|
||||
Guilhem Lettron <guilhem+github@lettron.fr>
|
||||
@@ -650,6 +661,7 @@ Guruprasad <lgp171188@gmail.com>
|
||||
Gustav Sinder <gustav.sinder@gmail.com>
|
||||
gwx296173 <gaojing3@huawei.com>
|
||||
Günter Zöchbauer <guenter@gzoechbauer.com>
|
||||
Hakan Özler <hakan.ozler@kodcu.com>
|
||||
Hans Kristian Flaatten <hans@starefossen.com>
|
||||
Hans Rødtang <hansrodtang@gmail.com>
|
||||
Hao Shu Wei <haosw@cn.ibm.com>
|
||||
@@ -681,8 +693,8 @@ Hunter Blanks <hunter@twilio.com>
|
||||
huqun <huqun@zju.edu.cn>
|
||||
Huu Nguyen <huu@prismskylabs.com>
|
||||
hyeongkyu.lee <hyeongkyu.lee@navercorp.com>
|
||||
hyp3rdino <markus.kortlang@lhsystems.com>
|
||||
Hyzhou Zhy <hyzhou.zhy@alibaba-inc.com>
|
||||
Iago López Galeiras <iago@kinvolk.io>
|
||||
Ian Babrou <ibobrik@gmail.com>
|
||||
Ian Bishop <ianbishop@pace7.com>
|
||||
Ian Bull <irbull@gmail.com>
|
||||
@@ -962,7 +974,7 @@ kies <lleelm@gmail.com>
|
||||
Kim BKC Carlbacker <kim.carlbacker@gmail.com>
|
||||
Kim Eik <kim@heldig.org>
|
||||
Kimbro Staken <kstaken@kstaken.com>
|
||||
Kir Kolyshkin <kir@openvz.org>
|
||||
Kir Kolyshkin <kolyshkin@gmail.com>
|
||||
Kiran Gangadharan <kiran.daredevil@gmail.com>
|
||||
Kirill Kolyshkin <kolyshkin@users.noreply.github.com>
|
||||
Kirill SIbirev <l0kix2@gmail.com>
|
||||
@@ -1036,7 +1048,7 @@ Lloyd Dewolf <foolswisdom@gmail.com>
|
||||
Lokesh Mandvekar <lsm5@fedoraproject.org>
|
||||
longliqiang88 <394564827@qq.com>
|
||||
Lorenz Leutgeb <lorenz.leutgeb@gmail.com>
|
||||
Lorenzo Fontana <fontanalorenzo@me.com>
|
||||
Lorenzo Fontana <lo@linux.com>
|
||||
Louis Opter <kalessin@kalessin.fr>
|
||||
Luca Favatella <lucafavatella@users.noreply.github.com>
|
||||
Luca Marturana <lucamarturana@gmail.com>
|
||||
@@ -1074,6 +1086,7 @@ mapk0y <mapk0y@gmail.com>
|
||||
Marc Abramowitz <marc@marc-abramowitz.com>
|
||||
Marc Kuo <kuomarc2@gmail.com>
|
||||
Marc Tamsky <mtamsky@gmail.com>
|
||||
Marcel Edmund Franke <marcel.edmund.franke@gmail.com>
|
||||
Marcelo Salazar <chelosalazar@gmail.com>
|
||||
Marco Hennings <marco.hennings@freiheit.com>
|
||||
Marcus Cobden <mcobden@cisco.com>
|
||||
@@ -1097,6 +1110,7 @@ Markan Patel <mpatel678@gmail.com>
|
||||
Marko Mikulicic <mmikulicic@gmail.com>
|
||||
Marko Tibold <marko@tibold.nl>
|
||||
Markus Fix <lispmeister@gmail.com>
|
||||
Markus Kortlang <hyp3rdino@googlemail.com>
|
||||
Martijn Dwars <ikben@martijndwars.nl>
|
||||
Martijn van Oosterhout <kleptog@svana.org>
|
||||
Martin Honermeyer <maze@strahlungsfrei.de>
|
||||
@@ -1120,17 +1134,18 @@ Matt McCormick <matt.mccormick@kitware.com>
|
||||
Matt Moore <mattmoor@google.com>
|
||||
Matt Richardson <matt@redgumtech.com.au>
|
||||
Matt Robenolt <matt@ydekproductions.com>
|
||||
Matt Schurenko <matt.schurenko@gmail.com>
|
||||
Matt Williams <mattyw@me.com>
|
||||
Matthew Heon <mheon@redhat.com>
|
||||
Matthew Lapworth <matthewl@bit-shift.net>
|
||||
Matthew Mayer <matthewkmayer@gmail.com>
|
||||
Matthew Mosesohn <raytrac3r@gmail.com>
|
||||
Matthew Mueller <mattmuelle@gmail.com>
|
||||
Matthew Riley <mattdr@google.com>
|
||||
Matthias Klumpp <matthias@tenstral.net>
|
||||
Matthias Kühnle <git.nivoc@neverbox.com>
|
||||
Matthias Rampke <mr@soundcloud.com>
|
||||
Matthieu Hauglustaine <matt.hauglustaine@gmail.com>
|
||||
mattymo <raytrac3r@gmail.com>
|
||||
mattyw <mattyw@me.com>
|
||||
Mauricio Garavaglia <mauricio@medallia.com>
|
||||
mauriyouth <mauriyouth@gmail.com>
|
||||
Max Shytikov <mshytikov@gmail.com>
|
||||
@@ -1215,16 +1230,14 @@ Morgy93 <thomas@ulfertsprygoda.de>
|
||||
Morten Siebuhr <sbhr@sbhr.dk>
|
||||
Morton Fox <github@qslw.com>
|
||||
Moysés Borges <moysesb@gmail.com>
|
||||
mqliang <mqliang.zju@gmail.com>
|
||||
mrfly <mr.wrfly@gmail.com>
|
||||
Mrunal Patel <mrunalp@gmail.com>
|
||||
msabansal <sabansal@microsoft.com>
|
||||
mschurenko <matt.schurenko@gmail.com>
|
||||
Muayyad Alsadi <alsadi@gmail.com>
|
||||
muge <stevezhang2014@gmail.com>
|
||||
Mustafa Akın <mustafa91@gmail.com>
|
||||
Muthukumar R <muthur@gmail.com>
|
||||
Máximo Cuadros <mcuadros@gmail.com>
|
||||
Médi-Rémi Hashim <medimatrix@users.noreply.github.com>
|
||||
Nace Oroz <orkica@gmail.com>
|
||||
Nahum Shalman <nshalman@omniti.com>
|
||||
Nakul Pathak <nakulpathak3@hotmail.com>
|
||||
Nalin Dahyabhai <nalin@redhat.com>
|
||||
@@ -1292,16 +1305,13 @@ Oliver Neal <ItsVeryWindy@users.noreply.github.com>
|
||||
Olivier Gambier <dmp42@users.noreply.github.com>
|
||||
Olle Jonsson <olle.jonsson@gmail.com>
|
||||
Oriol Francès <oriolfa@gmail.com>
|
||||
orkaa <orkica@gmail.com>
|
||||
Oskar Niburski <oskarniburski@gmail.com>
|
||||
Otto Kekäläinen <otto@seravo.fi>
|
||||
Ouyang Liduo <oyld0210@163.com>
|
||||
Ovidio Mallo <ovidio.mallo@gmail.com>
|
||||
oyld <oyld0210@163.com>
|
||||
ozlerhakan <hakan.ozler@kodcu.com>
|
||||
paetling <paetling@gmail.com>
|
||||
pandrew <letters@paulnotcom.se>
|
||||
panticz <mail@konczalski.de>
|
||||
Panagiotis Moustafellos <pmoust@elastic.co>
|
||||
Paolo G. Giarrusso <p.giarrusso@gmail.com>
|
||||
Pascal <pascalgn@users.noreply.github.com>
|
||||
Pascal Borreli <pascal@borreli.com>
|
||||
Pascal Hartig <phartig@rdrei.net>
|
||||
Patrick Böänziger <patrick.baenziger@bsi-software.com>
|
||||
@@ -1330,6 +1340,7 @@ Pavel Sutyrin <pavel.sutyrin@gmail.com>
|
||||
Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
|
||||
Pavlos Ratis <dastergon@gentoo.org>
|
||||
Pavol Vargovcik <pallly.vargovcik@gmail.com>
|
||||
Pawel Konczalski <mail@konczalski.de>
|
||||
Peeyush Gupta <gpeeyush@linux.vnet.ibm.com>
|
||||
Peggy Li <peggyli.224@gmail.com>
|
||||
Pei Su <sillyousu@gmail.com>
|
||||
@@ -1354,6 +1365,7 @@ Petr Švihlík <svihlik.petr@gmail.com>
|
||||
Phil <underscorephil@gmail.com>
|
||||
Phil Estes <estesp@linux.vnet.ibm.com>
|
||||
Phil Spitler <pspitler@gmail.com>
|
||||
Philip Alexander Etling <paetling@gmail.com>
|
||||
Philip Monroe <phil@philmonroe.com>
|
||||
Philipp Gillé <philipp.gille@gmail.com>
|
||||
Philipp Wahala <philipp.wahala@gmail.com>
|
||||
@@ -1372,6 +1384,7 @@ pixelistik <pixelistik@users.noreply.github.com>
|
||||
Porjo <porjo38@yahoo.com.au>
|
||||
Poul Kjeldager Sørensen <pks@s-innovations.net>
|
||||
Pradeep Chhetri <pradeep@indix.com>
|
||||
Pradip Dhara <pradipd@microsoft.com>
|
||||
Prasanna Gautam <prasannagautam@gmail.com>
|
||||
Pratik Karki <prertik@outlook.com>
|
||||
Prayag Verma <prayag.verma@gmail.com>
|
||||
@@ -1407,8 +1420,7 @@ Regan McCooey <rmccooey27@aol.com>
|
||||
Remi Rampin <remirampin@gmail.com>
|
||||
Remy Suen <remy.suen@gmail.com>
|
||||
Renato Riccieri Santos Zannon <renato.riccieri@gmail.com>
|
||||
resouer <resouer@163.com>
|
||||
rgstephens <greg@udon.org>
|
||||
Renaud Gaubert <rgaubert@nvidia.com>
|
||||
Rhys Hiltner <rhys@twitch.tv>
|
||||
Ricardo N Feliciano <FelicianoTech@gmail.com>
|
||||
Rich Moyse <rich@moyse.us>
|
||||
@@ -1514,8 +1526,8 @@ Sankar சங்கர் <sankar.curiosity@gmail.com>
|
||||
Sanket Saurav <sanketsaurav@gmail.com>
|
||||
Santhosh Manohar <santhosh@docker.com>
|
||||
sapphiredev <se.imas.kr@gmail.com>
|
||||
Sascha Andres <sascha.andres@outlook.com>
|
||||
Satnam Singh <satnam@raintown.org>
|
||||
satoru <satorulogic@gmail.com>
|
||||
Satoshi Amemiya <satoshi_amemiya@voyagegroup.com>
|
||||
Satoshi Tagomori <tagomoris@gmail.com>
|
||||
Scott Bessler <scottbessler@gmail.com>
|
||||
@@ -1545,6 +1557,7 @@ Serhat Gülçiçek <serhat25@gmail.com>
|
||||
Sevki Hasirci <s@sevki.org>
|
||||
Shane Canon <scanon@lbl.gov>
|
||||
Shane da Silva <shane@dasilva.io>
|
||||
Shaun Kaasten <shaunk@gmail.com>
|
||||
shaunol <shaunol@gmail.com>
|
||||
Shawn Landden <shawn@churchofgit.com>
|
||||
Shawn Siefkas <shawn.siefkas@meredith.com>
|
||||
@@ -1572,9 +1585,9 @@ Simon Ferquel <simon.ferquel@docker.com>
|
||||
Simon Leinen <simon.leinen@gmail.com>
|
||||
Simon Menke <simon.menke@gmail.com>
|
||||
Simon Taranto <simon.taranto@gmail.com>
|
||||
Simon Vikstrom <pullreq@devsn.se>
|
||||
Sindhu S <sindhus@live.in>
|
||||
Sjoerd Langkemper <sjoerd-github@linuxonly.nl>
|
||||
skaasten <shaunk@gmail.com>
|
||||
Solganik Alexander <solganik@gmail.com>
|
||||
Solomon Hykes <solomon@docker.com>
|
||||
Song Gao <song@gao.io>
|
||||
@@ -1621,6 +1634,7 @@ Swapnil Daingade <swapnil.daingade@gmail.com>
|
||||
Sylvain Baubeau <sbaubeau@redhat.com>
|
||||
Sylvain Bellemare <sylvain@ascribe.io>
|
||||
Sébastien <sebastien@yoozio.com>
|
||||
Sébastien HOUZÉ <cto@verylastroom.com>
|
||||
Sébastien Luttringer <seblu@seblu.net>
|
||||
Sébastien Stormacq <sebsto@users.noreply.github.com>
|
||||
Tabakhase <mail@tabakhase.com>
|
||||
@@ -1656,6 +1670,7 @@ Thomas Sjögren <konstruktoid@users.noreply.github.com>
|
||||
Thomas Swift <tgs242@gmail.com>
|
||||
Thomas Tanaka <thomas.tanaka@oracle.com>
|
||||
Thomas Texier <sharkone@en-mousse.org>
|
||||
Ti Zhou <tizhou1986@gmail.com>
|
||||
Tianon Gravi <admwiggin@gmail.com>
|
||||
Tianyi Wang <capkurmagati@gmail.com>
|
||||
Tibor Vass <teabee89@gmail.com>
|
||||
@@ -1696,6 +1711,7 @@ Tom Fotherby <tom+github@peopleperhour.com>
|
||||
Tom Howe <tom.howe@enstratius.com>
|
||||
Tom Hulihan <hulihan.tom159@gmail.com>
|
||||
Tom Maaswinkel <tom.maaswinkel@12wiki.eu>
|
||||
Tom Sweeney <tsweeney@redhat.com>
|
||||
Tom Wilkie <tom.wilkie@gmail.com>
|
||||
Tom X. Tobin <tomxtobin@tomxtobin.com>
|
||||
Tomas Tomecek <ttomecek@redhat.com>
|
||||
@@ -1720,9 +1736,10 @@ Trent Ogren <tedwardo2@gmail.com>
|
||||
Trevor <trevinwoodstock@gmail.com>
|
||||
Trevor Pounds <trevor.pounds@gmail.com>
|
||||
Trevor Sullivan <pcgeek86@gmail.com>
|
||||
trishnaguha <trishnaguha17@gmail.com>
|
||||
Trishna Guha <trishnaguha17@gmail.com>
|
||||
Tristan Carel <tristan@cogniteev.com>
|
||||
Troy Denton <trdenton@gmail.com>
|
||||
Tycho Andersen <tycho@docker.com>
|
||||
Tyler Brock <tyler.brock@gmail.com>
|
||||
Tzu-Jung Lee <roylee17@gmail.com>
|
||||
uhayate <uhayate.gong@daocloud.io>
|
||||
@@ -1771,6 +1788,7 @@ waitingkuo <waitingkuo0527@gmail.com>
|
||||
Walter Leibbrandt <github@wrl.co.za>
|
||||
Walter Stanish <walter@pratyeka.org>
|
||||
WANG Chao <wcwxyz@gmail.com>
|
||||
Wang Guoliang <liangcszzu@163.com>
|
||||
Wang Jie <wangjie5@chinaskycloud.com>
|
||||
Wang Long <long.wanglong@huawei.com>
|
||||
Wang Ping <present.wp@icloud.com>
|
||||
@@ -1786,6 +1804,7 @@ weiyan <weiyan3@huawei.com>
|
||||
Weiyang Zhu <cnresonant@gmail.com>
|
||||
Wen Cheng Ma <wenchma@cn.ibm.com>
|
||||
Wendel Fleming <wfleming@usc.edu>
|
||||
Wenjun Tang <tangwj2@lenovo.com>
|
||||
Wenkai Yin <yinw@vmware.com>
|
||||
Wentao Zhang <zhangwentao234@huawei.com>
|
||||
Wenxuan Zhao <viz@linux.com>
|
||||
@@ -1819,6 +1838,7 @@ Xinbo Weng <xihuanbo_0521@zju.edu.cn>
|
||||
Xinzi Zhou <imdreamrunner@gmail.com>
|
||||
Xiuming Chen <cc@cxm.cc>
|
||||
xlgao-zju <xlgao@zju.edu.cn>
|
||||
Xuecong Liao <satorulogic@gmail.com>
|
||||
xuzhaokui <cynicholas@gmail.com>
|
||||
Yahya <ya7yaz@gmail.com>
|
||||
YAMADA Tsuyoshi <tyamada@minimum2scp.org>
|
||||
@@ -1826,6 +1846,7 @@ Yamasaki Masahide <masahide.y@gmail.com>
|
||||
Yan Feng <yanfeng2@huawei.com>
|
||||
Yang Bai <hamo.by@gmail.com>
|
||||
Yang Pengfei <yangpengfei4@huawei.com>
|
||||
yangchenliang <yangchenliang@huawei.com>
|
||||
Yanqiang Miao <miao.yanqiang@zte.com.cn>
|
||||
Yao Zaiyong <yaozaiyong@hotmail.com>
|
||||
Yassine Tijani <yasstij11@gmail.com>
|
||||
@@ -1844,8 +1865,10 @@ Youcef YEKHLEF <yyekhlef@gmail.com>
|
||||
Yu Changchun <yuchangchun1@huawei.com>
|
||||
Yu Chengxia <yuchengxia@huawei.com>
|
||||
Yu Peng <yu.peng36@zte.com.cn>
|
||||
Yu-Ju Hong <yjhong@google.com>
|
||||
Yuan Sun <sunyuan3@huawei.com>
|
||||
Yuanhong Peng <pengyuanhong@huawei.com>
|
||||
Yuhao Fang <fangyuhao@gmail.com>
|
||||
Yunxiang Huang <hyxqshk@vip.qq.com>
|
||||
Yurii Rashkovskii <yrashk@gmail.com>
|
||||
yuzou <zouyu7@huawei.com>
|
||||
@@ -1860,6 +1883,7 @@ Zen Lin(Zhinan Lin) <linzhinan@huawei.com>
|
||||
Zhang Kun <zkazure@gmail.com>
|
||||
Zhang Wei <zhangwei555@huawei.com>
|
||||
Zhang Wentao <zhangwentao234@huawei.com>
|
||||
ZhangHang <stevezhang2014@gmail.com>
|
||||
zhangxianwei <xianwei.zw@alibaba-inc.com>
|
||||
Zhenan Ye <21551168@zju.edu.cn>
|
||||
zhenghenghuo <zhenghenghuo@zju.edu.cn>
|
||||
|
||||
75
vendor/github.com/docker/docker/CONTRIBUTING.md
generated
vendored
75
vendor/github.com/docker/docker/CONTRIBUTING.md
generated
vendored
@@ -1,8 +1,8 @@
|
||||
# Contributing to Docker
|
||||
# Contribute to the Moby Project
|
||||
|
||||
Want to hack on Docker? Awesome! We have a contributor's guide that explains
|
||||
[setting up a Docker development environment and the contribution
|
||||
process](https://docs.docker.com/opensource/project/who-written-for/).
|
||||
Want to hack on the Moby Project? Awesome! We have a contributor's guide that explains
|
||||
[setting up a development environment and the contribution
|
||||
process](docs/contributing/).
|
||||
|
||||
[](https://docs.docker.com/opensource/project/who-written-for/)
|
||||
|
||||
@@ -21,14 +21,14 @@ start participating.
|
||||
|
||||
## Reporting security issues
|
||||
|
||||
The Docker maintainers take security seriously. If you discover a security
|
||||
The Moby maintainers take security seriously. If you discover a security
|
||||
issue, please bring it to their attention right away!
|
||||
|
||||
Please **DO NOT** file a public issue, instead send your report privately to
|
||||
[security@docker.com](mailto:security@docker.com).
|
||||
|
||||
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||
We also like to send gifts—if you're into Docker schwag, make sure to let
|
||||
We also like to send gifts—if you're into schwag, make sure to let
|
||||
us know. We currently do not offer a paid security bounty program, but are not
|
||||
ruling it out in the future.
|
||||
|
||||
@@ -83,11 +83,7 @@ contributions, see [the advanced contribution
|
||||
section](https://docs.docker.com/opensource/workflow/advanced-contributing/) in
|
||||
the contributors guide.
|
||||
|
||||
We try hard to keep Docker lean and focused. Docker can't do everything for
|
||||
everybody. This means that we might decide against incorporating a new feature.
|
||||
However, there might be a way to implement that feature *on top of* Docker.
|
||||
|
||||
### Talking to other Docker users and contributors
|
||||
### Connect with other Moby Project contributors
|
||||
|
||||
<table class="tg">
|
||||
<col width="45%">
|
||||
@@ -96,52 +92,29 @@ However, there might be a way to implement that feature *on top of* Docker.
|
||||
<td>Forums</td>
|
||||
<td>
|
||||
A public forum for users to discuss questions and explore current design patterns and
|
||||
best practices about Docker and related projects in the Docker Ecosystem. To participate,
|
||||
just log in with your Docker Hub account on <a href="https://forums.docker.com" target="_blank">https://forums.docker.com</a>.
|
||||
best practices about all the Moby projects. To participate, log in with your Github
|
||||
account or create an account at <a href="https://forums.mobyproject.org" target="_blank">https://forums.mobyproject.org</a>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Internet Relay Chat (IRC)</td>
|
||||
<td>Slack</td>
|
||||
<td>
|
||||
<p>
|
||||
IRC a direct line to our most knowledgeable Docker users; we have
|
||||
both the <code>#docker</code> and <code>#docker-dev</code> group on
|
||||
<strong>irc.freenode.net</strong>.
|
||||
IRC is a rich chat protocol but it can overwhelm new users. You can search
|
||||
<a href="https://botbot.me/freenode/docker/#" target="_blank">our chat archives</a>.
|
||||
Register for the Docker Community Slack at
|
||||
<a href="https://community.docker.com/registrations/groups/4316" target="_blank">https://community.docker.com/registrations/groups/4316</a>.
|
||||
We use the #moby-project channel for general discussion, and there are seperate channels for other Moby projects such as #containerd.
|
||||
Archives are available at <a href="https://dockercommunity.slackarchive.io/" target="_blank">https://dockercommunity.slackarchive.io/</a>.
|
||||
</p>
|
||||
<p>
|
||||
Read our <a href="https://docs.docker.com/opensource/get-help/#irc-quickstart" target="_blank">IRC quickstart guide</a>
|
||||
for an easy way to get started.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Google Group</td>
|
||||
<td>
|
||||
The <a href="https://groups.google.com/forum/#!forum/docker-dev" target="_blank">docker-dev</a>
|
||||
group is for contributors and other people contributing to the Docker project.
|
||||
You can join them without a google account by sending an email to
|
||||
<a href="mailto:docker-dev+subscribe@googlegroups.com">docker-dev+subscribe@googlegroups.com</a>.
|
||||
After receiving the join-request message, you can simply reply to that to confirm the subscription.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Twitter</td>
|
||||
<td>
|
||||
You can follow <a href="https://twitter.com/docker/" target="_blank">Docker's Twitter feed</a>
|
||||
You can follow <a href="https://twitter.com/moby/" target="_blank">Moby Project Twitter feed</a>
|
||||
to get updates on our products. You can also tweet us questions or just
|
||||
share blogs or stories.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Stack Overflow</td>
|
||||
<td>
|
||||
Stack Overflow has thousands of Docker questions listed. We regularly
|
||||
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
|
||||
and so do many other knowledgeable Docker users.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -159,7 +132,7 @@ Submit tests for your changes. See [TESTING.md](./TESTING.md) for details.
|
||||
|
||||
If your changes need integration tests, write them against the API. The `cli`
|
||||
integration tests are slowly either migrated to API tests or moved away as unit
|
||||
tests in `docker/cli` and end-to-end tests for docker.
|
||||
tests in `docker/cli` and end-to-end tests for Docker.
|
||||
|
||||
Update the documentation when creating or modifying features. Test your
|
||||
documentation changes for clarity, concision, and correctness, as well as a
|
||||
@@ -266,15 +239,11 @@ Please see the [Coding Style](#coding-style) for further guidelines.
|
||||
|
||||
### Merge approval
|
||||
|
||||
Docker maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
||||
indicate acceptance.
|
||||
Moby maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
||||
indicate acceptance, or use the Github review approval feature.
|
||||
|
||||
A change requires LGTMs from an absolute majority of the maintainers of each
|
||||
component affected. For example, if a change affects `docs/` and `registry/`, it
|
||||
needs an absolute majority from the maintainers of `docs/` AND, separately, an
|
||||
absolute majority of the maintainers of `registry/`.
|
||||
|
||||
For more details, see the [MAINTAINERS](MAINTAINERS) page.
|
||||
For an explanation of the review and approval process see the
|
||||
[REVIEWING](project/REVIEWING.md) page.
|
||||
|
||||
### Sign your work
|
||||
|
||||
@@ -342,9 +311,9 @@ Don't forget: being a maintainer is a time investment. Make sure you
|
||||
will have time to make yourself available. You don't have to be a
|
||||
maintainer to make a difference on the project!
|
||||
|
||||
## Docker community guidelines
|
||||
## Moby community guidelines
|
||||
|
||||
We want to keep the Docker community awesome, growing and collaborative. We need
|
||||
We want to keep the Moby community awesome, growing and collaborative. We need
|
||||
your help to keep it that way. To help with this we've come up with some general
|
||||
guidelines for the community as a whole:
|
||||
|
||||
|
||||
53
vendor/github.com/docker/docker/Dockerfile
generated
vendored
53
vendor/github.com/docker/docker/Dockerfile
generated
vendored
@@ -23,7 +23,7 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM debian:jessie
|
||||
FROM debian:stretch
|
||||
|
||||
# allow replacing httpredir or deb mirror
|
||||
ARG APT_MIRROR=deb.debian.org
|
||||
@@ -51,21 +51,29 @@ RUN apt-get update && apt-get install -y \
|
||||
less \
|
||||
libapparmor-dev \
|
||||
libcap-dev \
|
||||
libdevmapper-dev \
|
||||
libnl-3-dev \
|
||||
libprotobuf-c0-dev \
|
||||
libprotobuf-dev \
|
||||
libsystemd-journal-dev \
|
||||
libseccomp-dev \
|
||||
libsystemd-dev \
|
||||
libtool \
|
||||
libudev-dev \
|
||||
mercurial \
|
||||
net-tools \
|
||||
pkg-config \
|
||||
protobuf-compiler \
|
||||
protobuf-c-compiler \
|
||||
python-backports.ssl-match-hostname \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-requests \
|
||||
python-setuptools \
|
||||
python-websocket \
|
||||
python-wheel \
|
||||
tar \
|
||||
thin-provisioning-tools \
|
||||
vim \
|
||||
vim-common \
|
||||
xfsprogs \
|
||||
@@ -73,42 +81,12 @@ RUN apt-get update && apt-get install -y \
|
||||
--no-install-recommends \
|
||||
&& pip install awscli==1.10.15
|
||||
|
||||
# Get lvm2 sources to build statically linked devmapper library
|
||||
ENV LVM2_VERSION 2.02.173
|
||||
RUN mkdir -p /usr/local/lvm2 \
|
||||
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||
|
||||
# Compile and install (only the needed library)
|
||||
RUN cd /usr/local/lvm2 \
|
||||
&& ./configure \
|
||||
--build="$(gcc -print-multiarch)" \
|
||||
--enable-static_link \
|
||||
--enable-pkgconfig \
|
||||
&& make -C include \
|
||||
&& make -C libdm install_device-mapper
|
||||
|
||||
# Install seccomp: the version shipped upstream is too old
|
||||
ENV SECCOMP_VERSION 2.3.2
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
# Install Go
|
||||
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
|
||||
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||
# with a heads-up.
|
||||
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
|
||||
ENV GO_VERSION 1.8.3
|
||||
ENV GO_VERSION 1.8.5
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
|
||||
@@ -155,11 +133,8 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef
|
||||
ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a
|
||||
# To run integration tests docker-pycreds is required.
|
||||
# Before running the integration tests conftest.py is
|
||||
# loaded which results in loads auth.py that
|
||||
# imports the docker-pycreds module.
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
@@ -214,5 +189,9 @@ RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
# Options for hack/validate/gometalinter
|
||||
ENV GOMETALINTER_OPTS="--deadline 2m"
|
||||
|
||||
# Upload docker source
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
|
||||
|
||||
88
vendor/github.com/docker/docker/Dockerfile.aarch64
generated
vendored
88
vendor/github.com/docker/docker/Dockerfile.aarch64
generated
vendored
@@ -15,87 +15,69 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM aarch64/ubuntu:xenial
|
||||
FROM arm64v8/debian:stretch
|
||||
|
||||
# allow replacing httpredir or deb mirror
|
||||
ARG APT_MIRROR=deb.debian.org
|
||||
RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apparmor \
|
||||
apt-utils \
|
||||
aufs-tools \
|
||||
automake \
|
||||
bash-completion \
|
||||
bsdmainutils \
|
||||
btrfs-tools \
|
||||
build-essential \
|
||||
cmake \
|
||||
createrepo \
|
||||
curl \
|
||||
dpkg-sig \
|
||||
g++ \
|
||||
gcc \
|
||||
git \
|
||||
iptables \
|
||||
jq \
|
||||
less \
|
||||
libapparmor-dev \
|
||||
libc6-dev \
|
||||
libcap-dev \
|
||||
libdevmapper-dev \
|
||||
libnl-3-dev \
|
||||
libprotobuf-c0-dev \
|
||||
libprotobuf-dev \
|
||||
libseccomp-dev \
|
||||
libsystemd-dev \
|
||||
libyaml-dev \
|
||||
libtool \
|
||||
libudev-dev \
|
||||
mercurial \
|
||||
net-tools \
|
||||
parallel \
|
||||
pkg-config \
|
||||
protobuf-compiler \
|
||||
protobuf-c-compiler \
|
||||
python-backports.ssl-match-hostname \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-requests \
|
||||
python-setuptools \
|
||||
python-websocket \
|
||||
golang-go \
|
||||
iproute2 \
|
||||
iputils-ping \
|
||||
python-wheel \
|
||||
tar \
|
||||
thin-provisioning-tools \
|
||||
vim \
|
||||
vim-common \
|
||||
xfsprogs \
|
||||
zip \
|
||||
--no-install-recommends
|
||||
|
||||
# Get lvm2 sources to build statically linked devmapper library
|
||||
ENV LVM2_VERSION 2.02.173
|
||||
RUN mkdir -p /usr/local/lvm2 \
|
||||
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||
|
||||
# Compile and install (only the needed library)
|
||||
RUN cd /usr/local/lvm2 \
|
||||
&& ./configure \
|
||||
--build="$(gcc -print-multiarch)" \
|
||||
--enable-static_link \
|
||||
--enable-pkgconfig \
|
||||
&& make -C include \
|
||||
&& make -C libdm install_device-mapper
|
||||
|
||||
# Install seccomp: the version shipped upstream is too old
|
||||
ENV SECCOMP_VERSION 2.3.2
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
# Install Go
|
||||
# We don't have official binary golang 1.7.5 tarballs for ARM64, either for Go or
|
||||
# bootstrap, so we use golang-go (1.6) as bootstrap to build Go from source code.
|
||||
# We don't use the official ARMv6 released binaries as a GOROOT_BOOTSTRAP, because
|
||||
# not all ARM64 platforms support 32-bit mode. 32-bit mode is optional for ARMv8.
|
||||
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
|
||||
ENV GO_VERSION 1.8.3
|
||||
RUN mkdir /usr/src/go && curl -fsSL https://golang.org/dl/go${GO_VERSION}.src.tar.gz | tar -v -C /usr/src/go -xz --strip-components=1 \
|
||||
&& cd /usr/src/go/src \
|
||||
&& GOOS=linux GOARCH=arm64 GOROOT_BOOTSTRAP="$(go env GOROOT)" ./make.bash
|
||||
ENV GO_VERSION 1.8.5
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-arm64.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
|
||||
ENV PATH /go/bin:/usr/src/go/bin:$PATH
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go
|
||||
|
||||
# Only install one version of the registry, because old version which support
|
||||
@@ -123,14 +105,11 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef
|
||||
# Before running the integration tests conftest.py is
|
||||
# loaded which results in loads auth.py that
|
||||
# imports the docker-pycreds module.
|
||||
ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a
|
||||
# To run integration tests docker-pycreds is required.
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
&& pip install wheel \
|
||||
&& pip install docker-pycreds==0.2.1 \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
@@ -173,11 +152,14 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli gometalinter
|
||||
ENV PATH=/usr/local/cli:$PATH
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
# Options for hack/validate/gometalinter
|
||||
ENV GOMETALINTER_OPTS="--deadline 4m -j2"
|
||||
|
||||
# Upload docker source
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
|
||||
54
vendor/github.com/docker/docker/Dockerfile.armhf
generated
vendored
54
vendor/github.com/docker/docker/Dockerfile.armhf
generated
vendored
@@ -15,7 +15,7 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM armhf/debian:jessie
|
||||
FROM arm32v7/debian:stretch
|
||||
|
||||
# allow replacing httpredir or deb mirror
|
||||
ARG APT_MIRROR=deb.debian.org
|
||||
@@ -39,39 +39,31 @@ RUN apt-get update && apt-get install -y \
|
||||
net-tools \
|
||||
libapparmor-dev \
|
||||
libcap-dev \
|
||||
libsystemd-journal-dev \
|
||||
libdevmapper-dev \
|
||||
libseccomp-dev \
|
||||
libsystemd-dev \
|
||||
libtool \
|
||||
libudev-dev \
|
||||
mercurial \
|
||||
pkg-config \
|
||||
python-backports.ssl-match-hostname \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-requests \
|
||||
python-setuptools \
|
||||
python-websocket \
|
||||
python-wheel \
|
||||
xfsprogs \
|
||||
tar \
|
||||
thin-provisioning-tools \
|
||||
vim-common \
|
||||
--no-install-recommends \
|
||||
&& pip install awscli==1.10.15
|
||||
|
||||
# Get lvm2 sources to build statically linked devmapper library
|
||||
ENV LVM2_VERSION 2.02.173
|
||||
RUN mkdir -p /usr/local/lvm2 \
|
||||
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||
|
||||
# Compile and install (only the needed library)
|
||||
RUN cd /usr/local/lvm2 \
|
||||
&& ./configure \
|
||||
--build="$(gcc -print-multiarch)" \
|
||||
--enable-static_link \
|
||||
--enable-pkgconfig \
|
||||
&& make -C include \
|
||||
&& make -C libdm install_device-mapper
|
||||
|
||||
|
||||
# Install Go
|
||||
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
|
||||
ENV GO_VERSION 1.8.3
|
||||
ENV GO_VERSION 1.8.5
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-armv6l.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
@@ -81,21 +73,6 @@ ENV GOPATH /go
|
||||
ENV GOARCH arm
|
||||
ENV GOARM 7
|
||||
|
||||
# Install seccomp: the version shipped upstream is too old
|
||||
ENV SECCOMP_VERSION 2.3.2
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
# Install two versions of the registry. The first is an older version that
|
||||
# only supports schema1 manifests. The second is a newer version that supports
|
||||
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||
@@ -126,10 +103,12 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef
|
||||
ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a
|
||||
# To run integration tests docker-pycreds is required.
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
&& pip install docker-pycreds==0.2.1 \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||
@@ -162,10 +141,13 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli gometalinter
|
||||
ENV PATH=/usr/local/cli:$PATH
|
||||
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
# Options for hack/validate/gometalinter
|
||||
ENV GOMETALINTER_OPTS="--deadline 10m -j2"
|
||||
|
||||
# Upload docker source
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
|
||||
71
vendor/github.com/docker/docker/Dockerfile.e2e
generated
vendored
Normal file
71
vendor/github.com/docker/docker/Dockerfile.e2e
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
## Step 1: Build tests
|
||||
FROM golang:1.8.5-alpine3.6 as builder
|
||||
|
||||
RUN apk add --update \
|
||||
bash \
|
||||
btrfs-progs-dev \
|
||||
build-base \
|
||||
curl \
|
||||
lvm2-dev \
|
||||
jq \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN mkdir -p /go/src/github.com/docker/docker/
|
||||
WORKDIR /go/src/github.com/docker/docker/
|
||||
|
||||
# Generate frozen images
|
||||
COPY contrib/download-frozen-image-v2.sh contrib/download-frozen-image-v2.sh
|
||||
RUN contrib/download-frozen-image-v2.sh /output/docker-frozen-images \
|
||||
buildpack-deps:jessie@sha256:85b379ec16065e4fe4127eb1c5fb1bcc03c559bd36dbb2e22ff496de55925fa6 \
|
||||
busybox:latest@sha256:32f093055929dbc23dec4d03e09dfe971f5973a9ca5cf059cbfb644c206aa83f \
|
||||
debian:jessie@sha256:72f784399fd2719b4cb4e16ef8e369a39dc67f53d978cd3e2e7bf4e502c7b793 \
|
||||
hello-world:latest@sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
|
||||
|
||||
# Download Docker CLI binary
|
||||
COPY hack/dockerfile hack/dockerfile
|
||||
RUN hack/dockerfile/install-binaries.sh dockercli
|
||||
|
||||
# Set tag and add sources
|
||||
ARG DOCKER_GITCOMMIT
|
||||
ENV DOCKER_GITCOMMIT=$DOCKER_GITCOMMIT
|
||||
ADD . .
|
||||
|
||||
# Build DockerSuite.TestBuild* dependency
|
||||
RUN CGO_ENABLED=0 go build -o /output/httpserver github.com/docker/docker/contrib/httpserver
|
||||
|
||||
# Build the integration tests and copy the resulting binaries to /output/tests
|
||||
RUN hack/make.sh build-integration-test-binary
|
||||
RUN mkdir -p /output/tests && find . -name test.main -exec cp --parents '{}' /output/tests \;
|
||||
|
||||
## Step 2: Generate testing image
|
||||
FROM alpine:3.6 as runner
|
||||
|
||||
# GNU tar is used for generating the emptyfs image
|
||||
RUN apk add --update \
|
||||
bash \
|
||||
ca-certificates \
|
||||
g++ \
|
||||
git \
|
||||
iptables \
|
||||
tar \
|
||||
xz \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Add an unprivileged user to be used for tests which need it
|
||||
RUN addgroup docker && adduser -D -G docker unprivilegeduser -s /bin/ash
|
||||
|
||||
COPY contrib/httpserver/Dockerfile /tests/contrib/httpserver/Dockerfile
|
||||
COPY contrib/syscall-test /tests/contrib/syscall-test
|
||||
COPY integration-cli/fixtures /tests/integration-cli/fixtures
|
||||
|
||||
COPY hack/test/e2e-run.sh /scripts/run.sh
|
||||
COPY hack/make/.ensure-emptyfs /scripts/ensure-emptyfs.sh
|
||||
|
||||
COPY --from=builder /output/docker-frozen-images /docker-frozen-images
|
||||
COPY --from=builder /output/httpserver /tests/contrib/httpserver/httpserver
|
||||
COPY --from=builder /output/tests /tests
|
||||
COPY --from=builder /usr/local/bin/docker /usr/bin/docker
|
||||
|
||||
ENV DOCKER_REMOTE_DAEMON=1 DOCKER_INTEGRATION_DAEMON_DEST=/
|
||||
|
||||
ENTRYPOINT ["/scripts/run.sh"]
|
||||
51
vendor/github.com/docker/docker/Dockerfile.ppc64le
generated
vendored
51
vendor/github.com/docker/docker/Dockerfile.ppc64le
generated
vendored
@@ -15,7 +15,7 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM ppc64le/debian:jessie
|
||||
FROM ppc64le/debian:stretch
|
||||
|
||||
# allow replacing httpredir or deb mirror
|
||||
ARG APT_MIRROR=deb.debian.org
|
||||
@@ -40,54 +40,31 @@ RUN apt-get update && apt-get install -y \
|
||||
net-tools \
|
||||
libapparmor-dev \
|
||||
libcap-dev \
|
||||
libsystemd-journal-dev \
|
||||
libdevmapper-dev \
|
||||
libseccomp-dev \
|
||||
libsystemd-dev \
|
||||
libtool \
|
||||
libudev-dev \
|
||||
mercurial \
|
||||
pkg-config \
|
||||
python-backports.ssl-match-hostname \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-requests \
|
||||
python-setuptools \
|
||||
python-websocket \
|
||||
python-wheel \
|
||||
xfsprogs \
|
||||
tar \
|
||||
thin-provisioning-tools \
|
||||
vim-common \
|
||||
--no-install-recommends
|
||||
|
||||
# Get lvm2 sources to build statically linked devmapper library
|
||||
ENV LVM2_VERSION 2.02.173
|
||||
RUN mkdir -p /usr/local/lvm2 \
|
||||
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||
|
||||
# Compile and install (only the needed library)
|
||||
RUN cd /usr/local/lvm2 \
|
||||
&& ./configure \
|
||||
--build="$(gcc -print-multiarch)" \
|
||||
--enable-static_link \
|
||||
--enable-pkgconfig \
|
||||
&& make -C include \
|
||||
&& make -C libdm install_device-mapper
|
||||
|
||||
# Install seccomp: the version shipped upstream is too old
|
||||
ENV SECCOMP_VERSION 2.3.2
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
|
||||
# Install Go
|
||||
# NOTE: official ppc64le go binaries weren't available until go 1.6.4 and 1.7.4
|
||||
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
|
||||
ENV GO_VERSION 1.8.3
|
||||
ENV GO_VERSION 1.8.5
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-ppc64le.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
|
||||
@@ -124,10 +101,12 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef
|
||||
ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a
|
||||
# To run integration tests docker-pycreds is required.
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
&& pip install docker-pycreds==0.2.1 \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||
@@ -160,7 +139,7 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli gometalinter
|
||||
ENV PATH=/usr/local/cli:$PATH
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
|
||||
50
vendor/github.com/docker/docker/Dockerfile.s390x
generated
vendored
50
vendor/github.com/docker/docker/Dockerfile.s390x
generated
vendored
@@ -15,7 +15,7 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM s390x/debian:jessie
|
||||
FROM s390x/debian:stretch
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -36,51 +36,29 @@ RUN apt-get update && apt-get install -y \
|
||||
net-tools \
|
||||
libapparmor-dev \
|
||||
libcap-dev \
|
||||
libsystemd-journal-dev \
|
||||
libdevmapper-dev \
|
||||
libseccomp-dev \
|
||||
libsystemd-dev \
|
||||
libtool \
|
||||
libudev-dev \
|
||||
mercurial \
|
||||
pkg-config \
|
||||
python-backports.ssl-match-hostname \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-requests \
|
||||
python-setuptools \
|
||||
python-websocket \
|
||||
python-wheel \
|
||||
xfsprogs \
|
||||
tar \
|
||||
thin-provisioning-tools \
|
||||
vim-common \
|
||||
--no-install-recommends
|
||||
|
||||
# Install seccomp: the version shipped upstream is too old
|
||||
ENV SECCOMP_VERSION 2.3.2
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
# Get lvm2 sources to build statically linked devmapper library
|
||||
ENV LVM2_VERSION 2.02.173
|
||||
RUN mkdir -p /usr/local/lvm2 \
|
||||
&& curl -fsSL "https://mirrors.kernel.org/sourceware/lvm2/LVM2.${LVM2_VERSION}.tgz" \
|
||||
| tar -xzC /usr/local/lvm2 --strip-components=1
|
||||
|
||||
# Compile and install (only the needed library)
|
||||
RUN cd /usr/local/lvm2 \
|
||||
&& ./configure \
|
||||
--build="$(gcc -print-multiarch)" \
|
||||
--enable-static_link \
|
||||
--enable-pkgconfig \
|
||||
&& make -C include \
|
||||
&& make -C libdm install_device-mapper
|
||||
|
||||
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
|
||||
ENV GO_VERSION 1.8.3
|
||||
ENV GO_VERSION 1.8.5
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-s390x.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
|
||||
@@ -117,10 +95,12 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT a962578e515185cf06506050b2200c0b81aa84ef
|
||||
ENV DOCKER_PY_COMMIT 1d6b5b203222ba5df7dedfcd1ee061a452f99c8a
|
||||
# To run integration tests docker-pycreds is required.
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
&& pip install docker-pycreds==0.2.1 \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||
@@ -153,7 +133,7 @@ RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
# Please edit hack/dockerfile/install-binaries.sh to update them.
|
||||
COPY hack/dockerfile/binaries-commits /tmp/binaries-commits
|
||||
COPY hack/dockerfile/install-binaries.sh /tmp/install-binaries.sh
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli
|
||||
RUN /tmp/install-binaries.sh tomlv vndr runc containerd tini proxy dockercli gometalinter
|
||||
ENV PATH=/usr/local/cli:$PATH
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
|
||||
20
vendor/github.com/docker/docker/Dockerfile.simple
generated
vendored
20
vendor/github.com/docker/docker/Dockerfile.simple
generated
vendored
@@ -5,7 +5,7 @@
|
||||
|
||||
# This represents the bare minimum required to build and test Docker.
|
||||
|
||||
FROM debian:jessie
|
||||
FROM debian:stretch
|
||||
|
||||
# allow replacing httpredir or deb mirror
|
||||
ARG APT_MIRROR=deb.debian.org
|
||||
@@ -23,6 +23,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
libapparmor-dev \
|
||||
libdevmapper-dev \
|
||||
libseccomp-dev \
|
||||
ca-certificates \
|
||||
e2fsprogs \
|
||||
iptables \
|
||||
@@ -34,27 +35,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
vim-common \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install seccomp: the version shipped upstream is too old
|
||||
ENV SECCOMP_VERSION 2.3.2
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||
&& ( \
|
||||
cd "$SECCOMP_PATH" \
|
||||
&& ./configure --prefix=/usr/local \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& ldconfig \
|
||||
) \
|
||||
&& rm -rf "$SECCOMP_PATH"
|
||||
|
||||
# Install Go
|
||||
# IMPORTANT: If the version of Go is updated, the Windows to Linux CI machines
|
||||
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||
# with a heads-up.
|
||||
# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored
|
||||
ENV GO_VERSION 1.8.3
|
||||
ENV GO_VERSION 1.8.5
|
||||
RUN curl -fsSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
|
||||
19
vendor/github.com/docker/docker/Dockerfile.solaris
generated
vendored
19
vendor/github.com/docker/docker/Dockerfile.solaris
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# Defines an image that hosts a native Docker build environment for Solaris
|
||||
# TODO: Improve stub
|
||||
|
||||
FROM solaris:latest
|
||||
|
||||
# compile and runtime deps
|
||||
RUN pkg install --accept \
|
||||
git \
|
||||
gnu-coreutils \
|
||||
gnu-make \
|
||||
gnu-tar \
|
||||
diagnostic/top \
|
||||
golang \
|
||||
library/golang/* \
|
||||
developer/gcc-*
|
||||
|
||||
ENV GOPATH /go/:/usr/lib/gocode/1.5/
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
2
vendor/github.com/docker/docker/Dockerfile.windows
generated
vendored
2
vendor/github.com/docker/docker/Dockerfile.windows
generated
vendored
@@ -161,7 +161,7 @@ SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPref
|
||||
# Environment variable notes:
|
||||
# - GO_VERSION must be consistent with 'Dockerfile' used by Linux.
|
||||
# - FROM_DOCKERFILE is used for detection of building within a container.
|
||||
ENV GO_VERSION=1.8.3 `
|
||||
ENV GO_VERSION=1.8.5 `
|
||||
GIT_VERSION=2.11.1 `
|
||||
GOPATH=C:\go `
|
||||
FROM_DOCKERFILE=1
|
||||
|
||||
6
vendor/github.com/docker/docker/MAINTAINERS
generated
vendored
6
vendor/github.com/docker/docker/MAINTAINERS
generated
vendored
@@ -1,4 +1,4 @@
|
||||
# Docker maintainers file
|
||||
# Moby maintainers file
|
||||
#
|
||||
# This file describes who runs the docker/docker project and how.
|
||||
# This is a living document - if you see something out of date or missing, speak up!
|
||||
@@ -264,7 +264,7 @@
|
||||
[people.cpuguy83]
|
||||
Name = "Brian Goff"
|
||||
Email = "cpuguy83@gmail.com"
|
||||
Github = "cpuguy83"
|
||||
GitHub = "cpuguy83"
|
||||
|
||||
[people.chanwit]
|
||||
Name = "Chanwit Kaewkasi"
|
||||
@@ -364,7 +364,7 @@
|
||||
[people.misty]
|
||||
Name = "Misty Stanley-Jones"
|
||||
Email = "misty@docker.com"
|
||||
GitHub = "mstanleyjones"
|
||||
GitHub = "mistyhacks"
|
||||
|
||||
[people.mlaventure]
|
||||
Name = "Kenfe-Mickaël Laventure"
|
||||
|
||||
23
vendor/github.com/docker/docker/Makefile
generated
vendored
23
vendor/github.com/docker/docker/Makefile
generated
vendored
@@ -1,4 +1,4 @@
|
||||
.PHONY: all binary dynbinary build cross deb help init-go-pkg-cache install manpages rpm run shell test test-docker-py test-integration test-unit tgz validate win
|
||||
.PHONY: all binary dynbinary build cross deb help init-go-pkg-cache install manpages rpm run shell test test-docker-py test-integration test-unit validate win
|
||||
|
||||
# set the graph driver as the current graphdriver if not set
|
||||
DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
|
||||
@@ -16,6 +16,13 @@ export DOCKER_GITCOMMIT
|
||||
# env vars passed through directly to Docker's build scripts
|
||||
# to allow things like `make KEEPBUNDLE=1 binary` easily
|
||||
# `project/PACKAGERS.md` have some limited documentation of some of these
|
||||
#
|
||||
# DOCKER_LDFLAGS can be used to pass additional parameters to -ldflags
|
||||
# option of "go build". For example, a built-in graphdriver priority list
|
||||
# can be changed during build time like this:
|
||||
#
|
||||
# make DOCKER_LDFLAGS="-X github.com/docker/docker/daemon/graphdriver.priority=overlay2,devicemapper" dynbinary
|
||||
#
|
||||
DOCKER_ENVS := \
|
||||
-e DOCKER_CROSSPLATFORMS \
|
||||
-e BUILD_APT_MIRROR \
|
||||
@@ -31,10 +38,12 @@ DOCKER_ENVS := \
|
||||
-e DOCKER_GITCOMMIT \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
-e DOCKER_INCREMENTAL_BINARY \
|
||||
-e DOCKER_LDFLAGS \
|
||||
-e DOCKER_PORT \
|
||||
-e DOCKER_REMAP_ROOT \
|
||||
-e DOCKER_STORAGE_OPTS \
|
||||
-e DOCKER_USERLANDPROXY \
|
||||
-e TEST_INTEGRATION_DIR \
|
||||
-e TESTDIRS \
|
||||
-e TESTFLAGS \
|
||||
-e TIMEOUT \
|
||||
@@ -43,7 +52,8 @@ DOCKER_ENVS := \
|
||||
-e NO_PROXY \
|
||||
-e http_proxy \
|
||||
-e https_proxy \
|
||||
-e no_proxy
|
||||
-e no_proxy \
|
||||
-e VERSION
|
||||
# note: we _cannot_ add "-e DOCKER_BUILDTAGS" here because even if it's unset in the shell, that would shadow the "ENV DOCKER_BUILDTAGS" set in our Dockerfile, which is very important for our official builds
|
||||
|
||||
# to allow `make BIND_DIR=. shell` or `make BIND_DIR= test`
|
||||
@@ -110,7 +120,7 @@ dynbinary: build ## build the linux dynbinaries
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary
|
||||
|
||||
build: bundles init-go-pkg-cache
|
||||
$(warning The docker client CLI has moved to github.com/docker/cli. By default, it is built from the git sha specified in hack/dockerfile/binaries-commits. For a dev-test cycle involving the CLI, run:${\n} DOCKER_CLI_PATH=/host/path/to/cli/binary make shell ${\n} then change the cli and compile into a binary at the same location.${\n})
|
||||
$(warning The docker client CLI has moved to github.com/docker/cli. For a dev-test cycle involving the CLI, run:${\n} DOCKER_CLI_PATH=/host/path/to/cli/binary make shell ${\n} then change the cli and compile into a binary at the same location.${\n})
|
||||
docker build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
|
||||
|
||||
bundles:
|
||||
@@ -148,8 +158,8 @@ run: build ## run the docker daemon in a container
|
||||
shell: build ## start a shell inside the build env
|
||||
$(DOCKER_RUN_DOCKER) bash
|
||||
|
||||
test: build ## run the unit, integration and docker-py tests
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary cross test-unit test-integration test-docker-py
|
||||
test: build test-unit ## run the unit, integration and docker-py tests
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary cross test-integration test-docker-py
|
||||
|
||||
test-docker-py: build ## run the docker-py tests
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py
|
||||
@@ -162,9 +172,6 @@ test-integration: build ## run the integration tests
|
||||
test-unit: build ## run the unit tests
|
||||
$(DOCKER_RUN_DOCKER) hack/test/unit
|
||||
|
||||
tgz: build ## build the archives (.zip on windows and .tgz\notherwise) containing the binaries
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross tgz
|
||||
|
||||
validate: build ## validate DCO, Seccomp profile generation, gofmt,\n./pkg/ isolation, golint, tests, tomls, go vet and vendor
|
||||
$(DOCKER_RUN_DOCKER) hack/validate/all
|
||||
|
||||
|
||||
69
vendor/github.com/docker/docker/README.md
generated
vendored
69
vendor/github.com/docker/docker/README.md
generated
vendored
@@ -1,70 +1,38 @@
|
||||
### Docker users, see [Moby and Docker](https://mobyproject.org/#moby-and-docker) to clarify the relationship between the projects
|
||||
|
||||
### Docker maintainers and contributors, see [Transitioning to Moby](#transitioning-to-moby) for more details
|
||||
|
||||
The Moby Project
|
||||
================
|
||||
|
||||

|
||||
|
||||
Moby is an open-source project created by Docker to advance the software containerization movement.
|
||||
It provides a “Lego set” of dozens of components, the framework for assembling them into custom container-based systems, and a place for all container enthusiasts to experiment and exchange ideas.
|
||||
Moby is an open-source project created by Docker to enable and accelerate software containerization.
|
||||
|
||||
# Moby
|
||||
|
||||
## Overview
|
||||
|
||||
At the core of Moby is a framework to assemble specialized container systems.
|
||||
It provides:
|
||||
|
||||
- A library of containerized components for all vital aspects of a container system: OS, container runtime, orchestration, infrastructure management, networking, storage, security, build, image distribution, etc.
|
||||
- Tools to assemble the components into runnable artifacts for a variety of platforms and architectures: bare metal (both x86 and Arm); executables for Linux, Mac and Windows; VM images for popular cloud and virtualization providers.
|
||||
- A set of reference assemblies which can be used as-is, modified, or used as inspiration to create your own.
|
||||
|
||||
All Moby components are containers, so creating new components is as easy as building a new OCI-compatible container.
|
||||
It provides a "Lego set" of toolkit components, the framework for assembling them into custom container-based systems, and a place for all container enthusiasts and professionals to experiment and exchange ideas.
|
||||
Components include container build tools, a container registry, orchestration tools, a runtime and more, and these can be used as building blocks in conjunction with other tools and projects.
|
||||
|
||||
## Principles
|
||||
|
||||
Moby is an open project guided by strong principles, but modular, flexible and without too strong an opinion on user experience, so it is open to the community to help set its direction.
|
||||
The guiding principles are:
|
||||
Moby is an open project guided by strong principles, aiming to be modular, flexible and without too strong an opinion on user experience.
|
||||
It is open to the community to help set its direction.
|
||||
|
||||
- Modular: the project includes lots of components that have well-defined functions and APIs that work together.
|
||||
- Batteries included but swappable: Moby includes enough components to build fully featured container system, but its modular architecture ensures that most of the components can be swapped by different implementations.
|
||||
- Usable security: Moby will provide secure defaults without compromising usability.
|
||||
- Container centric: Moby is built with containers, for running containers.
|
||||
|
||||
With Moby, you should be able to describe all the components of your distributed application, from the high-level configuration files down to the kernel you would like to use and build and deploy it easily.
|
||||
|
||||
Moby uses [containerd](https://github.com/containerd/containerd) as the default container runtime.
|
||||
- Usable security: Moby provides secure defaults without compromising usability.
|
||||
- Developer focused: The APIs are intended to be functional and useful to build powerful tools.
|
||||
They are not necessarily intended as end user tools but as components aimed at developers.
|
||||
Documentation and UX is aimed at developers not end users.
|
||||
|
||||
## Audience
|
||||
|
||||
Moby is recommended for anyone who wants to assemble a container-based system. This includes:
|
||||
The Moby Project is intended for engineers, integrators and enthusiasts looking to modify, hack, fix, experiment, invent and build systems based on containers.
|
||||
It is not for people looking for a commercially supported system, but for people who want to work and learn with open source code.
|
||||
|
||||
- Hackers who want to customize or patch their Docker build
|
||||
- System engineers or integrators building a container system
|
||||
- Infrastructure providers looking to adapt existing container systems to their environment
|
||||
- Container enthusiasts who want to experiment with the latest container tech
|
||||
- Open-source developers looking to test their project in a variety of different systems
|
||||
- Anyone curious about Docker internals and how it’s built
|
||||
## Relationship with Docker
|
||||
|
||||
Moby is NOT recommended for:
|
||||
The components and tools in the Moby Project are initially the open source components that Docker and the community have built for the Docker Project.
|
||||
New projects can be added if they fit with the community goals. Docker is committed to using Moby as the upstream for the Docker Product.
|
||||
However, other projects are also encouraged to use Moby as an upstream, and to reuse the components in diverse ways, and all these uses will be treated in the same way. External maintainers and contributors are welcomed.
|
||||
|
||||
- Application developers looking for an easy way to run their applications in containers. We recommend Docker CE instead.
|
||||
- Enterprise IT and development teams looking for a ready-to-use, commercially supported container platform. We recommend Docker EE instead.
|
||||
- Anyone curious about containers and looking for an easy way to learn. We recommend the [docker.com](https://www.docker.com/) website instead.
|
||||
|
||||
# Transitioning to Moby
|
||||
|
||||
Docker is transitioning all of its open source collaborations to the Moby project going forward.
|
||||
During the transition, all open source activity should continue as usual.
|
||||
|
||||
We are proposing the following list of changes:
|
||||
|
||||
- splitting up the engine into more open components
|
||||
- removing the docker UI, SDK etc to keep them in the Docker org
|
||||
- clarifying that the project is not limited to the engine, but to the assembly of all the individual components of the Docker platform
|
||||
- open-source new tools & components which we currently use to assemble the Docker product, but could benefit the community
|
||||
- defining an open, community-centric governance inspired by the Fedora project (a very successful example of balancing the needs of the community with the constraints of the primary corporate sponsor)
|
||||
The Moby project is not intended as a location for support or feature requests for Docker products, but as a place for contributors to work on open source code, fix bugs, and make the code more useful.
|
||||
The releases are supported by the maintainers, community and users, on a best efforts basis only, and are not intended for customers who want enterprise or commercial support; Docker EE is the appropriate product for these use cases.
|
||||
|
||||
-----
|
||||
|
||||
@@ -82,7 +50,6 @@ violate applicable laws.
|
||||
|
||||
For more information, please see https://www.bis.doc.gov
|
||||
|
||||
|
||||
Licensing
|
||||
=========
|
||||
Moby is licensed under the Apache License, Version 2.0. See
|
||||
|
||||
122
vendor/github.com/docker/docker/ROADMAP.md
generated
vendored
122
vendor/github.com/docker/docker/ROADMAP.md
generated
vendored
@@ -1,118 +1,68 @@
|
||||
Docker Engine Roadmap
|
||||
=====================
|
||||
Moby Project Roadmap
|
||||
====================
|
||||
|
||||
### How should I use this document?
|
||||
|
||||
This document provides description of items that the project decided to prioritize. This should
|
||||
serve as a reference point for Docker contributors to understand where the project is going, and
|
||||
help determine if a contribution could be conflicting with some longer terms plans.
|
||||
serve as a reference point for Moby contributors to understand where the project is going, and
|
||||
help determine if a contribution could be conflicting with some longer term plans.
|
||||
|
||||
The fact that a feature isn't listed here doesn't mean that a patch for it will automatically be
|
||||
refused (except for those mentioned as "frozen features" below)! We are always happy to receive
|
||||
patches for new cool features we haven't thought about, or didn't judge priority. Please however
|
||||
understand that such patches might take longer for us to review.
|
||||
refused! We are always happy to receive patches for new cool features we haven't thought about,
|
||||
or didn't judge to be a priority. Please however understand that such patches might take longer
|
||||
for us to review.
|
||||
|
||||
### How can I help?
|
||||
|
||||
Short term objectives are listed in the [wiki](https://github.com/docker/docker/wiki) and described
|
||||
in [Issues](https://github.com/docker/docker/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap). Our
|
||||
Short term objectives are listed in
|
||||
[Issues](https://github.com/moby/moby/issues?q=is%3Aopen+is%3Aissue+label%3Aroadmap). Our
|
||||
goal is to split down the workload in such way that anybody can jump in and help. Please comment on
|
||||
issues if you want to take it to avoid duplicating effort! Similarly, if a maintainer is already
|
||||
assigned on an issue you'd like to participate in, pinging him on IRC or GitHub to offer your help is
|
||||
issues if you want to work on it to avoid duplicating effort! Similarly, if a maintainer is already
|
||||
assigned on an issue you'd like to participate in, pinging him on GitHub to offer your help is
|
||||
the best way to go.
|
||||
|
||||
### How can I add something to the roadmap?
|
||||
|
||||
The roadmap process is new to the Docker Engine: we are only beginning to structure and document the
|
||||
The roadmap process is new to the Moby Project: we are only beginning to structure and document the
|
||||
project objectives. Our immediate goal is to be more transparent, and work with our community to
|
||||
focus our efforts on fewer prioritized topics.
|
||||
|
||||
We hope to offer in the near future a process allowing anyone to propose a topic to the roadmap, but
|
||||
we are not quite there yet. For the time being, the BDFL remains the keeper of the roadmap, and we
|
||||
won't be accepting pull requests adding or removing items from this file.
|
||||
we are not quite there yet. For the time being, it is best to discuss with the maintainers on an
|
||||
issue, in the Slack channel, or in person at the Moby Summits that happen every few months.
|
||||
|
||||
# 1. Features and refactoring
|
||||
|
||||
## 1.1 Runtime improvements
|
||||
|
||||
We recently introduced [`runC`](https://runc.io) as a standalone low-level tool for container
|
||||
execution. The initial goal was to integrate runC as a replacement in the Engine for the traditional
|
||||
default libcontainer `execdriver`, but the Engine internals were not ready for this.
|
||||
We introduced [`runC`](https://runc.io) as a standalone low-level tool for container
|
||||
execution in 2015, the first stage in spinning out parts of the Engine into standalone tools.
|
||||
|
||||
As runC continued evolving, and the OCI specification along with it, we created
|
||||
[`containerd`](https://containerd.tools/), a daemon to control and monitor multiple `runC`. This is
|
||||
the new target for Engine integration, as it can entirely replace the whole `execdriver`
|
||||
architecture, and container monitoring along with it.
|
||||
[`containerd`](https://github.com/containerd/containerd), a daemon to control and monitor `runC`.
|
||||
In late 2016 this was relaunched as the `containerd` 1.0 track, aiming to provide a common runtime
|
||||
for the whole spectrum of container systems, including Kubernetes, with wide community support.
|
||||
This change meant that there was an increased scope for `containerd`, including image management
|
||||
and storage drivers.
|
||||
|
||||
Docker Engine will rely on a long-running `containerd` companion daemon for all container execution
|
||||
Moby will rely on a long-running `containerd` companion daemon for all container execution
|
||||
related operations. This could open the door in the future for Engine restarts without interrupting
|
||||
running containers.
|
||||
running containers. The switch over to containerd 1.0 is an important goal for the project, and
|
||||
will result in a significant simplification of the functions implemented in this repository.
|
||||
|
||||
## 1.2 Plugins improvements
|
||||
## 1.2 Internal decoupling
|
||||
|
||||
Docker Engine 1.7.0 introduced plugin support, initially for the use cases of volumes and networks
|
||||
extensions. The plugin infrastructure was kept minimal as we were collecting use cases and real
|
||||
world feedback before optimizing for any particular workflow.
|
||||
A lot of work has been done in trying to decouple Moby internals. This process of creating
|
||||
standalone projects with a well defined function that attract a dedicated community should continue.
|
||||
As well as integrating `containerd` we would like to integrate [BuildKit](https://github.com/moby/buildkit)
|
||||
as the next standalone component.
|
||||
|
||||
In the future, we'd like plugins to become first class citizens, and encourage an ecosystem of
|
||||
plugins. This implies in particular making it trivially easy to distribute plugins as containers
|
||||
through any Registry instance, as well as solving the commonly heard pain points of plugins needing
|
||||
to be treated as somewhat special (being active at all time, started before any other user
|
||||
containers, and not as easily dismissed).
|
||||
We see gRPC as the natural communication layer between decoupled components.
|
||||
|
||||
## 1.3 Internal decoupling
|
||||
## 1.3 Custom assembly tooling
|
||||
|
||||
A lot of work has been done in trying to decouple the Docker Engine's internals. In particular, the
|
||||
API implementation has been refactored, and the Builder side of the daemon is now
|
||||
[fully independent](https://github.com/docker/docker/tree/master/builder) while still residing in
|
||||
the same repository.
|
||||
|
||||
We are exploring ways to go further with that decoupling, capitalizing on the work introduced by the
|
||||
runtime renovation and plugins improvement efforts. Indeed, the combination of `containerd` support
|
||||
with the concept of "special" containers opens the door for bootstrapping more Engine internals
|
||||
using the same facilities.
|
||||
|
||||
## 1.4 Cluster capable Engine
|
||||
|
||||
The community has been pushing for a more cluster capable Docker Engine, and a huge effort was spent
|
||||
adding features such as multihost networking, and node discovery down at the Engine level. Yet, the
|
||||
Engine is currently incapable of taking scheduling decisions alone, and continues relying on Swarm
|
||||
for that.
|
||||
|
||||
We plan to complete this effort and make Engine fully cluster capable. Multiple instances of the
|
||||
Docker Engine being already capable of discovering each other and establish overlay networking for
|
||||
their container to communicate, the next step is for a given Engine to gain ability to dispatch work
|
||||
to another node in the cluster. This will be introduced in a backward compatible way, such that a
|
||||
`docker run` invocation on a particular node remains fully deterministic.
|
||||
|
||||
# 2. Frozen features
|
||||
|
||||
## 2.1 Docker exec
|
||||
|
||||
We won't accept patches expanding the surface of `docker exec`, which we intend to keep as a
|
||||
*debugging* feature, as well as being strongly dependent on the Runtime ingredient effort.
|
||||
|
||||
## 2.2 Remote Registry Operations
|
||||
|
||||
A large amount of work is ongoing in the area of image distribution and provenance. This includes
|
||||
moving to the V2 Registry API and heavily refactoring the code that powers these features. The
|
||||
desired result is more secure, reliable and easier to use image distribution.
|
||||
|
||||
Part of the problem with this part of the code base is the lack of a stable and flexible interface.
|
||||
If new features are added that access the registry without solidifying these interfaces, achieving
|
||||
feature parity will continue to be elusive. While we get a handle on this situation, we are imposing
|
||||
a moratorium on new code that accesses the Registry API in commands that don't already make remote
|
||||
calls.
|
||||
|
||||
Currently, only the following commands cause interaction with a remote registry:
|
||||
|
||||
- push
|
||||
- pull
|
||||
- run
|
||||
- build
|
||||
- search
|
||||
- login
|
||||
|
||||
In the interest of stabilizing the registry access model during this ongoing work, we are not
|
||||
accepting additions to other commands that will cause remote interaction with the Registry API. This
|
||||
moratorium will lift when the goals of the distribution project have been met.
|
||||
We have been prototyping the Moby [assembly tool](https://github.com/moby/tool) which was originally
|
||||
developed for LinuxKit and intend to turn it into a more generic packaging and assembly mechanism
|
||||
that can build not only the default version of Moby, as distribution packages or other useful forms,
|
||||
but can also build very different container systems, themselves built of cooperating daemons built in
|
||||
and running in containers. We intend to merge this functionality into this repo.
|
||||
|
||||
1
vendor/github.com/docker/docker/VERSION
generated
vendored
1
vendor/github.com/docker/docker/VERSION
generated
vendored
@@ -1 +0,0 @@
|
||||
17.06.0-dev
|
||||
8
vendor/github.com/docker/docker/api/README.md
generated
vendored
8
vendor/github.com/docker/docker/api/README.md
generated
vendored
@@ -10,7 +10,7 @@ It consists of various components in this repository:
|
||||
- `client/` The Go client used by the command-line client. It can also be used by third-party Go programs.
|
||||
- `daemon/` The daemon, which serves the API.
|
||||
|
||||
## Swagger definition
|
||||
## Swagger definition
|
||||
|
||||
The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to:
|
||||
|
||||
@@ -20,7 +20,7 @@ The API is defined by the [Swagger](http://swagger.io/specification/) definition
|
||||
|
||||
## Updating the API documentation
|
||||
|
||||
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, you'll need to edit this file to represent the change in the documentation.
|
||||
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, edit this file to represent the change in the documentation.
|
||||
|
||||
The file is split into two main sections:
|
||||
|
||||
@@ -29,9 +29,9 @@ The file is split into two main sections:
|
||||
|
||||
To make an edit, first look for the endpoint you want to edit under `paths`, then make the required edits. Endpoints may reference reusable objects with `$ref`, which can be found in the `definitions` section.
|
||||
|
||||
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919)
|
||||
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919).
|
||||
|
||||
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful for when you are making edits to ensure you are doing the right thing.
|
||||
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful when making edits to ensure you are doing the right thing.
|
||||
|
||||
## Viewing the API documentation
|
||||
|
||||
|
||||
56
vendor/github.com/docker/docker/api/common.go
generated
vendored
56
vendor/github.com/docker/docker/api/common.go
generated
vendored
@@ -1,65 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// DefaultVersion of Current REST API
|
||||
DefaultVersion string = "1.32"
|
||||
DefaultVersion string = "1.35"
|
||||
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
NoBaseImageSpecifier string = "scratch"
|
||||
)
|
||||
|
||||
// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
|
||||
// otherwise generates a new one
|
||||
func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
||||
err := system.MkdirAll(filepath.Dir(trustKeyPath), 0700, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trustKey, err := libtrust.LoadKeyFile(trustKeyPath)
|
||||
if err == libtrust.ErrKeyFileDoesNotExist {
|
||||
trustKey, err = libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error generating key: %s", err)
|
||||
}
|
||||
encodedKey, err := serializePrivateKey(trustKey, filepath.Ext(trustKeyPath))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error serializing key: %s", err)
|
||||
}
|
||||
if err := ioutils.AtomicWriteFile(trustKeyPath, encodedKey, os.FileMode(0600)); err != nil {
|
||||
return nil, fmt.Errorf("Error saving key file: %s", err)
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err)
|
||||
}
|
||||
return trustKey, nil
|
||||
}
|
||||
|
||||
func serializePrivateKey(key libtrust.PrivateKey, ext string) (encoded []byte, err error) {
|
||||
if ext == ".json" || ext == ".jwk" {
|
||||
encoded, err = json.Marshal(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to encode private key JWK: %s", err)
|
||||
}
|
||||
} else {
|
||||
pemBlock, err := key.PEMBlock()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to encode private key PEM: %s", err)
|
||||
}
|
||||
encoded = pem.EncodeToMemory(pemBlock)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
77
vendor/github.com/docker/docker/api/common_test.go
generated
vendored
77
vendor/github.com/docker/docker/api/common_test.go
generated
vendored
@@ -1,77 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"os"
|
||||
)
|
||||
|
||||
// LoadOrCreateTrustKey
|
||||
func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) {
|
||||
tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpKeyFolderPath)
|
||||
|
||||
tmpKeyFile, err := ioutil.TempFile(tmpKeyFolderPath, "keyfile")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := LoadOrCreateTrustKey(tmpKeyFile.Name()); err == nil {
|
||||
t.Fatal("expected an error, got nothing.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLoadOrCreateTrustKeyCreateKey(t *testing.T) {
|
||||
tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpKeyFolderPath)
|
||||
|
||||
// Without the need to create the folder hierarchy
|
||||
tmpKeyFile := filepath.Join(tmpKeyFolderPath, "keyfile")
|
||||
|
||||
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(tmpKeyFile); err != nil {
|
||||
t.Fatalf("Expected to find a file %s, got %v", tmpKeyFile, err)
|
||||
}
|
||||
|
||||
// With the need to create the folder hierarchy as tmpKeyFie is in a path
|
||||
// where some folders do not exist.
|
||||
tmpKeyFile = filepath.Join(tmpKeyFolderPath, "folder/hierarchy/keyfile")
|
||||
|
||||
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(tmpKeyFile); err != nil {
|
||||
t.Fatalf("Expected to find a file %s, got %v", tmpKeyFile, err)
|
||||
}
|
||||
|
||||
// With no path at all
|
||||
defer os.Remove("keyfile")
|
||||
if key, err := LoadOrCreateTrustKey("keyfile"); err != nil || key == nil {
|
||||
t.Fatalf("expected a new key file, got : %v and %v", err, key)
|
||||
}
|
||||
|
||||
if _, err := os.Stat("keyfile"); err != nil {
|
||||
t.Fatalf("Expected to find a file keyfile, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadOrCreateTrustKeyLoadValidKey(t *testing.T) {
|
||||
tmpKeyFile := filepath.Join("fixtures", "keyfile")
|
||||
|
||||
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||
t.Fatalf("expected a key file, got : %v and %v", err, key)
|
||||
}
|
||||
}
|
||||
10
vendor/github.com/docker/docker/api/errdefs/is.go
generated
vendored
10
vendor/github.com/docker/docker/api/errdefs/is.go
generated
vendored
@@ -25,7 +25,7 @@ func getImplementer(err error) error {
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotFound returns if the passed in error is a ErrNotFound
|
||||
// IsNotFound returns if the passed in error is an ErrNotFound
|
||||
func IsNotFound(err error) bool {
|
||||
_, ok := getImplementer(err).(ErrNotFound)
|
||||
return ok
|
||||
@@ -37,7 +37,7 @@ func IsInvalidParameter(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsConflict returns if the passed in error is a ErrConflict
|
||||
// IsConflict returns if the passed in error is an ErrConflict
|
||||
func IsConflict(err error) bool {
|
||||
_, ok := getImplementer(err).(ErrConflict)
|
||||
return ok
|
||||
@@ -55,13 +55,13 @@ func IsUnavailable(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsForbidden returns if the passed in error is a ErrForbidden
|
||||
// IsForbidden returns if the passed in error is an ErrForbidden
|
||||
func IsForbidden(err error) bool {
|
||||
_, ok := getImplementer(err).(ErrForbidden)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsSystem returns if the passed in error is a ErrSystem
|
||||
// IsSystem returns if the passed in error is an ErrSystem
|
||||
func IsSystem(err error) bool {
|
||||
_, ok := getImplementer(err).(ErrSystem)
|
||||
return ok
|
||||
@@ -73,7 +73,7 @@ func IsNotModified(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsNotImplemented returns if the passed in error is a ErrNotImplemented
|
||||
// IsNotImplemented returns if the passed in error is an ErrNotImplemented
|
||||
func IsNotImplemented(err error) bool {
|
||||
_, ok := getImplementer(err).(ErrNotImplemented)
|
||||
return ok
|
||||
|
||||
4
vendor/github.com/docker/docker/api/server/httputils/form.go
generated
vendored
4
vendor/github.com/docker/docker/api/server/httputils/form.go
generated
vendored
@@ -2,7 +2,6 @@ package httputils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -69,8 +68,7 @@ func ArchiveFormValues(r *http.Request, vars map[string]string) (ArchiveOptions,
|
||||
if name == "" {
|
||||
return ArchiveOptions{}, badParameterError{"name"}
|
||||
}
|
||||
|
||||
path := filepath.FromSlash(r.Form.Get("path"))
|
||||
path := r.Form.Get("path")
|
||||
if path == "" {
|
||||
return ArchiveOptions{}, badParameterError{"path"}
|
||||
}
|
||||
|
||||
19
vendor/github.com/docker/docker/api/server/httputils/write_log_stream.go
generated
vendored
19
vendor/github.com/docker/docker/api/server/httputils/write_log_stream.go
generated
vendored
@@ -11,26 +11,19 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/jsonlog"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
)
|
||||
|
||||
// WriteLogStream writes an encoded byte stream of log messages from the
|
||||
// messages channel, multiplexing them with a stdcopy.Writer if mux is true
|
||||
func WriteLogStream(ctx context.Context, w io.Writer, msgs <-chan *backend.LogMessage, config *types.ContainerLogsOptions, mux bool) {
|
||||
func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMessage, config *types.ContainerLogsOptions, mux bool) {
|
||||
wf := ioutils.NewWriteFlusher(w)
|
||||
defer wf.Close()
|
||||
|
||||
wf.Flush()
|
||||
|
||||
// this might seem like doing below is clear:
|
||||
// var outStream io.Writer = wf
|
||||
// however, this GREATLY DISPLEASES golint, and if you do that, it will
|
||||
// fail CI. we need outstream to be type writer because if we mux streams,
|
||||
// we will need to reassign all of the streams to be stdwriters, which only
|
||||
// conforms to the io.Writer interface.
|
||||
var outStream io.Writer
|
||||
outStream = wf
|
||||
outStream := io.Writer(wf)
|
||||
errStream := outStream
|
||||
sysErrStream := errStream
|
||||
if mux {
|
||||
@@ -56,11 +49,7 @@ func WriteLogStream(ctx context.Context, w io.Writer, msgs <-chan *backend.LogMe
|
||||
logLine = append(logLine, msg.Line...)
|
||||
}
|
||||
if config.Timestamps {
|
||||
// TODO(dperny) the format is defined in
|
||||
// daemon/logger/logger.go as logger.TimeFormat. importing
|
||||
// logger is verboten (not part of backend) so idk if just
|
||||
// importing the same thing from jsonlog is good enough
|
||||
logLine = append([]byte(msg.Timestamp.Format(jsonlog.RFC3339NanoFixed)+" "), logLine...)
|
||||
logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...)
|
||||
}
|
||||
if msg.Source == "stdout" && config.ShowStdout {
|
||||
outStream.Write(logLine)
|
||||
|
||||
22
vendor/github.com/docker/docker/api/server/middleware/version.go
generated
vendored
22
vendor/github.com/docker/docker/api/server/middleware/version.go
generated
vendored
@@ -28,11 +28,14 @@ func NewVersionMiddleware(s, d, m string) VersionMiddleware {
|
||||
}
|
||||
|
||||
type versionUnsupportedError struct {
|
||||
version, minVersion string
|
||||
version, minVersion, maxVersion string
|
||||
}
|
||||
|
||||
func (e versionUnsupportedError) Error() string {
|
||||
return fmt.Sprintf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", e.version, e.minVersion)
|
||||
if e.minVersion != "" {
|
||||
return fmt.Sprintf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", e.version, e.minVersion)
|
||||
}
|
||||
return fmt.Sprintf("client version %s is too new. Maximum supported API version is %s", e.version, e.maxVersion)
|
||||
}
|
||||
|
||||
func (e versionUnsupportedError) InvalidParameter() {}
|
||||
@@ -40,19 +43,20 @@ func (e versionUnsupportedError) InvalidParameter() {}
|
||||
// WrapHandler returns a new handler function wrapping the previous one in the request chain.
|
||||
func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
w.Header().Set("Server", fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS))
|
||||
w.Header().Set("API-Version", v.defaultVersion)
|
||||
w.Header().Set("OSType", runtime.GOOS)
|
||||
|
||||
apiVersion := vars["version"]
|
||||
if apiVersion == "" {
|
||||
apiVersion = v.defaultVersion
|
||||
}
|
||||
|
||||
if versions.LessThan(apiVersion, v.minVersion) {
|
||||
return versionUnsupportedError{apiVersion, v.minVersion}
|
||||
return versionUnsupportedError{version: apiVersion, minVersion: v.minVersion}
|
||||
}
|
||||
if versions.GreaterThan(apiVersion, v.defaultVersion) {
|
||||
return versionUnsupportedError{version: apiVersion, maxVersion: v.defaultVersion}
|
||||
}
|
||||
|
||||
header := fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS)
|
||||
w.Header().Set("Server", header)
|
||||
w.Header().Set("API-Version", v.defaultVersion)
|
||||
w.Header().Set("OSType", runtime.GOOS)
|
||||
// nolint: golint
|
||||
ctx = context.WithValue(ctx, "api-version", apiVersion)
|
||||
return handler(ctx, w, r, vars)
|
||||
|
||||
57
vendor/github.com/docker/docker/api/server/middleware/version_test.go
generated
vendored
57
vendor/github.com/docker/docker/api/server/middleware/version_test.go
generated
vendored
@@ -3,10 +3,12 @@ package middleware
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -31,7 +33,7 @@ func TestVersionMiddleware(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionMiddlewareWithErrors(t *testing.T) {
|
||||
func TestVersionMiddlewareVersionTooOld(t *testing.T) {
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if httputils.VersionFromContext(ctx) == "" {
|
||||
t.Fatal("Expected version, got empty string")
|
||||
@@ -55,3 +57,56 @@ func TestVersionMiddlewareWithErrors(t *testing.T) {
|
||||
t.Fatalf("Expected too old client error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionMiddlewareVersionTooNew(t *testing.T) {
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if httputils.VersionFromContext(ctx) == "" {
|
||||
t.Fatal("Expected version, got empty string")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
defaultVersion := "1.10.0"
|
||||
minVersion := "1.2.0"
|
||||
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
||||
h := m.WrapHandler(handler)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
ctx := context.Background()
|
||||
|
||||
vars := map[string]string{"version": "9999.9999"}
|
||||
err := h(ctx, resp, req, vars)
|
||||
|
||||
if !strings.Contains(err.Error(), "client version 9999.9999 is too new. Maximum supported API version is 1.10.0") {
|
||||
t.Fatalf("Expected too new client error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionMiddlewareWithErrorsReturnsHeaders(t *testing.T) {
|
||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if httputils.VersionFromContext(ctx) == "" {
|
||||
t.Fatal("Expected version, got empty string")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
defaultVersion := "1.10.0"
|
||||
minVersion := "1.2.0"
|
||||
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
||||
h := m.WrapHandler(handler)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
ctx := context.Background()
|
||||
|
||||
vars := map[string]string{"version": "0.1"}
|
||||
err := h(ctx, resp, req, vars)
|
||||
|
||||
assert.Error(t, err)
|
||||
hdr := resp.Result().Header
|
||||
assert.Contains(t, hdr.Get("Server"), "Docker/"+defaultVersion)
|
||||
assert.Contains(t, hdr.Get("Server"), runtime.GOOS)
|
||||
assert.Equal(t, hdr.Get("API-Version"), defaultVersion)
|
||||
assert.Equal(t, hdr.Get("OSType"), runtime.GOOS)
|
||||
}
|
||||
|
||||
20
vendor/github.com/docker/docker/api/server/router/build/build_routes.go
generated
vendored
20
vendor/github.com/docker/docker/api/server/router/build/build_routes.go
generated
vendored
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -67,6 +69,24 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
options.Squash = httputils.BoolValue(r, "squash")
|
||||
options.Target = r.FormValue("target")
|
||||
options.RemoteContext = r.FormValue("remote")
|
||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||
// TODO @jhowardmsft. The following environment variable is an interim
|
||||
// measure to allow the daemon to have a default platform if omitted by
|
||||
// the client. This allows LCOW and WCOW to work with a down-level CLI
|
||||
// for a short period of time, as the CLI changes can't be merged
|
||||
// until after the daemon changes have been merged. Once the CLI is
|
||||
// updated, this can be removed. PR for CLI is currently in
|
||||
// https://github.com/docker/cli/pull/474.
|
||||
apiPlatform := r.FormValue("platform")
|
||||
if system.LCOWSupported() && apiPlatform == "" {
|
||||
apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED")
|
||||
}
|
||||
p := system.ParsePlatform(apiPlatform)
|
||||
if err := system.ValidatePlatform(p); err != nil {
|
||||
return nil, validationError{fmt.Errorf("invalid platform: %s", err)}
|
||||
}
|
||||
options.Platform = p.OS
|
||||
}
|
||||
|
||||
if r.Form.Get("shmsize") != "" {
|
||||
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
|
||||
|
||||
2
vendor/github.com/docker/docker/api/server/router/container/backend.go
generated
vendored
2
vendor/github.com/docker/docker/api/server/router/container/backend.go
generated
vendored
@@ -18,7 +18,7 @@ type execBackend interface {
|
||||
ContainerExecCreate(name string, config *types.ExecConfig) (string, error)
|
||||
ContainerExecInspect(id string) (*backend.ExecInspect, error)
|
||||
ContainerExecResize(name string, height, width int) error
|
||||
ContainerExecStart(ctx context.Context, name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error
|
||||
ContainerExecStart(ctx context.Context, name string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error
|
||||
ExecExists(name string) (bool, error)
|
||||
}
|
||||
|
||||
|
||||
29
vendor/github.com/docker/docker/api/server/router/container/container_routes.go
generated
vendored
29
vendor/github.com/docker/docker/api/server/router/container/container_routes.go
generated
vendored
@@ -28,7 +28,7 @@ func (s *containerRouter) getContainersJSON(ctx context.Context, w http.Response
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
filter, err := filters.FromParam(r.Form.Get("filters"))
|
||||
filter, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
||||
config := &backend.ContainerStatsConfig{
|
||||
Stream: stream,
|
||||
OutStream: w,
|
||||
Version: string(httputils.VersionFromContext(ctx)),
|
||||
Version: httputils.VersionFromContext(ctx),
|
||||
}
|
||||
|
||||
return s.backend.ContainerStats(ctx, vars["name"], config)
|
||||
@@ -96,6 +96,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
|
||||
Follow: httputils.BoolValue(r, "follow"),
|
||||
Timestamps: httputils.BoolValue(r, "timestamps"),
|
||||
Since: r.Form.Get("since"),
|
||||
Until: r.Form.Get("until"),
|
||||
Tail: r.Form.Get("tail"),
|
||||
ShowStdout: stdout,
|
||||
ShowStderr: stderr,
|
||||
@@ -280,11 +281,12 @@ func (s *containerRouter) postContainersWait(ctx context.Context, w http.Respons
|
||||
// Behavior changed in version 1.30 to handle wait condition and to
|
||||
// return headers immediately.
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
legacyBehavior := versions.LessThan(version, "1.30")
|
||||
legacyBehaviorPre130 := versions.LessThan(version, "1.30")
|
||||
legacyRemovalWaitPre134 := false
|
||||
|
||||
// The wait condition defaults to "not-running".
|
||||
waitCondition := containerpkg.WaitConditionNotRunning
|
||||
if !legacyBehavior {
|
||||
if !legacyBehaviorPre130 {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -293,6 +295,7 @@ func (s *containerRouter) postContainersWait(ctx context.Context, w http.Respons
|
||||
waitCondition = containerpkg.WaitConditionNextExit
|
||||
case container.WaitConditionRemoved:
|
||||
waitCondition = containerpkg.WaitConditionRemoved
|
||||
legacyRemovalWaitPre134 = versions.LessThan(version, "1.34")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +309,7 @@ func (s *containerRouter) postContainersWait(ctx context.Context, w http.Respons
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if !legacyBehavior {
|
||||
if !legacyBehaviorPre130 {
|
||||
// Write response header immediately.
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
@@ -317,8 +320,22 @@ func (s *containerRouter) postContainersWait(ctx context.Context, w http.Respons
|
||||
// Block on the result of the wait operation.
|
||||
status := <-waitC
|
||||
|
||||
// With API < 1.34, wait on WaitConditionRemoved did not return
|
||||
// in case container removal failed. The only way to report an
|
||||
// error back to the client is to not write anything (i.e. send
|
||||
// an empty response which will be treated as an error).
|
||||
if legacyRemovalWaitPre134 && status.Err() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var waitError *container.ContainerWaitOKBodyError
|
||||
if status.Err() != nil {
|
||||
waitError = &container.ContainerWaitOKBodyError{Message: status.Err().Error()}
|
||||
}
|
||||
|
||||
return json.NewEncoder(w).Encode(&container.ContainerWaitOKBody{
|
||||
StatusCode: int64(status.ExitCode()),
|
||||
Error: waitError,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -588,7 +605,7 @@ func (s *containerRouter) postContainersPrune(ctx context.Context, w http.Respon
|
||||
return err
|
||||
}
|
||||
|
||||
pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return validationError{err}
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/docker/api/server/router/container/exec.go
generated
vendored
2
vendor/github.com/docker/docker/api/server/router/container/exec.go
generated
vendored
@@ -126,7 +126,7 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
return err
|
||||
}
|
||||
stdout.Write([]byte(err.Error() + "\r\n"))
|
||||
logrus.Errorf("Error running exec in container: %v", err)
|
||||
logrus.Errorf("Error running exec %s in container: %v", execName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
127
vendor/github.com/docker/docker/api/server/router/image/image_routes.go
generated
vendored
127
vendor/github.com/docker/docker/api/server/router/image/image_routes.go
generated
vendored
@@ -3,9 +3,10 @@ package image
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/registry"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@@ -66,90 +68,75 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
|
||||
return err
|
||||
}
|
||||
|
||||
return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{
|
||||
ID: string(imgID),
|
||||
})
|
||||
return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID})
|
||||
}
|
||||
|
||||
// Creates an image from Pull or from Import
|
||||
func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
image = r.Form.Get("fromImage")
|
||||
repo = r.Form.Get("repo")
|
||||
tag = r.Form.Get("tag")
|
||||
message = r.Form.Get("message")
|
||||
err error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
image = r.Form.Get("fromImage")
|
||||
repo = r.Form.Get("repo")
|
||||
tag = r.Form.Get("tag")
|
||||
message = r.Form.Get("message")
|
||||
err error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
platform = &specs.Platform{}
|
||||
)
|
||||
defer output.Close()
|
||||
|
||||
// TODO @jhowardmsft LCOW Support: Eventually we will need an API change
|
||||
// so that platform comes from (for example) r.Form.Get("platform"). For
|
||||
// the initial implementation, we assume that the platform is the
|
||||
// runtime OS of the host. It will also need a validation function such
|
||||
// as below which should be called after getting it from the API.
|
||||
//
|
||||
// Ensures the requested platform is valid and normalized
|
||||
//func validatePlatform(req string) (string, error) {
|
||||
// req = strings.ToLower(req)
|
||||
// if req == "" {
|
||||
// req = runtime.GOOS // default to host platform
|
||||
// }
|
||||
// valid := []string{runtime.GOOS}
|
||||
//
|
||||
// if system.LCOWSupported() {
|
||||
// valid = append(valid, "linux")
|
||||
// }
|
||||
//
|
||||
// for _, item := range valid {
|
||||
// if req == item {
|
||||
// return req, nil
|
||||
// }
|
||||
// }
|
||||
// return "", fmt.Errorf("invalid platform requested: %s", req)
|
||||
//}
|
||||
//
|
||||
// And in the call-site:
|
||||
// if platform, err = validatePlatform(platform); err != nil {
|
||||
// return err
|
||||
// }
|
||||
platform := runtime.GOOS
|
||||
if system.LCOWSupported() {
|
||||
platform = "linux"
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if image != "" { //pull
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
}
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||
// TODO @jhowardmsft. The following environment variable is an interim
|
||||
// measure to allow the daemon to have a default platform if omitted by
|
||||
// the client. This allows LCOW and WCOW to work with a down-level CLI
|
||||
// for a short period of time, as the CLI changes can't be merged
|
||||
// until after the daemon changes have been merged. Once the CLI is
|
||||
// updated, this can be removed. PR for CLI is currently in
|
||||
// https://github.com/docker/cli/pull/474.
|
||||
apiPlatform := r.FormValue("platform")
|
||||
if system.LCOWSupported() && apiPlatform == "" {
|
||||
apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED")
|
||||
}
|
||||
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
authConfig := &types.AuthConfig{}
|
||||
if authEncoded != "" {
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
authConfig = &types.AuthConfig{}
|
||||
}
|
||||
platform = system.ParsePlatform(apiPlatform)
|
||||
if err = system.ValidatePlatform(platform); err != nil {
|
||||
err = fmt.Errorf("invalid platform: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output)
|
||||
} else { //import
|
||||
src := r.Form.Get("fromSrc")
|
||||
// 'err' MUST NOT be defined within this block, we need any error
|
||||
// generated from the download to be available to the output
|
||||
// stream processing below
|
||||
err = s.backend.ImportImage(src, repo, platform, tag, message, r.Body, output, r.Form["changes"])
|
||||
if err == nil {
|
||||
if image != "" { //pull
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
metaHeaders[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
authEncoded := r.Header.Get("X-Registry-Auth")
|
||||
authConfig := &types.AuthConfig{}
|
||||
if authEncoded != "" {
|
||||
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
|
||||
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
authConfig = &types.AuthConfig{}
|
||||
}
|
||||
}
|
||||
err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output)
|
||||
} else { //import
|
||||
src := r.Form.Get("fromSrc")
|
||||
// 'err' MUST NOT be defined within this block, we need any error
|
||||
// generated from the download to be available to the output
|
||||
// stream processing below
|
||||
err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"])
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if !output.Flushed() {
|
||||
@@ -304,7 +291,7 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
|
||||
return err
|
||||
}
|
||||
|
||||
imageFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
imageFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -387,7 +374,7 @@ func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter
|
||||
return err
|
||||
}
|
||||
|
||||
pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
12
vendor/github.com/docker/docker/api/server/router/network/filter.go
generated
vendored
12
vendor/github.com/docker/docker/api/server/router/network/filter.go
generated
vendored
@@ -45,27 +45,27 @@ func filterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.N
|
||||
|
||||
displayNet := []types.NetworkResource{}
|
||||
for _, nw := range nws {
|
||||
if filter.Include("driver") {
|
||||
if filter.Contains("driver") {
|
||||
if !filter.ExactMatch("driver", nw.Driver) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if filter.Include("name") {
|
||||
if filter.Contains("name") {
|
||||
if !filter.Match("name", nw.Name) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if filter.Include("id") {
|
||||
if filter.Contains("id") {
|
||||
if !filter.Match("id", nw.ID) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if filter.Include("label") {
|
||||
if filter.Contains("label") {
|
||||
if !filter.MatchKVList("label", nw.Labels) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if filter.Include("scope") {
|
||||
if filter.Contains("scope") {
|
||||
if !filter.ExactMatch("scope", nw.Scope) {
|
||||
continue
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func filterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.N
|
||||
displayNet = append(displayNet, nw)
|
||||
}
|
||||
|
||||
if filter.Include("type") {
|
||||
if filter.Contains("type") {
|
||||
typeNet := []types.NetworkResource{}
|
||||
errFilter := filter.WalkValues("type", func(fval string) error {
|
||||
passList, err := filterNetworkByType(displayNet, fval)
|
||||
|
||||
26
vendor/github.com/docker/docker/api/server/router/network/network_routes.go
generated
vendored
26
vendor/github.com/docker/docker/api/server/router/network/network_routes.go
generated
vendored
@@ -37,7 +37,7 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit
|
||||
}
|
||||
|
||||
filter := r.Form.Get("filters")
|
||||
netFilters, err := filters.FromParam(filter)
|
||||
netFilters, err := filters.FromJSON(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -100,6 +100,24 @@ func (e ambigousResultsError) Error() string {
|
||||
|
||||
func (ambigousResultsError) InvalidParameter() {}
|
||||
|
||||
type conflictError struct {
|
||||
cause error
|
||||
}
|
||||
|
||||
func (e conflictError) Error() string {
|
||||
return e.cause.Error()
|
||||
}
|
||||
|
||||
func (e conflictError) Cause() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
func (e conflictError) Conflict() {}
|
||||
|
||||
func nameConflict(name string) error {
|
||||
return conflictError{libnetwork.NetworkNameError(name)}
|
||||
}
|
||||
|
||||
func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
@@ -225,7 +243,7 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
|
||||
}
|
||||
|
||||
if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 {
|
||||
return libnetwork.NetworkNameError(create.Name)
|
||||
return nameConflict(create.Name)
|
||||
}
|
||||
|
||||
nw, err := n.backend.CreateNetwork(create)
|
||||
@@ -235,7 +253,7 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
|
||||
// check if user defined CheckDuplicate, if set true, return err
|
||||
// otherwise prepare a warning message
|
||||
if create.CheckDuplicate {
|
||||
return libnetwork.NetworkNameError(create.Name)
|
||||
return nameConflict(create.Name)
|
||||
}
|
||||
warning = libnetwork.NetworkNameError(create.Name).Error()
|
||||
}
|
||||
@@ -489,7 +507,7 @@ func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWr
|
||||
return err
|
||||
}
|
||||
|
||||
pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/docker/api/server/router/plugin/plugin_routes.go
generated
vendored
2
vendor/github.com/docker/docker/api/server/router/plugin/plugin_routes.go
generated
vendored
@@ -290,7 +290,7 @@ func (pr *pluginRouter) listPlugins(ctx context.Context, w http.ResponseWriter,
|
||||
return err
|
||||
}
|
||||
|
||||
pluginFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
pluginFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
22
vendor/github.com/docker/docker/api/server/router/swarm/cluster_routes.go
generated
vendored
22
vendor/github.com/docker/docker/api/server/router/swarm/cluster_routes.go
generated
vendored
@@ -151,7 +151,7 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
filter, err := filters.FromParam(r.Form.Get("filters"))
|
||||
filter, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return invalidRequestError{err}
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *h
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
filter, err := filters.FromParam(r.Form.Get("filters"))
|
||||
filter, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -339,7 +339,7 @@ func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *h
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
filter, err := filters.FromParam(r.Form.Get("filters"))
|
||||
filter, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -367,7 +367,7 @@ func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
filters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
filters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -427,18 +427,14 @@ func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter,
|
||||
}
|
||||
|
||||
id := vars["id"]
|
||||
if err := sr.backend.UpdateSecret(id, version, secret); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return sr.backend.UpdateSecret(id, version, secret)
|
||||
}
|
||||
|
||||
func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
filters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
filters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -498,9 +494,5 @@ func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter,
|
||||
}
|
||||
|
||||
id := vars["id"]
|
||||
if err := sr.backend.UpdateConfig(id, version, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return sr.backend.UpdateConfig(id, version, config)
|
||||
}
|
||||
|
||||
3
vendor/github.com/docker/docker/api/server/router/swarm/helpers.go
generated
vendored
3
vendor/github.com/docker/docker/api/server/router/swarm/helpers.go
generated
vendored
@@ -2,6 +2,7 @@ package swarm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
@@ -12,7 +13,7 @@ import (
|
||||
|
||||
// swarmLogs takes an http response, request, and selector, and writes the logs
|
||||
// specified by the selector to the response
|
||||
func (sr *swarmRouter) swarmLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, selector *backend.LogSelector) error {
|
||||
func (sr *swarmRouter) swarmLogs(ctx context.Context, w io.Writer, r *http.Request, selector *backend.LogSelector) error {
|
||||
// Args are validated before the stream starts because when it starts we're
|
||||
// sending HTTP 200 by writing an empty chunk of data to tell the client that
|
||||
// daemon is going to stream. By sending this initial HTTP 200 we can't report
|
||||
|
||||
4
vendor/github.com/docker/docker/api/server/router/system/system_routes.go
generated
vendored
4
vendor/github.com/docker/docker/api/server/router/system/system_routes.go
generated
vendored
@@ -123,11 +123,11 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
|
||||
|
||||
if !onlyPastEvents {
|
||||
dur := until.Sub(now)
|
||||
timeout = time.NewTimer(dur).C
|
||||
timeout = time.After(dur)
|
||||
}
|
||||
}
|
||||
|
||||
ef, err := filters.FromParam(r.Form.Get("filters"))
|
||||
ef, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/docker/api/server/router/volume/volume_routes.go
generated
vendored
2
vendor/github.com/docker/docker/api/server/router/volume/volume_routes.go
generated
vendored
@@ -72,7 +72,7 @@ func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWrit
|
||||
return err
|
||||
}
|
||||
|
||||
pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
|
||||
pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
1
vendor/github.com/docker/docker/api/server/server.go
generated
vendored
1
vendor/github.com/docker/docker/api/server/server.go
generated
vendored
@@ -23,7 +23,6 @@ const versionMatcher = "/v{version:[0-9.]+}"
|
||||
// Config provides the configuration for the API server
|
||||
type Config struct {
|
||||
Logging bool
|
||||
EnableCors bool
|
||||
CorsHeaders string
|
||||
Version string
|
||||
SocketGroup string
|
||||
|
||||
118
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
118
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
@@ -19,10 +19,10 @@ produces:
|
||||
consumes:
|
||||
- "application/json"
|
||||
- "text/plain"
|
||||
basePath: "/v1.32"
|
||||
basePath: "/v1.35"
|
||||
info:
|
||||
title: "Docker Engine API"
|
||||
version: "1.32"
|
||||
version: "1.35"
|
||||
x-logo:
|
||||
url: "https://docs.docker.com/images/logo-docker-main.png"
|
||||
description: |
|
||||
@@ -42,34 +42,26 @@ info:
|
||||
|
||||
# Versioning
|
||||
|
||||
The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break.
|
||||
The API is usually changed in each release, so API calls are versioned to
|
||||
ensure that clients don't break. To lock to a specific version of the API,
|
||||
you prefix the URL with its version, for example, call `/v1.30/info` to use
|
||||
the v1.30 version of the `/info` endpoint. If the API version specified in
|
||||
the URL is not supported by the daemon, a HTTP `400 Bad Request` error message
|
||||
is returned.
|
||||
|
||||
For Docker Engine 17.07, the API version is 1.31. To lock to this version, you prefix the URL with `/v1.31`. For example, calling `/info` is the same as calling `/v1.31/info`.
|
||||
If you omit the version-prefix, the current version of the API (v1.35) is used.
|
||||
For example, calling `/info` is the same as calling `/v1.35/info`. Using the
|
||||
API without a version-prefix is deprecated and will be removed in a future release.
|
||||
|
||||
Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine.
|
||||
Engine releases in the near future should support this version of the API,
|
||||
so your client will continue to work even if it is talking to a newer Engine.
|
||||
|
||||
In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker.
|
||||
The API uses an open schema model, which means server may add extra properties
|
||||
to responses. Likewise, the server will ignore any extra query parameters and
|
||||
request body properties. When you write clients, you need to ignore additional
|
||||
properties in responses to ensure they do not break when talking to newer
|
||||
daemons.
|
||||
|
||||
The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons.
|
||||
|
||||
This documentation is for version 1.32 of the API. Use this table to find documentation for previous versions of the API:
|
||||
|
||||
Docker version | API version | Changes
|
||||
----------------|-------------|---------
|
||||
17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes)
|
||||
17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes)
|
||||
17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes)
|
||||
17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes)
|
||||
17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes)
|
||||
1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes)
|
||||
1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes)
|
||||
1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes)
|
||||
1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes)
|
||||
1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes)
|
||||
1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes)
|
||||
1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes)
|
||||
1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes)
|
||||
1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes)
|
||||
|
||||
# Authentication
|
||||
|
||||
@@ -144,6 +136,10 @@ tags:
|
||||
x-displayName: "Secrets"
|
||||
description: |
|
||||
Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work.
|
||||
- name: "Config"
|
||||
x-displayName: "Configs"
|
||||
description: |
|
||||
Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work.
|
||||
# System things
|
||||
- name: "Plugin"
|
||||
x-displayName: "Plugins"
|
||||
@@ -714,7 +710,15 @@ definitions:
|
||||
description: "Gives the container full access to the host."
|
||||
PublishAllPorts:
|
||||
type: "boolean"
|
||||
description: "Allocates a random host port for all of a container's exposed ports."
|
||||
description: |
|
||||
Allocates an ephemeral host port for all of a container's
|
||||
exposed ports.
|
||||
|
||||
Ports are de-allocated when the container stops and allocated when the container starts.
|
||||
The allocated port might be changed when restarting the container.
|
||||
|
||||
The port is selected from the ephemeral port range that depends on the kernel.
|
||||
For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`.
|
||||
ReadonlyRootfs:
|
||||
type: "boolean"
|
||||
description: "Mount the container's root filesystem as read only."
|
||||
@@ -2672,7 +2676,13 @@ definitions:
|
||||
ConfigName is the name of the config that this references, but this is just provided for
|
||||
lookup/display purposes. The config in the reference will be identified by its ID.
|
||||
type: "string"
|
||||
|
||||
Isolation:
|
||||
type: "string"
|
||||
description: "Isolation technology of the containers running the service. (Windows only)"
|
||||
enum:
|
||||
- "default"
|
||||
- "process"
|
||||
- "hyperv"
|
||||
Resources:
|
||||
description: "Resource requirements which apply to each individual container created as part of the service."
|
||||
type: "object"
|
||||
@@ -3036,6 +3046,24 @@ definitions:
|
||||
PublishedPort:
|
||||
description: "The port on the swarm hosts."
|
||||
type: "integer"
|
||||
PublishMode:
|
||||
description: |
|
||||
The mode in which port is published.
|
||||
|
||||
<p><br /></p>
|
||||
|
||||
- "ingress" makes the target port accessible on on every node,
|
||||
regardless of whether there is a task for the service running on
|
||||
that node or not.
|
||||
- "host" bypasses the routing mesh and publish the port directly on
|
||||
the swarm node where that service is running.
|
||||
|
||||
type: "string"
|
||||
enum:
|
||||
- "ingress"
|
||||
- "host"
|
||||
default: "ingress"
|
||||
example: "ingress"
|
||||
|
||||
EndpointSpec:
|
||||
description: "Properties that can be configured to access and load balance a service."
|
||||
@@ -4929,6 +4957,11 @@ paths:
|
||||
description: "Only return logs since this time, as a UNIX timestamp"
|
||||
type: "integer"
|
||||
default: 0
|
||||
- name: "until"
|
||||
in: "query"
|
||||
description: "Only return logs before this time, as a UNIX timestamp"
|
||||
type: "integer"
|
||||
default: 0
|
||||
- name: "timestamps"
|
||||
in: "query"
|
||||
description: "Add timestamps to every log line"
|
||||
@@ -5697,6 +5730,13 @@ paths:
|
||||
description: "Exit code of the container"
|
||||
type: "integer"
|
||||
x-nullable: false
|
||||
Error:
|
||||
description: "container waiting error, if any"
|
||||
type: "object"
|
||||
properties:
|
||||
Message:
|
||||
description: "Details of an error"
|
||||
type: "string"
|
||||
404:
|
||||
description: "no such container"
|
||||
schema:
|
||||
@@ -6157,6 +6197,11 @@ paths:
|
||||
|
||||
Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API.
|
||||
type: "string"
|
||||
- name: "platform"
|
||||
in: "query"
|
||||
description: "Platform in the format os[/arch[/variant]]"
|
||||
type: "string"
|
||||
default: ""
|
||||
responses:
|
||||
200:
|
||||
description: "no error"
|
||||
@@ -6238,6 +6283,11 @@ paths:
|
||||
in: "header"
|
||||
description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
|
||||
type: "string"
|
||||
- name: "platform"
|
||||
in: "query"
|
||||
description: "Platform in the format os[/arch[/variant]]"
|
||||
type: "string"
|
||||
default: ""
|
||||
tags: ["Image"]
|
||||
/images/{name}/json:
|
||||
get:
|
||||
@@ -6936,16 +6986,20 @@ paths:
|
||||
description: |
|
||||
A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters:
|
||||
|
||||
- `config=<string>` config name or ID
|
||||
- `container=<string>` container name or ID
|
||||
- `daemon=<string>` daemon name or ID
|
||||
- `event=<string>` event type
|
||||
- `image=<string>` image name or ID
|
||||
- `label=<string>` image or container label
|
||||
- `network=<string>` network name or ID
|
||||
- `node=<string>` node ID
|
||||
- `plugin`=<string> plugin name or ID
|
||||
- `scope`=<string> local or swarm
|
||||
- `type=<string>` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service` or `secret`
|
||||
- `volume=<string>` volume name or ID
|
||||
- `scope`=<string> local or swarm
|
||||
- `secret=<string>` secret name or ID
|
||||
- `service=<string>` service name or ID
|
||||
- `type=<string>` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config`
|
||||
- `volume=<string>` volume name
|
||||
type: "string"
|
||||
tags: ["System"]
|
||||
/system/df:
|
||||
@@ -7837,7 +7891,7 @@ paths:
|
||||
summary: "Connect a container to a network"
|
||||
operationId: "NetworkConnect"
|
||||
consumes:
|
||||
- "application/octet-stream"
|
||||
- "application/json"
|
||||
responses:
|
||||
200:
|
||||
description: "No error"
|
||||
|
||||
2
vendor/github.com/docker/docker/api/types/backend/build.go
generated
vendored
2
vendor/github.com/docker/docker/api/types/backend/build.go
generated
vendored
@@ -40,5 +40,5 @@ type GetImageAndLayerOptions struct {
|
||||
PullOption PullOption
|
||||
AuthConfig map[string]types.AuthConfig
|
||||
Output io.Writer
|
||||
Platform string
|
||||
OS string
|
||||
}
|
||||
|
||||
17
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
17
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
@@ -74,6 +74,7 @@ type ContainerLogsOptions struct {
|
||||
ShowStdout bool
|
||||
ShowStderr bool
|
||||
Since string
|
||||
Until string
|
||||
Timestamps bool
|
||||
Follow bool
|
||||
Tail string
|
||||
@@ -179,10 +180,7 @@ type ImageBuildOptions struct {
|
||||
ExtraHosts []string // List of extra hosts
|
||||
Target string
|
||||
SessionID string
|
||||
|
||||
// TODO @jhowardmsft LCOW Support: This will require extending to include
|
||||
// `Platform string`, but is ommited for now as it's hard-coded temporarily
|
||||
// to avoid API changes.
|
||||
Platform string
|
||||
}
|
||||
|
||||
// ImageBuildResponse holds information
|
||||
@@ -195,7 +193,8 @@ type ImageBuildResponse struct {
|
||||
|
||||
// ImageCreateOptions holds information to create images.
|
||||
type ImageCreateOptions struct {
|
||||
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry.
|
||||
Platform string // Platform is the target platform of the image if it needs to be pulled from the registry.
|
||||
}
|
||||
|
||||
// ImageImportSource holds source information for ImageImport
|
||||
@@ -206,9 +205,10 @@ type ImageImportSource struct {
|
||||
|
||||
// ImageImportOptions holds information to import images from the client host.
|
||||
type ImageImportOptions struct {
|
||||
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
|
||||
Message string // Message is the message to tag the image with
|
||||
Changes []string // Changes are the raw changes to apply to this image
|
||||
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
|
||||
Message string // Message is the message to tag the image with
|
||||
Changes []string // Changes are the raw changes to apply to this image
|
||||
Platform string // Platform is the target platform of the image
|
||||
}
|
||||
|
||||
// ImageListOptions holds parameters to filter the list of images with.
|
||||
@@ -229,6 +229,7 @@ type ImagePullOptions struct {
|
||||
All bool
|
||||
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||
PrivilegeFunc RequestPrivilegeFunc
|
||||
Platform string
|
||||
}
|
||||
|
||||
// RequestPrivilegeFunc is a function interface that
|
||||
|
||||
1
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
1
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
@@ -16,7 +16,6 @@ type ContainerCreateConfig struct {
|
||||
HostConfig *container.HostConfig
|
||||
NetworkingConfig *network.NetworkingConfig
|
||||
AdjustCPUShares bool
|
||||
Platform string
|
||||
}
|
||||
|
||||
// ContainerRmConfig holds arguments for the container remove
|
||||
|
||||
12
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
12
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
@@ -7,10 +7,22 @@ package container
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ContainerWaitOKBodyError container waiting error, if any
|
||||
// swagger:model ContainerWaitOKBodyError
|
||||
type ContainerWaitOKBodyError struct {
|
||||
|
||||
// Details of an error
|
||||
Message string `json:"Message,omitempty"`
|
||||
}
|
||||
|
||||
// ContainerWaitOKBody container wait o k body
|
||||
// swagger:model ContainerWaitOKBody
|
||||
type ContainerWaitOKBody struct {
|
||||
|
||||
// error
|
||||
// Required: true
|
||||
Error *ContainerWaitOKBodyError `json:"Error"`
|
||||
|
||||
// Exit code of the container
|
||||
// Required: true
|
||||
StatusCode int64 `json:"StatusCode"`
|
||||
|
||||
21
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
21
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
@@ -20,6 +20,27 @@ func (i Isolation) IsDefault() bool {
|
||||
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
||||
}
|
||||
|
||||
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||
func (i Isolation) IsHyperV() bool {
|
||||
return strings.ToLower(string(i)) == "hyperv"
|
||||
}
|
||||
|
||||
// IsProcess indicates the use of process isolation
|
||||
func (i Isolation) IsProcess() bool {
|
||||
return strings.ToLower(string(i)) == "process"
|
||||
}
|
||||
|
||||
const (
|
||||
// IsolationEmpty is unspecified (same behavior as default)
|
||||
IsolationEmpty = Isolation("")
|
||||
// IsolationDefault is the default isolation mode on current daemon
|
||||
IsolationDefault = Isolation("default")
|
||||
// IsolationProcess is process isolation mode
|
||||
IsolationProcess = Isolation("process")
|
||||
// IsolationHyperV is HyperV isolation mode
|
||||
IsolationHyperV = Isolation("hyperv")
|
||||
)
|
||||
|
||||
// IpcMode represents the container ipc stack.
|
||||
type IpcMode string
|
||||
|
||||
|
||||
14
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
14
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
@@ -1,9 +1,5 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsBridge indicates whether container uses the bridge network stack
|
||||
// in windows it is given the name NAT
|
||||
func (n NetworkMode) IsBridge() bool {
|
||||
@@ -21,16 +17,6 @@ func (n NetworkMode) IsUserDefined() bool {
|
||||
return !n.IsDefault() && !n.IsNone() && !n.IsBridge() && !n.IsContainer()
|
||||
}
|
||||
|
||||
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||
func (i Isolation) IsHyperV() bool {
|
||||
return strings.ToLower(string(i)) == "hyperv"
|
||||
}
|
||||
|
||||
// IsProcess indicates the use of process isolation
|
||||
func (i Isolation) IsProcess() bool {
|
||||
return strings.ToLower(string(i)) == "process"
|
||||
}
|
||||
|
||||
// IsValid indicates if an isolation technology is valid
|
||||
func (i Isolation) IsValid() bool {
|
||||
return i.IsDefault() || i.IsHyperV() || i.IsProcess()
|
||||
|
||||
24
vendor/github.com/docker/docker/api/types/filters/example_test.go
generated
vendored
Normal file
24
vendor/github.com/docker/docker/api/types/filters/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package filters
|
||||
|
||||
func ExampleArgs_MatchKVList() {
|
||||
args := NewArgs(
|
||||
Arg("label", "image=foo"),
|
||||
Arg("label", "state=running"))
|
||||
|
||||
// returns true because there are no values for bogus
|
||||
args.MatchKVList("bogus", nil)
|
||||
|
||||
// returns false because there are no sources
|
||||
args.MatchKVList("label", nil)
|
||||
|
||||
// returns true because all sources are matched
|
||||
args.MatchKVList("label", map[string]string{
|
||||
"image": "foo",
|
||||
"state": "running",
|
||||
})
|
||||
|
||||
// returns false because the values do not match
|
||||
args.MatchKVList("label", map[string]string{
|
||||
"image": "other",
|
||||
})
|
||||
}
|
||||
269
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
269
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
@@ -1,5 +1,6 @@
|
||||
// Package filters provides helper function to parse and handle command line
|
||||
// filter, used for example in docker ps or docker images commands.
|
||||
/*Package filters provides tools for encoding a mapping of keys to a set of
|
||||
multiple values.
|
||||
*/
|
||||
package filters
|
||||
|
||||
import (
|
||||
@@ -11,27 +12,34 @@ import (
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
)
|
||||
|
||||
// Args stores filter arguments as map key:{map key: bool}.
|
||||
// It contains an aggregation of the map of arguments (which are in the form
|
||||
// of -f 'key=value') based on the key, and stores values for the same key
|
||||
// in a map with string keys and boolean values.
|
||||
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
|
||||
// the args will be {"image.name":{"ubuntu":true},"label":{"label1=1":true,"label2=2":true}}
|
||||
// Args stores a mapping of keys to a set of multiple values.
|
||||
type Args struct {
|
||||
fields map[string]map[string]bool
|
||||
}
|
||||
|
||||
// NewArgs initializes a new Args struct.
|
||||
func NewArgs() Args {
|
||||
return Args{fields: map[string]map[string]bool{}}
|
||||
// KeyValuePair are used to initialize a new Args
|
||||
type KeyValuePair struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// ParseFlag parses the argument to the filter flag. Like
|
||||
// Arg creates a new KeyValuePair for initializing Args
|
||||
func Arg(key, value string) KeyValuePair {
|
||||
return KeyValuePair{Key: key, Value: value}
|
||||
}
|
||||
|
||||
// NewArgs returns a new Args populated with the initial args
|
||||
func NewArgs(initialArgs ...KeyValuePair) Args {
|
||||
args := Args{fields: map[string]map[string]bool{}}
|
||||
for _, arg := range initialArgs {
|
||||
args.Add(arg.Key, arg.Value)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// ParseFlag parses a key=value string and adds it to an Args.
|
||||
//
|
||||
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
||||
//
|
||||
// If prev map is provided, then it is appended to, and returned. By default a new
|
||||
// map is created.
|
||||
// Deprecated: Use Args.Add()
|
||||
func ParseFlag(arg string, prev Args) (Args, error) {
|
||||
filters := prev
|
||||
if len(arg) == 0 {
|
||||
@@ -52,74 +60,95 @@ func ParseFlag(arg string, prev Args) (Args, error) {
|
||||
return filters, nil
|
||||
}
|
||||
|
||||
// ErrBadFormat is an error returned in case of bad format for a filter.
|
||||
// ErrBadFormat is an error returned when a filter is not in the form key=value
|
||||
//
|
||||
// Deprecated: this error will be removed in a future version
|
||||
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
|
||||
|
||||
// ToParam packs the Args into a string for easy transport from client to server.
|
||||
// ToParam encodes the Args as args JSON encoded string
|
||||
//
|
||||
// Deprecated: use ToJSON
|
||||
func ToParam(a Args) (string, error) {
|
||||
// this way we don't URL encode {}, just empty space
|
||||
return ToJSON(a)
|
||||
}
|
||||
|
||||
// MarshalJSON returns a JSON byte representation of the Args
|
||||
func (args Args) MarshalJSON() ([]byte, error) {
|
||||
if len(args.fields) == 0 {
|
||||
return []byte{}, nil
|
||||
}
|
||||
return json.Marshal(args.fields)
|
||||
}
|
||||
|
||||
// ToJSON returns the Args as a JSON encoded string
|
||||
func ToJSON(a Args) (string, error) {
|
||||
if a.Len() == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(a.fields)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf), nil
|
||||
buf, err := json.Marshal(a)
|
||||
return string(buf), err
|
||||
}
|
||||
|
||||
// ToParamWithVersion packs the Args into a string for easy transport from client to server.
|
||||
// The generated string will depend on the specified version (corresponding to the API version).
|
||||
// ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22
|
||||
// then the encoded format will use an older legacy format where the values are a
|
||||
// list of strings, instead of a set.
|
||||
//
|
||||
// Deprecated: Use ToJSON
|
||||
func ToParamWithVersion(version string, a Args) (string, error) {
|
||||
// this way we don't URL encode {}, just empty space
|
||||
if a.Len() == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// for daemons older than v1.10, filter must be of the form map[string][]string
|
||||
var buf []byte
|
||||
var err error
|
||||
if version != "" && versions.LessThan(version, "1.22") {
|
||||
buf, err = json.Marshal(convertArgsToSlice(a.fields))
|
||||
} else {
|
||||
buf, err = json.Marshal(a.fields)
|
||||
buf, err := json.Marshal(convertArgsToSlice(a.fields))
|
||||
return string(buf), err
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf), nil
|
||||
|
||||
return ToJSON(a)
|
||||
}
|
||||
|
||||
// FromParam unpacks the filter Args.
|
||||
// FromParam decodes a JSON encoded string into Args
|
||||
//
|
||||
// Deprecated: use FromJSON
|
||||
func FromParam(p string) (Args, error) {
|
||||
if len(p) == 0 {
|
||||
return NewArgs(), nil
|
||||
}
|
||||
|
||||
r := strings.NewReader(p)
|
||||
d := json.NewDecoder(r)
|
||||
|
||||
m := map[string]map[string]bool{}
|
||||
if err := d.Decode(&m); err != nil {
|
||||
r.Seek(0, 0)
|
||||
|
||||
// Allow parsing old arguments in slice format.
|
||||
// Because other libraries might be sending them in this format.
|
||||
deprecated := map[string][]string{}
|
||||
if deprecatedErr := d.Decode(&deprecated); deprecatedErr == nil {
|
||||
m = deprecatedArgs(deprecated)
|
||||
} else {
|
||||
return NewArgs(), err
|
||||
}
|
||||
}
|
||||
return Args{m}, nil
|
||||
return FromJSON(p)
|
||||
}
|
||||
|
||||
// Get returns the list of values associates with a field.
|
||||
// It returns a slice of strings to keep backwards compatibility with old code.
|
||||
func (filters Args) Get(field string) []string {
|
||||
values := filters.fields[field]
|
||||
// FromJSON decodes a JSON encoded string into Args
|
||||
func FromJSON(p string) (Args, error) {
|
||||
args := NewArgs()
|
||||
|
||||
if p == "" {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
raw := []byte(p)
|
||||
err := json.Unmarshal(raw, &args)
|
||||
if err == nil {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// Fallback to parsing arguments in the legacy slice format
|
||||
deprecated := map[string][]string{}
|
||||
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
|
||||
return args, err
|
||||
}
|
||||
|
||||
args.fields = deprecatedArgs(deprecated)
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON populates the Args from JSON encode bytes
|
||||
func (args Args) UnmarshalJSON(raw []byte) error {
|
||||
if len(raw) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(raw, &args.fields)
|
||||
}
|
||||
|
||||
// Get returns the list of values associated with the key
|
||||
func (args Args) Get(key string) []string {
|
||||
values := args.fields[key]
|
||||
if values == nil {
|
||||
return make([]string, 0)
|
||||
}
|
||||
@@ -130,37 +159,34 @@ func (filters Args) Get(field string) []string {
|
||||
return slice
|
||||
}
|
||||
|
||||
// Add adds a new value to a filter field.
|
||||
func (filters Args) Add(name, value string) {
|
||||
if _, ok := filters.fields[name]; ok {
|
||||
filters.fields[name][value] = true
|
||||
// Add a new value to the set of values
|
||||
func (args Args) Add(key, value string) {
|
||||
if _, ok := args.fields[key]; ok {
|
||||
args.fields[key][value] = true
|
||||
} else {
|
||||
filters.fields[name] = map[string]bool{value: true}
|
||||
args.fields[key] = map[string]bool{value: true}
|
||||
}
|
||||
}
|
||||
|
||||
// Del removes a value from a filter field.
|
||||
func (filters Args) Del(name, value string) {
|
||||
if _, ok := filters.fields[name]; ok {
|
||||
delete(filters.fields[name], value)
|
||||
if len(filters.fields[name]) == 0 {
|
||||
delete(filters.fields, name)
|
||||
// Del removes a value from the set
|
||||
func (args Args) Del(key, value string) {
|
||||
if _, ok := args.fields[key]; ok {
|
||||
delete(args.fields[key], value)
|
||||
if len(args.fields[key]) == 0 {
|
||||
delete(args.fields, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of fields in the arguments.
|
||||
func (filters Args) Len() int {
|
||||
return len(filters.fields)
|
||||
// Len returns the number of keys in the mapping
|
||||
func (args Args) Len() int {
|
||||
return len(args.fields)
|
||||
}
|
||||
|
||||
// MatchKVList returns true if the values for the specified field matches the ones
|
||||
// from the sources.
|
||||
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||
// field is 'label' and sources are {'label1': '1', 'label2': '2'}
|
||||
// it returns true.
|
||||
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||
fieldValues := filters.fields[field]
|
||||
// MatchKVList returns true if all the pairs in sources exist as key=value
|
||||
// pairs in the mapping at key, or if there are no values at key.
|
||||
func (args Args) MatchKVList(key string, sources map[string]string) bool {
|
||||
fieldValues := args.fields[key]
|
||||
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
if len(fieldValues) == 0 {
|
||||
@@ -171,8 +197,8 @@ func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
for name2match := range fieldValues {
|
||||
testKV := strings.SplitN(name2match, "=", 2)
|
||||
for value := range fieldValues {
|
||||
testKV := strings.SplitN(value, "=", 2)
|
||||
|
||||
v, ok := sources[testKV[0]]
|
||||
if !ok {
|
||||
@@ -186,16 +212,13 @@ func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Match returns true if the values for the specified field matches the source string
|
||||
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||
// field is 'image.name' and source is 'ubuntu'
|
||||
// it returns true.
|
||||
func (filters Args) Match(field, source string) bool {
|
||||
if filters.ExactMatch(field, source) {
|
||||
// Match returns true if any of the values at key match the source string
|
||||
func (args Args) Match(field, source string) bool {
|
||||
if args.ExactMatch(field, source) {
|
||||
return true
|
||||
}
|
||||
|
||||
fieldValues := filters.fields[field]
|
||||
fieldValues := args.fields[field]
|
||||
for name2match := range fieldValues {
|
||||
match, err := regexp.MatchString(name2match, source)
|
||||
if err != nil {
|
||||
@@ -208,9 +231,9 @@ func (filters Args) Match(field, source string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ExactMatch returns true if the source matches exactly one of the filters.
|
||||
func (filters Args) ExactMatch(field, source string) bool {
|
||||
fieldValues, ok := filters.fields[field]
|
||||
// ExactMatch returns true if the source matches exactly one of the values.
|
||||
func (args Args) ExactMatch(key, source string) bool {
|
||||
fieldValues, ok := args.fields[key]
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
if !ok || len(fieldValues) == 0 {
|
||||
return true
|
||||
@@ -220,14 +243,15 @@ func (filters Args) ExactMatch(field, source string) bool {
|
||||
return fieldValues[source]
|
||||
}
|
||||
|
||||
// UniqueExactMatch returns true if there is only one filter and the source matches exactly this one.
|
||||
func (filters Args) UniqueExactMatch(field, source string) bool {
|
||||
fieldValues := filters.fields[field]
|
||||
// UniqueExactMatch returns true if there is only one value and the source
|
||||
// matches exactly the value.
|
||||
func (args Args) UniqueExactMatch(key, source string) bool {
|
||||
fieldValues := args.fields[key]
|
||||
//do not filter if there is no filter set or cannot determine filter
|
||||
if len(fieldValues) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(filters.fields[field]) != 1 {
|
||||
if len(args.fields[key]) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -235,14 +259,14 @@ func (filters Args) UniqueExactMatch(field, source string) bool {
|
||||
return fieldValues[source]
|
||||
}
|
||||
|
||||
// FuzzyMatch returns true if the source matches exactly one of the filters,
|
||||
// or the source has one of the filters as a prefix.
|
||||
func (filters Args) FuzzyMatch(field, source string) bool {
|
||||
if filters.ExactMatch(field, source) {
|
||||
// FuzzyMatch returns true if the source matches exactly one value, or the
|
||||
// source has one of the values as a prefix.
|
||||
func (args Args) FuzzyMatch(key, source string) bool {
|
||||
if args.ExactMatch(key, source) {
|
||||
return true
|
||||
}
|
||||
|
||||
fieldValues := filters.fields[field]
|
||||
fieldValues := args.fields[key]
|
||||
for prefix := range fieldValues {
|
||||
if strings.HasPrefix(source, prefix) {
|
||||
return true
|
||||
@@ -251,9 +275,17 @@ func (filters Args) FuzzyMatch(field, source string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Include returns true if the name of the field to filter is in the filters.
|
||||
func (filters Args) Include(field string) bool {
|
||||
_, ok := filters.fields[field]
|
||||
// Include returns true if the key exists in the mapping
|
||||
//
|
||||
// Deprecated: use Contains
|
||||
func (args Args) Include(field string) bool {
|
||||
_, ok := args.fields[field]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Contains returns true if the key exists in the mapping
|
||||
func (args Args) Contains(field string) bool {
|
||||
_, ok := args.fields[field]
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -265,10 +297,10 @@ func (e invalidFilter) Error() string {
|
||||
|
||||
func (invalidFilter) InvalidParameter() {}
|
||||
|
||||
// Validate ensures that all the fields in the filter are valid.
|
||||
// It returns an error as soon as it finds an invalid field.
|
||||
func (filters Args) Validate(accepted map[string]bool) error {
|
||||
for name := range filters.fields {
|
||||
// Validate compared the set of accepted keys against the keys in the mapping.
|
||||
// An error is returned if any mapping keys are not in the accepted set.
|
||||
func (args Args) Validate(accepted map[string]bool) error {
|
||||
for name := range args.fields {
|
||||
if !accepted[name] {
|
||||
return invalidFilter(name)
|
||||
}
|
||||
@@ -276,13 +308,14 @@ func (filters Args) Validate(accepted map[string]bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WalkValues iterates over the list of filtered values for a field.
|
||||
// It stops the iteration if it finds an error and it returns that error.
|
||||
func (filters Args) WalkValues(field string, op func(value string) error) error {
|
||||
if _, ok := filters.fields[field]; !ok {
|
||||
// WalkValues iterates over the list of values for a key in the mapping and calls
|
||||
// op() for each value. If op returns an error the iteration stops and the
|
||||
// error is returned.
|
||||
func (args Args) WalkValues(field string, op func(value string) error) error {
|
||||
if _, ok := args.fields[field]; !ok {
|
||||
return nil
|
||||
}
|
||||
for v := range filters.fields[field] {
|
||||
for v := range args.fields[field] {
|
||||
if err := op(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
59
vendor/github.com/docker/docker/api/types/filters/parse_test.go
generated
vendored
59
vendor/github.com/docker/docker/api/types/filters/parse_test.go
generated
vendored
@@ -3,6 +3,9 @@ package filters
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseArgs(t *testing.T) {
|
||||
@@ -16,23 +19,18 @@ func TestParseArgs(t *testing.T) {
|
||||
args = NewArgs()
|
||||
err error
|
||||
)
|
||||
|
||||
for i := range flagArgs {
|
||||
args, err = ParseFlag(flagArgs[i], args)
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse %s: %s", flagArgs[i], err)
|
||||
}
|
||||
}
|
||||
if len(args.Get("created")) != 1 {
|
||||
t.Error("failed to set this arg")
|
||||
}
|
||||
if len(args.Get("image.name")) != 2 {
|
||||
t.Error("the args should have collapsed")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Len(t, args.Get("created"), 1)
|
||||
assert.Len(t, args.Get("image.name"), 2)
|
||||
}
|
||||
|
||||
func TestParseArgsEdgeCase(t *testing.T) {
|
||||
var filters Args
|
||||
args, err := ParseFlag("", filters)
|
||||
var args Args
|
||||
args, err := ParseFlag("", args)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -44,14 +42,14 @@ func TestParseArgsEdgeCase(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestToParam(t *testing.T) {
|
||||
func TestToJSON(t *testing.T) {
|
||||
fields := map[string]map[string]bool{
|
||||
"created": {"today": true},
|
||||
"image.name": {"ubuntu*": true, "*untu": true},
|
||||
}
|
||||
a := Args{fields: fields}
|
||||
|
||||
_, err := ToParam(a)
|
||||
_, err := ToJSON(a)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal the filters: %s", err)
|
||||
}
|
||||
@@ -82,7 +80,7 @@ func TestToParamWithVersion(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromParam(t *testing.T) {
|
||||
func TestFromJSON(t *testing.T) {
|
||||
invalids := []string{
|
||||
"anything",
|
||||
"['a','list']",
|
||||
@@ -105,14 +103,14 @@ func TestFromParam(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, invalid := range invalids {
|
||||
if _, err := FromParam(invalid); err == nil {
|
||||
if _, err := FromJSON(invalid); err == nil {
|
||||
t.Fatalf("Expected an error with %v, got nothing", invalid)
|
||||
}
|
||||
}
|
||||
|
||||
for expectedArgs, matchers := range valid {
|
||||
for _, json := range matchers {
|
||||
args, err := FromParam(json)
|
||||
args, err := FromJSON(json)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -138,11 +136,11 @@ func TestFromParam(t *testing.T) {
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
a := Args{}
|
||||
v, err := ToParam(a)
|
||||
v, err := ToJSON(a)
|
||||
if err != nil {
|
||||
t.Errorf("failed to marshal the filters: %s", err)
|
||||
}
|
||||
v1, err := FromParam(v)
|
||||
v1, err := FromJSON(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
@@ -184,7 +182,7 @@ func TestArgsMatchKVList(t *testing.T) {
|
||||
}
|
||||
|
||||
for args, field := range matches {
|
||||
if args.MatchKVList(field, sources) != true {
|
||||
if !args.MatchKVList(field, sources) {
|
||||
t.Fatalf("Expected true for %v on %v, got false", sources, args)
|
||||
}
|
||||
}
|
||||
@@ -204,7 +202,7 @@ func TestArgsMatchKVList(t *testing.T) {
|
||||
}
|
||||
|
||||
for args, field := range differs {
|
||||
if args.MatchKVList(field, sources) != false {
|
||||
if args.MatchKVList(field, sources) {
|
||||
t.Fatalf("Expected false for %v on %v, got true", sources, args)
|
||||
}
|
||||
}
|
||||
@@ -233,9 +231,8 @@ func TestArgsMatch(t *testing.T) {
|
||||
}
|
||||
|
||||
for args, field := range matches {
|
||||
if args.Match(field, source) != true {
|
||||
t.Fatalf("Expected true for %v on %v, got false", source, args)
|
||||
}
|
||||
assert.True(t, args.Match(field, source),
|
||||
"Expected field %s to match %s", field, source)
|
||||
}
|
||||
|
||||
differs := map[*Args]string{
|
||||
@@ -258,9 +255,8 @@ func TestArgsMatch(t *testing.T) {
|
||||
}
|
||||
|
||||
for args, field := range differs {
|
||||
if args.Match(field, source) != false {
|
||||
t.Fatalf("Expected false for %v on %v, got true", source, args)
|
||||
}
|
||||
assert.False(t, args.Match(field, source),
|
||||
"Expected field %s to not match %s", field, source)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,6 +337,17 @@ func TestOnlyOneExactMatch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
f := NewArgs()
|
||||
if f.Contains("status") {
|
||||
t.Fatal("Expected to not contain a status key, got true")
|
||||
}
|
||||
f.Add("status", "running")
|
||||
if !f.Contains("status") {
|
||||
t.Fatal("Expected to contain a status key, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInclude(t *testing.T) {
|
||||
f := NewArgs()
|
||||
if f.Include("status") {
|
||||
|
||||
9
vendor/github.com/docker/docker/api/types/swarm/container.go
generated
vendored
9
vendor/github.com/docker/docker/api/types/swarm/container.go
generated
vendored
@@ -65,8 +65,9 @@ type ContainerSpec struct {
|
||||
// The format of extra hosts on swarmkit is specified in:
|
||||
// http://man7.org/linux/man-pages/man5/hosts.5.html
|
||||
// IP_address canonical_hostname [aliases...]
|
||||
Hosts []string `json:",omitempty"`
|
||||
DNSConfig *DNSConfig `json:",omitempty"`
|
||||
Secrets []*SecretReference `json:",omitempty"`
|
||||
Configs []*ConfigReference `json:",omitempty"`
|
||||
Hosts []string `json:",omitempty"`
|
||||
DNSConfig *DNSConfig `json:",omitempty"`
|
||||
Secrets []*SecretReference `json:",omitempty"`
|
||||
Configs []*ConfigReference `json:",omitempty"`
|
||||
Isolation container.Isolation `json:",omitempty"`
|
||||
}
|
||||
|
||||
4
vendor/github.com/docker/docker/api/types/time/timestamp.go
generated
vendored
4
vendor/github.com/docker/docker/api/types/time/timestamp.go
generated
vendored
@@ -29,10 +29,8 @@ func GetTimestamp(value string, reference time.Time) (string, error) {
|
||||
}
|
||||
|
||||
var format string
|
||||
var parseInLocation bool
|
||||
|
||||
// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
|
||||
parseInLocation = !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
|
||||
parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
|
||||
|
||||
if strings.Contains(value, ".") {
|
||||
if parseInLocation {
|
||||
|
||||
6
vendor/github.com/docker/docker/builder/builder.go
generated
vendored
6
vendor/github.com/docker/docker/builder/builder.go
generated
vendored
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
containerpkg "github.com/docker/docker/container"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -24,7 +25,7 @@ const (
|
||||
// instructions in the builder.
|
||||
type Source interface {
|
||||
// Root returns root path for accessing source
|
||||
Root() string
|
||||
Root() containerfs.ContainerFS
|
||||
// Close allows to signal that the filesystem tree won't be used anymore.
|
||||
// For Context implementations using a temporary directory, it is recommended to
|
||||
// delete the temporary directory in Close().
|
||||
@@ -94,12 +95,13 @@ type Image interface {
|
||||
ImageID() string
|
||||
RunConfig() *container.Config
|
||||
MarshalJSON() ([]byte, error)
|
||||
OperatingSystem() string
|
||||
}
|
||||
|
||||
// ReleaseableLayer is an image layer that can be mounted and released
|
||||
type ReleaseableLayer interface {
|
||||
Release() error
|
||||
Mount() (string, error)
|
||||
Mount() (containerfs.ContainerFS, error)
|
||||
Commit(platform string) (ReleaseableLayer, error)
|
||||
DiffID() layer.DiffID
|
||||
}
|
||||
|
||||
20
vendor/github.com/docker/docker/builder/dockerfile/buildargs.go
generated
vendored
20
vendor/github.com/docker/docker/builder/dockerfile/buildargs.go
generated
vendored
@@ -42,6 +42,26 @@ func newBuildArgs(argsFromOptions map[string]*string) *buildArgs {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *buildArgs) Clone() *buildArgs {
|
||||
result := newBuildArgs(b.argsFromOptions)
|
||||
for k, v := range b.allowedBuildArgs {
|
||||
result.allowedBuildArgs[k] = v
|
||||
}
|
||||
for k, v := range b.allowedMetaArgs {
|
||||
result.allowedMetaArgs[k] = v
|
||||
}
|
||||
for k := range b.referencedArgs {
|
||||
result.referencedArgs[k] = struct{}{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (b *buildArgs) MergeReferencedArgs(other *buildArgs) {
|
||||
for k := range other.referencedArgs {
|
||||
b.referencedArgs[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// WarnOnUnusedBuildArgs checks if there are any leftover build-args that were
|
||||
// passed but not consumed during build. Print a warning, if there are any.
|
||||
func (b *buildArgs) WarnOnUnusedBuildArgs(out io.Writer) {
|
||||
|
||||
318
vendor/github.com/docker/docker/builder/dockerfile/builder.go
generated
vendored
318
vendor/github.com/docker/docker/builder/dockerfile/builder.go
generated
vendored
@@ -13,12 +13,10 @@ import (
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/builder/dockerfile/instructions"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/docker/docker/builder/fscache"
|
||||
"github.com/docker/docker/builder/remotecontext"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
@@ -43,6 +41,10 @@ var validCommitCommands = map[string]bool{
|
||||
"workdir": true,
|
||||
}
|
||||
|
||||
const (
|
||||
stepFormat = "Step %d/%d : %v"
|
||||
)
|
||||
|
||||
// SessionGetter is object used to get access to a session by uuid
|
||||
type SessionGetter interface {
|
||||
Get(ctx context.Context, uuid string) (session.Caller, error)
|
||||
@@ -50,21 +52,21 @@ type SessionGetter interface {
|
||||
|
||||
// BuildManager is shared across all Builder objects
|
||||
type BuildManager struct {
|
||||
archiver *archive.Archiver
|
||||
backend builder.Backend
|
||||
pathCache pathCache // TODO: make this persistent
|
||||
sg SessionGetter
|
||||
fsCache *fscache.FSCache
|
||||
idMappings *idtools.IDMappings
|
||||
backend builder.Backend
|
||||
pathCache pathCache // TODO: make this persistent
|
||||
sg SessionGetter
|
||||
fsCache *fscache.FSCache
|
||||
}
|
||||
|
||||
// NewBuildManager creates a BuildManager
|
||||
func NewBuildManager(b builder.Backend, sg SessionGetter, fsCache *fscache.FSCache, idMappings *idtools.IDMappings) (*BuildManager, error) {
|
||||
bm := &BuildManager{
|
||||
backend: b,
|
||||
pathCache: &syncmap.Map{},
|
||||
sg: sg,
|
||||
archiver: chrootarchive.NewArchiver(idMappings),
|
||||
fsCache: fsCache,
|
||||
backend: b,
|
||||
pathCache: &syncmap.Map{},
|
||||
sg: sg,
|
||||
idMappings: idMappings,
|
||||
fsCache: fsCache,
|
||||
}
|
||||
if err := fsCache.RegisterTransport(remotecontext.ClientSessionRemote, NewClientSessionTransport()); err != nil {
|
||||
return nil, err
|
||||
@@ -91,15 +93,6 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO @jhowardmsft LCOW support - this will require rework to allow both linux and Windows simultaneously.
|
||||
// This is an interim solution to hardcode to linux if LCOW is turned on.
|
||||
if dockerfile.Platform == "" {
|
||||
dockerfile.Platform = runtime.GOOS
|
||||
if dockerfile.Platform == "windows" && system.LCOWSupported() {
|
||||
dockerfile.Platform = "linux"
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -109,16 +102,27 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
|
||||
source = src
|
||||
}
|
||||
|
||||
os := runtime.GOOS
|
||||
optionsPlatform := system.ParsePlatform(config.Options.Platform)
|
||||
if dockerfile.OS != "" {
|
||||
if optionsPlatform.OS != "" && optionsPlatform.OS != dockerfile.OS {
|
||||
return nil, fmt.Errorf("invalid platform")
|
||||
}
|
||||
os = dockerfile.OS
|
||||
} else if optionsPlatform.OS != "" {
|
||||
os = optionsPlatform.OS
|
||||
}
|
||||
config.Options.Platform = os
|
||||
dockerfile.OS = os
|
||||
|
||||
builderOptions := builderOptions{
|
||||
Options: config.Options,
|
||||
ProgressWriter: config.ProgressWriter,
|
||||
Backend: bm.backend,
|
||||
PathCache: bm.pathCache,
|
||||
Archiver: bm.archiver,
|
||||
Platform: dockerfile.Platform,
|
||||
IDMappings: bm.idMappings,
|
||||
}
|
||||
|
||||
return newBuilder(ctx, builderOptions).build(source, dockerfile)
|
||||
return newBuilder(ctx, builderOptions, os).build(source, dockerfile)
|
||||
}
|
||||
|
||||
func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func(), options *types.ImageBuildOptions) (builder.Source, error) {
|
||||
@@ -127,10 +131,10 @@ func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func
|
||||
}
|
||||
logrus.Debug("client is session enabled")
|
||||
|
||||
ctx, cancelCtx := context.WithTimeout(ctx, sessionConnectTimeout)
|
||||
connectCtx, cancelCtx := context.WithTimeout(ctx, sessionConnectTimeout)
|
||||
defer cancelCtx()
|
||||
|
||||
c, err := bm.sg.Get(ctx, options.SessionID)
|
||||
c, err := bm.sg.Get(connectCtx, options.SessionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -160,8 +164,7 @@ type builderOptions struct {
|
||||
Backend builder.Backend
|
||||
ProgressWriter backend.ProgressWriter
|
||||
PathCache pathCache
|
||||
Archiver *archive.Archiver
|
||||
Platform string
|
||||
IDMappings *idtools.IDMappings
|
||||
}
|
||||
|
||||
// Builder is a Dockerfile builder
|
||||
@@ -177,40 +180,21 @@ type Builder struct {
|
||||
docker builder.Backend
|
||||
clientCtx context.Context
|
||||
|
||||
archiver *archive.Archiver
|
||||
buildStages *buildStages
|
||||
idMappings *idtools.IDMappings
|
||||
disableCommit bool
|
||||
buildArgs *buildArgs
|
||||
imageSources *imageSources
|
||||
pathCache pathCache
|
||||
containerManager *containerManager
|
||||
imageProber ImageProber
|
||||
|
||||
// TODO @jhowardmft LCOW Support. This will be moved to options at a later
|
||||
// stage, however that cannot be done now as it affects the public API
|
||||
// if it were.
|
||||
platform string
|
||||
}
|
||||
|
||||
// newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options.
|
||||
// TODO @jhowardmsft LCOW support: Eventually platform can be moved into the builder
|
||||
// options, however, that would be an API change as it shares types.ImageBuildOptions.
|
||||
func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
|
||||
func newBuilder(clientCtx context.Context, options builderOptions, os string) *Builder {
|
||||
config := options.Options
|
||||
if config == nil {
|
||||
config = new(types.ImageBuildOptions)
|
||||
}
|
||||
|
||||
// @jhowardmsft LCOW Support. For the time being, this is interim. Eventually
|
||||
// will be moved to types.ImageBuildOptions, but it can't for now as that would
|
||||
// be an API change.
|
||||
if options.Platform == "" {
|
||||
options.Platform = runtime.GOOS
|
||||
}
|
||||
if options.Platform == "windows" && system.LCOWSupported() {
|
||||
options.Platform = "linux"
|
||||
}
|
||||
|
||||
b := &Builder{
|
||||
clientCtx: clientCtx,
|
||||
options: config,
|
||||
@@ -219,14 +203,11 @@ func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
|
||||
Aux: options.ProgressWriter.AuxFormatter,
|
||||
Output: options.ProgressWriter.Output,
|
||||
docker: options.Backend,
|
||||
archiver: options.Archiver,
|
||||
buildArgs: newBuildArgs(config.BuildArgs),
|
||||
buildStages: newBuildStages(),
|
||||
idMappings: options.IDMappings,
|
||||
imageSources: newImageSources(clientCtx, options),
|
||||
pathCache: options.PathCache,
|
||||
imageProber: newImageProber(options.Backend, config.CacheFrom, options.Platform, config.NoCache),
|
||||
imageProber: newImageProber(options.Backend, config.CacheFrom, os, config.NoCache),
|
||||
containerManager: newContainerManager(options.Backend),
|
||||
platform: options.Platform,
|
||||
}
|
||||
|
||||
return b
|
||||
@@ -239,24 +220,27 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil
|
||||
|
||||
addNodesForLabelOption(dockerfile.AST, b.options.Labels)
|
||||
|
||||
if err := checkDispatchDockerfile(dockerfile.AST); err != nil {
|
||||
buildsFailed.WithValues(metricsDockerfileSyntaxError).Inc()
|
||||
stages, metaArgs, err := instructions.Parse(dockerfile.AST)
|
||||
if err != nil {
|
||||
if instructions.IsUnknownInstruction(err) {
|
||||
buildsFailed.WithValues(metricsUnknownInstructionError).Inc()
|
||||
}
|
||||
return nil, validationError{err}
|
||||
}
|
||||
|
||||
dispatchState, err := b.dispatchDockerfileWithCancellation(dockerfile, source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.options.Target != "" && !dispatchState.isCurrentStage(b.options.Target) {
|
||||
buildsFailed.WithValues(metricsBuildTargetNotReachableError).Inc()
|
||||
return nil, errors.Errorf("failed to reach build target %s in Dockerfile", b.options.Target)
|
||||
if b.options.Target != "" {
|
||||
targetIx, found := instructions.HasStage(stages, b.options.Target)
|
||||
if !found {
|
||||
buildsFailed.WithValues(metricsBuildTargetNotReachableError).Inc()
|
||||
return nil, errors.Errorf("failed to reach build target %s in Dockerfile", b.options.Target)
|
||||
}
|
||||
stages = stages[:targetIx+1]
|
||||
}
|
||||
|
||||
dockerfile.PrintWarnings(b.Stderr)
|
||||
b.buildArgs.WarnOnUnusedBuildArgs(b.Stderr)
|
||||
|
||||
dispatchState, err := b.dispatchDockerfileWithCancellation(stages, metaArgs, dockerfile.EscapeToken, source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dispatchState.imageID == "" {
|
||||
buildsFailed.WithValues(metricsDockerfileEmptyError).Inc()
|
||||
return nil, errors.New("No image was generated. Is your Dockerfile empty?")
|
||||
@@ -271,61 +255,87 @@ func emitImageID(aux *streamformatter.AuxFormatter, state *dispatchState) error
|
||||
return aux.Emit(types.BuildResult{ID: state.imageID})
|
||||
}
|
||||
|
||||
func (b *Builder) dispatchDockerfileWithCancellation(dockerfile *parser.Result, source builder.Source) (*dispatchState, error) {
|
||||
shlex := NewShellLex(dockerfile.EscapeToken)
|
||||
state := newDispatchState()
|
||||
total := len(dockerfile.AST.Children)
|
||||
var err error
|
||||
for i, n := range dockerfile.AST.Children {
|
||||
select {
|
||||
case <-b.clientCtx.Done():
|
||||
logrus.Debug("Builder: build cancelled!")
|
||||
fmt.Fprint(b.Stdout, "Build cancelled")
|
||||
buildsFailed.WithValues(metricsBuildCanceled).Inc()
|
||||
return nil, errors.New("Build cancelled")
|
||||
default:
|
||||
// Not cancelled yet, keep going...
|
||||
}
|
||||
func processMetaArg(meta instructions.ArgCommand, shlex *ShellLex, args *buildArgs) error {
|
||||
// ShellLex currently only support the concatenated string format
|
||||
envs := convertMapToEnvList(args.GetAllAllowed())
|
||||
if err := meta.Expand(func(word string) (string, error) {
|
||||
return shlex.ProcessWord(word, envs)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
args.AddArg(meta.Key, meta.Value)
|
||||
args.AddMetaArg(meta.Key, meta.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// If this is a FROM and we have a previous image then
|
||||
// emit an aux message for that image since it is the
|
||||
// end of the previous stage
|
||||
if n.Value == command.From {
|
||||
if err := emitImageID(b.Aux, state); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
func printCommand(out io.Writer, currentCommandIndex int, totalCommands int, cmd interface{}) int {
|
||||
fmt.Fprintf(out, stepFormat, currentCommandIndex, totalCommands, cmd)
|
||||
fmt.Fprintln(out)
|
||||
return currentCommandIndex + 1
|
||||
}
|
||||
|
||||
if n.Value == command.From && state.isCurrentStage(b.options.Target) {
|
||||
break
|
||||
}
|
||||
func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.Stage, metaArgs []instructions.ArgCommand, escapeToken rune, source builder.Source) (*dispatchState, error) {
|
||||
dispatchRequest := dispatchRequest{}
|
||||
buildArgs := newBuildArgs(b.options.BuildArgs)
|
||||
totalCommands := len(metaArgs) + len(parseResult)
|
||||
currentCommandIndex := 1
|
||||
for _, stage := range parseResult {
|
||||
totalCommands += len(stage.Commands)
|
||||
}
|
||||
shlex := NewShellLex(escapeToken)
|
||||
for _, meta := range metaArgs {
|
||||
currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, &meta)
|
||||
|
||||
opts := dispatchOptions{
|
||||
state: state,
|
||||
stepMsg: formatStep(i, total),
|
||||
node: n,
|
||||
shlex: shlex,
|
||||
source: source,
|
||||
}
|
||||
if state, err = b.dispatch(opts); err != nil {
|
||||
if b.options.ForceRemove {
|
||||
b.containerManager.RemoveAll(b.Stdout)
|
||||
}
|
||||
err := processMetaArg(meta, shlex, buildArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(b.Stdout, " ---> %s\n", stringid.TruncateID(state.imageID))
|
||||
if b.options.Remove {
|
||||
b.containerManager.RemoveAll(b.Stdout)
|
||||
stagesResults := newStagesBuildResults()
|
||||
|
||||
for _, stage := range parseResult {
|
||||
if err := stagesResults.checkStageNameAvailable(stage.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dispatchRequest = newDispatchRequest(b, escapeToken, source, buildArgs, stagesResults)
|
||||
|
||||
currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, stage.SourceCode)
|
||||
if err := initializeStage(dispatchRequest, &stage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dispatchRequest.state.updateRunConfig()
|
||||
fmt.Fprintf(b.Stdout, " ---> %s\n", stringid.TruncateID(dispatchRequest.state.imageID))
|
||||
for _, cmd := range stage.Commands {
|
||||
select {
|
||||
case <-b.clientCtx.Done():
|
||||
logrus.Debug("Builder: build cancelled!")
|
||||
fmt.Fprint(b.Stdout, "Build cancelled\n")
|
||||
buildsFailed.WithValues(metricsBuildCanceled).Inc()
|
||||
return nil, errors.New("Build cancelled")
|
||||
default:
|
||||
// Not cancelled yet, keep going...
|
||||
}
|
||||
|
||||
currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, cmd)
|
||||
|
||||
if err := dispatch(dispatchRequest, cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dispatchRequest.state.updateRunConfig()
|
||||
fmt.Fprintf(b.Stdout, " ---> %s\n", stringid.TruncateID(dispatchRequest.state.imageID))
|
||||
|
||||
}
|
||||
if err := emitImageID(b.Aux, dispatchRequest.state); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buildArgs.MergeReferencedArgs(dispatchRequest.state.buildArgs)
|
||||
if err := commitStage(dispatchRequest.state, stagesResults); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Emit a final aux message for the final image
|
||||
if err := emitImageID(b.Aux, state); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return state, nil
|
||||
buildArgs.WarnOnUnusedBuildArgs(b.Stdout)
|
||||
return dispatchRequest.state, nil
|
||||
}
|
||||
|
||||
func addNodesForLabelOption(dockerfile *parser.Node, labels map[string]string) {
|
||||
@@ -351,25 +361,19 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
|
||||
return config, nil
|
||||
}
|
||||
|
||||
b := newBuilder(context.Background(), builderOptions{
|
||||
Options: &types.ImageBuildOptions{NoCache: true},
|
||||
})
|
||||
|
||||
dockerfile, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
|
||||
if err != nil {
|
||||
return nil, validationError{err}
|
||||
}
|
||||
|
||||
// TODO @jhowardmsft LCOW support. For now, if LCOW enabled, switch to linux.
|
||||
// Also explicitly set the platform. Ultimately this will be in the builder
|
||||
// options, but we can't do that yet as it would change the API.
|
||||
if dockerfile.Platform == "" {
|
||||
dockerfile.Platform = runtime.GOOS
|
||||
os := runtime.GOOS
|
||||
if dockerfile.OS != "" {
|
||||
os = dockerfile.OS
|
||||
}
|
||||
if dockerfile.Platform == "windows" && system.LCOWSupported() {
|
||||
dockerfile.Platform = "linux"
|
||||
}
|
||||
b.platform = dockerfile.Platform
|
||||
|
||||
b := newBuilder(context.Background(), builderOptions{
|
||||
Options: &types.ImageBuildOptions{NoCache: true},
|
||||
}, os)
|
||||
|
||||
// ensure that the commands are valid
|
||||
for _, n := range dockerfile.AST.Children {
|
||||
@@ -382,39 +386,33 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
|
||||
b.Stderr = ioutil.Discard
|
||||
b.disableCommit = true
|
||||
|
||||
if err := checkDispatchDockerfile(dockerfile.AST); err != nil {
|
||||
return nil, validationError{err}
|
||||
commands := []instructions.Command{}
|
||||
for _, n := range dockerfile.AST.Children {
|
||||
cmd, err := instructions.ParseCommand(n)
|
||||
if err != nil {
|
||||
return nil, validationError{err}
|
||||
}
|
||||
commands = append(commands, cmd)
|
||||
}
|
||||
dispatchState := newDispatchState()
|
||||
dispatchState.runConfig = config
|
||||
return dispatchFromDockerfile(b, dockerfile, dispatchState, nil)
|
||||
|
||||
dispatchRequest := newDispatchRequest(b, dockerfile.EscapeToken, nil, newBuildArgs(b.options.BuildArgs), newStagesBuildResults())
|
||||
dispatchRequest.state.runConfig = config
|
||||
dispatchRequest.state.imageID = config.Image
|
||||
for _, cmd := range commands {
|
||||
err := dispatch(dispatchRequest, cmd)
|
||||
if err != nil {
|
||||
return nil, validationError{err}
|
||||
}
|
||||
dispatchRequest.state.updateRunConfig()
|
||||
}
|
||||
|
||||
return dispatchRequest.state.runConfig, nil
|
||||
}
|
||||
|
||||
func checkDispatchDockerfile(dockerfile *parser.Node) error {
|
||||
for _, n := range dockerfile.Children {
|
||||
if err := checkDispatch(n); err != nil {
|
||||
return errors.Wrapf(err, "Dockerfile parse error line %d", n.StartLine)
|
||||
}
|
||||
func convertMapToEnvList(m map[string]string) []string {
|
||||
result := []string{}
|
||||
for k, v := range m {
|
||||
result = append(result, k+"="+v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dispatchFromDockerfile(b *Builder, result *parser.Result, dispatchState *dispatchState, source builder.Source) (*container.Config, error) {
|
||||
shlex := NewShellLex(result.EscapeToken)
|
||||
ast := result.AST
|
||||
total := len(ast.Children)
|
||||
|
||||
for i, n := range ast.Children {
|
||||
opts := dispatchOptions{
|
||||
state: dispatchState,
|
||||
stepMsg: formatStep(i, total),
|
||||
node: n,
|
||||
shlex: shlex,
|
||||
source: source,
|
||||
}
|
||||
if _, err := b.dispatch(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return dispatchState.runConfig, nil
|
||||
return result
|
||||
}
|
||||
|
||||
2
vendor/github.com/docker/docker/builder/dockerfile/builder_unix.go
generated
vendored
2
vendor/github.com/docker/docker/builder/dockerfile/builder_unix.go
generated
vendored
@@ -2,6 +2,6 @@
|
||||
|
||||
package dockerfile
|
||||
|
||||
func defaultShellForPlatform(platform string) []string {
|
||||
func defaultShellForOS(os string) []string {
|
||||
return []string{"/bin/sh", "-c"}
|
||||
}
|
||||
|
||||
4
vendor/github.com/docker/docker/builder/dockerfile/builder_windows.go
generated
vendored
4
vendor/github.com/docker/docker/builder/dockerfile/builder_windows.go
generated
vendored
@@ -1,7 +1,7 @@
|
||||
package dockerfile
|
||||
|
||||
func defaultShellForPlatform(platform string) []string {
|
||||
if platform == "linux" {
|
||||
func defaultShellForOS(os string) []string {
|
||||
if os == "linux" {
|
||||
return []string{"/bin/sh", "-c"}
|
||||
}
|
||||
return []string{"cmd", "/S", "/C"}
|
||||
|
||||
1
vendor/github.com/docker/docker/builder/dockerfile/clientsession.go
generated
vendored
1
vendor/github.com/docker/docker/builder/dockerfile/clientsession.go
generated
vendored
@@ -41,7 +41,6 @@ func (cst *ClientSessionTransport) Copy(ctx context.Context, id fscache.RemoteId
|
||||
type ClientSessionSourceIdentifier struct {
|
||||
includePatterns []string
|
||||
caller session.Caller
|
||||
sharedKey string
|
||||
uuid string
|
||||
}
|
||||
|
||||
|
||||
3
vendor/github.com/docker/docker/builder/dockerfile/containerbackend.go
generated
vendored
3
vendor/github.com/docker/docker/builder/dockerfile/containerbackend.go
generated
vendored
@@ -32,7 +32,6 @@ func (c *containerManager) Create(runConfig *container.Config, hostConfig *conta
|
||||
container, err := c.backend.ContainerCreate(types.ContainerCreateConfig{
|
||||
Config: runConfig,
|
||||
HostConfig: hostConfig,
|
||||
Platform: platform,
|
||||
})
|
||||
if err != nil {
|
||||
return container, err
|
||||
@@ -103,7 +102,7 @@ func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr i
|
||||
|
||||
func logCancellationError(cancelErrCh chan error, msg string) {
|
||||
if cancelErr := <-cancelErrCh; cancelErr != nil {
|
||||
logrus.Debugf("Build cancelled (%v): ", cancelErr, msg)
|
||||
logrus.Debugf("Build cancelled (%v): %s", cancelErr, msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
208
vendor/github.com/docker/docker/builder/dockerfile/copy.go
generated
vendored
208
vendor/github.com/docker/docker/builder/dockerfile/copy.go
generated
vendored
@@ -1,12 +1,15 @@
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -14,16 +17,18 @@ import (
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/remotecontext"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const unnamedFilename = "__unnamed__"
|
||||
|
||||
type pathCache interface {
|
||||
Load(key interface{}) (value interface{}, ok bool)
|
||||
Store(key, value interface{})
|
||||
@@ -32,14 +37,14 @@ type pathCache interface {
|
||||
// copyInfo is a data object which stores the metadata about each source file in
|
||||
// a copyInstruction
|
||||
type copyInfo struct {
|
||||
root string
|
||||
root containerfs.ContainerFS
|
||||
path string
|
||||
hash string
|
||||
noDecompress bool
|
||||
}
|
||||
|
||||
func (c copyInfo) fullPath() (string, error) {
|
||||
return symlink.FollowSymlinkInScope(filepath.Join(c.root, c.path), c.root)
|
||||
return c.root.ResolveScopedPath(c.path, true)
|
||||
}
|
||||
|
||||
func newCopyInfoFromSource(source builder.Source, path string, hash string) copyInfo {
|
||||
@@ -56,6 +61,7 @@ type copyInstruction struct {
|
||||
cmdName string
|
||||
infos []copyInfo
|
||||
dest string
|
||||
chownStr string
|
||||
allowLocalDecompression bool
|
||||
}
|
||||
|
||||
@@ -67,6 +73,7 @@ type copier struct {
|
||||
pathCache pathCache
|
||||
download sourceDownloader
|
||||
tmpPaths []string
|
||||
platform string
|
||||
}
|
||||
|
||||
func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, imageSource *imageMount) copier {
|
||||
@@ -75,6 +82,7 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i
|
||||
pathCache: req.builder.pathCache,
|
||||
download: download,
|
||||
imageSource: imageSource,
|
||||
platform: req.builder.options.Platform,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,14 +90,14 @@ func (o *copier) createCopyInstruction(args []string, cmdName string) (copyInstr
|
||||
inst := copyInstruction{cmdName: cmdName}
|
||||
last := len(args) - 1
|
||||
|
||||
// Work in daemon-specific filepath semantics
|
||||
inst.dest = filepath.FromSlash(args[last])
|
||||
|
||||
infos, err := o.getCopyInfosForSourcePaths(args[0:last])
|
||||
// Work in platform-specific filepath semantics
|
||||
inst.dest = fromSlash(args[last], o.platform)
|
||||
separator := string(separator(o.platform))
|
||||
infos, err := o.getCopyInfosForSourcePaths(args[0:last], inst.dest)
|
||||
if err != nil {
|
||||
return inst, errors.Wrapf(err, "%s failed", cmdName)
|
||||
}
|
||||
if len(infos) > 1 && !strings.HasSuffix(inst.dest, string(os.PathSeparator)) {
|
||||
if len(infos) > 1 && !strings.HasSuffix(inst.dest, separator) {
|
||||
return inst, errors.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName)
|
||||
}
|
||||
inst.infos = infos
|
||||
@@ -98,10 +106,11 @@ func (o *copier) createCopyInstruction(args []string, cmdName string) (copyInstr
|
||||
|
||||
// getCopyInfosForSourcePaths iterates over the source files and calculate the info
|
||||
// needed to copy (e.g. hash value if cached)
|
||||
func (o *copier) getCopyInfosForSourcePaths(sources []string) ([]copyInfo, error) {
|
||||
// The dest is used in case source is URL (and ends with "/")
|
||||
func (o *copier) getCopyInfosForSourcePaths(sources []string, dest string) ([]copyInfo, error) {
|
||||
var infos []copyInfo
|
||||
for _, orig := range sources {
|
||||
subinfos, err := o.getCopyInfoForSourcePath(orig)
|
||||
subinfos, err := o.getCopyInfoForSourcePath(orig, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -114,15 +123,24 @@ func (o *copier) getCopyInfosForSourcePaths(sources []string) ([]copyInfo, error
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func (o *copier) getCopyInfoForSourcePath(orig string) ([]copyInfo, error) {
|
||||
func (o *copier) getCopyInfoForSourcePath(orig, dest string) ([]copyInfo, error) {
|
||||
if !urlutil.IsURL(orig) {
|
||||
return o.calcCopyInfo(orig, true)
|
||||
}
|
||||
|
||||
remote, path, err := o.download(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o.tmpPaths = append(o.tmpPaths, remote.Root())
|
||||
// If path == "" then we are unable to determine filename from src
|
||||
// We have to make sure dest is available
|
||||
if path == "" {
|
||||
if strings.HasSuffix(dest, "/") {
|
||||
return nil, errors.Errorf("cannot determine filename for source %s", orig)
|
||||
}
|
||||
path = unnamedFilename
|
||||
}
|
||||
o.tmpPaths = append(o.tmpPaths, remote.Root().Path())
|
||||
|
||||
hash, err := remote.Hash(path)
|
||||
ci := newCopyInfoFromSource(remote, path, hash)
|
||||
@@ -142,14 +160,6 @@ func (o *copier) Cleanup() {
|
||||
// TODO: allowWildcards can probably be removed by refactoring this function further.
|
||||
func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo, error) {
|
||||
imageSource := o.imageSource
|
||||
if err := validateCopySourcePath(imageSource, origPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Work in daemon-specific OS filepath semantics
|
||||
origPath = filepath.FromSlash(origPath)
|
||||
origPath = strings.TrimPrefix(origPath, string(os.PathSeparator))
|
||||
origPath = strings.TrimPrefix(origPath, "."+string(os.PathSeparator))
|
||||
|
||||
// TODO: do this when creating copier. Requires validateCopySourcePath
|
||||
// (and other below) to be aware of the difference sources. Why is it only
|
||||
@@ -166,8 +176,20 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo,
|
||||
return nil, errors.Errorf("missing build context")
|
||||
}
|
||||
|
||||
root := o.source.Root()
|
||||
|
||||
if err := validateCopySourcePath(imageSource, origPath, root.OS()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Work in source OS specific filepath semantics
|
||||
// For LCOW, this is NOT the daemon OS.
|
||||
origPath = root.FromSlash(origPath)
|
||||
origPath = strings.TrimPrefix(origPath, string(root.Separator()))
|
||||
origPath = strings.TrimPrefix(origPath, "."+string(root.Separator()))
|
||||
|
||||
// Deal with wildcards
|
||||
if allowWildcards && containsWildcards(origPath) {
|
||||
if allowWildcards && containsWildcards(origPath, root.OS()) {
|
||||
return o.copyWithWildcards(origPath)
|
||||
}
|
||||
|
||||
@@ -199,6 +221,19 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo,
|
||||
return newCopyInfos(newCopyInfoFromSource(o.source, origPath, hash)), nil
|
||||
}
|
||||
|
||||
func containsWildcards(name, platform string) bool {
|
||||
isWindows := platform == "windows"
|
||||
for i := 0; i < len(name); i++ {
|
||||
ch := name[i]
|
||||
if ch == '\\' && !isWindows {
|
||||
i++
|
||||
} else if ch == '*' || ch == '?' || ch == '[' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (o *copier) storeInPathCache(im *imageMount, path string, hash string) {
|
||||
if im != nil {
|
||||
o.pathCache.Store(im.ImageID()+path, hash)
|
||||
@@ -206,12 +241,13 @@ func (o *copier) storeInPathCache(im *imageMount, path string, hash string) {
|
||||
}
|
||||
|
||||
func (o *copier) copyWithWildcards(origPath string) ([]copyInfo, error) {
|
||||
root := o.source.Root()
|
||||
var copyInfos []copyInfo
|
||||
if err := filepath.Walk(o.source.Root(), func(path string, info os.FileInfo, err error) error {
|
||||
if err := root.Walk(root.Path(), func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, err := remotecontext.Rel(o.source.Root(), path)
|
||||
rel, err := remotecontext.Rel(root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -219,7 +255,7 @@ func (o *copier) copyWithWildcards(origPath string) ([]copyInfo, error) {
|
||||
if rel == "." {
|
||||
return nil
|
||||
}
|
||||
if match, _ := filepath.Match(origPath, rel); !match {
|
||||
if match, _ := root.Match(origPath, rel); !match {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -261,7 +297,7 @@ func walkSource(source builder.Source, origPath string) ([]string, error) {
|
||||
}
|
||||
// Must be a dir
|
||||
var subfiles []string
|
||||
err = filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
|
||||
err = source.Root().Walk(fp, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -300,22 +336,40 @@ func errOnSourceDownload(_ string) (builder.Source, string, error) {
|
||||
return nil, "", errors.New("source can't be a URL for COPY")
|
||||
}
|
||||
|
||||
func getFilenameForDownload(path string, resp *http.Response) string {
|
||||
// Guess filename based on source
|
||||
if path != "" && !strings.HasSuffix(path, "/") {
|
||||
if filename := filepath.Base(filepath.FromSlash(path)); filename != "" {
|
||||
return filename
|
||||
}
|
||||
}
|
||||
|
||||
// Guess filename based on Content-Disposition
|
||||
if contentDisposition := resp.Header.Get("Content-Disposition"); contentDisposition != "" {
|
||||
if _, params, err := mime.ParseMediaType(contentDisposition); err == nil {
|
||||
if params["filename"] != "" && !strings.HasSuffix(params["filename"], "/") {
|
||||
if filename := filepath.Base(filepath.FromSlash(params["filename"])); filename != "" {
|
||||
return filename
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote builder.Source, p string, err error) {
|
||||
u, err := url.Parse(srcURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filename := filepath.Base(filepath.FromSlash(u.Path)) // Ensure in platform semantics
|
||||
if filename == "" {
|
||||
err = errors.Errorf("cannot determine filename from url: %s", u)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := remotecontext.GetWithStatusError(srcURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filename := getFilenameForDownload(u.Path, resp)
|
||||
|
||||
// Prepare file in a tmp dir
|
||||
tmpDir, err := ioutils.TempDir("", "docker-remote")
|
||||
if err != nil {
|
||||
@@ -326,7 +380,13 @@ func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote b
|
||||
os.RemoveAll(tmpDir)
|
||||
}
|
||||
}()
|
||||
tmpFileName := filepath.Join(tmpDir, filename)
|
||||
// If filename is empty, the returned filename will be "" but
|
||||
// the tmp filename will be created as "__unnamed__"
|
||||
tmpFileName := filename
|
||||
if filename == "" {
|
||||
tmpFileName = unnamedFilename
|
||||
}
|
||||
tmpFileName = filepath.Join(tmpDir, tmpFileName)
|
||||
tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -362,13 +422,19 @@ func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote b
|
||||
return
|
||||
}
|
||||
|
||||
lc, err := remotecontext.NewLazySource(tmpDir)
|
||||
lc, err := remotecontext.NewLazySource(containerfs.NewLocalContainerFS(tmpDir))
|
||||
return lc, filename, err
|
||||
}
|
||||
|
||||
type copyFileOptions struct {
|
||||
decompress bool
|
||||
archiver *archive.Archiver
|
||||
chownPair idtools.IDPair
|
||||
archiver Archiver
|
||||
}
|
||||
|
||||
type copyEndpoint struct {
|
||||
driver containerfs.Driver
|
||||
path string
|
||||
}
|
||||
|
||||
func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions) error {
|
||||
@@ -376,6 +442,7 @@ func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destPath, err := dest.fullPath()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -383,57 +450,90 @@ func performCopyForInfo(dest copyInfo, source copyInfo, options copyFileOptions)
|
||||
|
||||
archiver := options.archiver
|
||||
|
||||
src, err := os.Stat(srcPath)
|
||||
srcEndpoint := ©Endpoint{driver: source.root, path: srcPath}
|
||||
destEndpoint := ©Endpoint{driver: dest.root, path: destPath}
|
||||
|
||||
src, err := source.root.Stat(srcPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "source path not found")
|
||||
}
|
||||
if src.IsDir() {
|
||||
return copyDirectory(archiver, srcPath, destPath)
|
||||
return copyDirectory(archiver, srcEndpoint, destEndpoint, options.chownPair)
|
||||
}
|
||||
if options.decompress && archive.IsArchivePath(srcPath) && !source.noDecompress {
|
||||
if options.decompress && isArchivePath(source.root, srcPath) && !source.noDecompress {
|
||||
return archiver.UntarPath(srcPath, destPath)
|
||||
}
|
||||
|
||||
destExistsAsDir, err := isExistingDirectory(destPath)
|
||||
destExistsAsDir, err := isExistingDirectory(destEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// dest.path must be used because destPath has already been cleaned of any
|
||||
// trailing slash
|
||||
if endsInSlash(dest.path) || destExistsAsDir {
|
||||
if endsInSlash(dest.root, dest.path) || destExistsAsDir {
|
||||
// source.path must be used to get the correct filename when the source
|
||||
// is a symlink
|
||||
destPath = filepath.Join(destPath, filepath.Base(source.path))
|
||||
destPath = dest.root.Join(destPath, source.root.Base(source.path))
|
||||
destEndpoint = ©Endpoint{driver: dest.root, path: destPath}
|
||||
}
|
||||
return copyFile(archiver, srcPath, destPath)
|
||||
return copyFile(archiver, srcEndpoint, destEndpoint, options.chownPair)
|
||||
}
|
||||
|
||||
func copyDirectory(archiver *archive.Archiver, source, dest string) error {
|
||||
if err := archiver.CopyWithTar(source, dest); err != nil {
|
||||
func isArchivePath(driver containerfs.ContainerFS, path string) bool {
|
||||
file, err := driver.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
rdr, err := archive.DecompressStream(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
r := tar.NewReader(rdr)
|
||||
_, err = r.Next()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func copyDirectory(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.IDPair) error {
|
||||
destExists, err := isExistingDirectory(dest)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to query destination path")
|
||||
}
|
||||
|
||||
if err := archiver.CopyWithTar(source.path, dest.path); err != nil {
|
||||
return errors.Wrapf(err, "failed to copy directory")
|
||||
}
|
||||
return fixPermissions(source, dest, archiver.IDMappings.RootPair())
|
||||
// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
|
||||
return fixPermissions(source.path, dest.path, chownPair, !destExists)
|
||||
}
|
||||
|
||||
func copyFile(archiver *archive.Archiver, source, dest string) error {
|
||||
rootIDs := archiver.IDMappings.RootPair()
|
||||
|
||||
if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest), 0755, rootIDs); err != nil {
|
||||
return errors.Wrapf(err, "failed to create new directory")
|
||||
func copyFile(archiver Archiver, source, dest *copyEndpoint, chownPair idtools.IDPair) error {
|
||||
if runtime.GOOS == "windows" && dest.driver.OS() == "linux" {
|
||||
// LCOW
|
||||
if err := dest.driver.MkdirAll(dest.driver.Dir(dest.path), 0755); err != nil {
|
||||
return errors.Wrapf(err, "failed to create new directory")
|
||||
}
|
||||
} else {
|
||||
if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, chownPair); err != nil {
|
||||
// Normal containers
|
||||
return errors.Wrapf(err, "failed to create new directory")
|
||||
}
|
||||
}
|
||||
if err := archiver.CopyFileWithTar(source, dest); err != nil {
|
||||
|
||||
if err := archiver.CopyFileWithTar(source.path, dest.path); err != nil {
|
||||
return errors.Wrapf(err, "failed to copy file")
|
||||
}
|
||||
return fixPermissions(source, dest, rootIDs)
|
||||
// TODO: @gupta-ak. Investigate how LCOW permission mappings will work.
|
||||
return fixPermissions(source.path, dest.path, chownPair, false)
|
||||
}
|
||||
|
||||
func endsInSlash(path string) bool {
|
||||
return strings.HasSuffix(path, string(os.PathSeparator))
|
||||
func endsInSlash(driver containerfs.Driver, path string) bool {
|
||||
return strings.HasSuffix(path, string(driver.Separator()))
|
||||
}
|
||||
|
||||
// isExistingDirectory returns true if the path exists and is a directory
|
||||
func isExistingDirectory(path string) (bool, error) {
|
||||
destStat, err := os.Stat(path)
|
||||
func isExistingDirectory(point *copyEndpoint) (bool, error) {
|
||||
destStat, err := point.driver.Stat(point.path)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
return false, nil
|
||||
|
||||
114
vendor/github.com/docker/docker/builder/dockerfile/copy_test.go
generated
vendored
114
vendor/github.com/docker/docker/builder/dockerfile/copy_test.go
generated
vendored
@@ -1,16 +1,18 @@
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/testutil/tempfile"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/gotestyourself/gotestyourself/fs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsExistingDirectory(t *testing.T) {
|
||||
tmpfile := tempfile.NewTempFile(t, "file-exists-test", "something")
|
||||
tmpfile := fs.NewFile(t, "file-exists-test", fs.WithContent("something"))
|
||||
defer tmpfile.Remove()
|
||||
tmpdir := tempfile.NewTempDir(t, "dir-exists-test")
|
||||
tmpdir := fs.NewDir(t, "dir-exists-test")
|
||||
defer tmpdir.Remove()
|
||||
|
||||
var testcases = []struct {
|
||||
@@ -20,7 +22,7 @@ func TestIsExistingDirectory(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
doc: "directory exists",
|
||||
path: tmpdir.Path,
|
||||
path: tmpdir.Path(),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
@@ -30,16 +32,116 @@ func TestIsExistingDirectory(t *testing.T) {
|
||||
},
|
||||
{
|
||||
doc: "file exists",
|
||||
path: tmpfile.Name(),
|
||||
path: tmpfile.Path(),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
result, err := isExistingDirectory(testcase.path)
|
||||
result, err := isExistingDirectory(©Endpoint{driver: containerfs.NewLocalDriver(), path: testcase.path})
|
||||
if !assert.NoError(t, err) {
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, testcase.expected, result, testcase.doc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFilenameForDownload(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
path string
|
||||
disposition string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
path: "http://www.example.com/",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
path: "http://www.example.com/xyz",
|
||||
expected: "xyz",
|
||||
},
|
||||
{
|
||||
path: "http://www.example.com/xyz.html",
|
||||
expected: "xyz.html",
|
||||
},
|
||||
{
|
||||
path: "http://www.example.com/xyz/",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
path: "http://www.example.com/xyz/uvw",
|
||||
expected: "uvw",
|
||||
},
|
||||
{
|
||||
path: "http://www.example.com/xyz/uvw.html",
|
||||
expected: "uvw.html",
|
||||
},
|
||||
{
|
||||
path: "http://www.example.com/xyz/uvw/",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
path: "/xyz",
|
||||
expected: "xyz",
|
||||
},
|
||||
{
|
||||
path: "/xyz.html",
|
||||
expected: "xyz.html",
|
||||
},
|
||||
{
|
||||
path: "/xyz/",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
path: "/xyz/",
|
||||
disposition: "attachment; filename=xyz.html",
|
||||
expected: "xyz.html",
|
||||
},
|
||||
{
|
||||
disposition: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
disposition: "attachment; filename=xyz",
|
||||
expected: "xyz",
|
||||
},
|
||||
{
|
||||
disposition: "attachment; filename=xyz.html",
|
||||
expected: "xyz.html",
|
||||
},
|
||||
{
|
||||
disposition: "attachment; filename=\"xyz\"",
|
||||
expected: "xyz",
|
||||
},
|
||||
{
|
||||
disposition: "attachment; filename=\"xyz.html\"",
|
||||
expected: "xyz.html",
|
||||
},
|
||||
{
|
||||
disposition: "attachment; filename=\"/xyz.html\"",
|
||||
expected: "xyz.html",
|
||||
},
|
||||
{
|
||||
disposition: "attachment; filename=\"/xyz/uvw\"",
|
||||
expected: "uvw",
|
||||
},
|
||||
{
|
||||
disposition: "attachment; filename=\"Naïve file.txt\"",
|
||||
expected: "Naïve file.txt",
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
resp := http.Response{
|
||||
Header: make(map[string][]string),
|
||||
}
|
||||
if testcase.disposition != "" {
|
||||
resp.Header.Add("Content-Disposition", testcase.disposition)
|
||||
}
|
||||
filename := getFilenameForDownload(testcase.path, &resp)
|
||||
assert.Equal(t, testcase.expected, filename)
|
||||
}
|
||||
}
|
||||
|
||||
20
vendor/github.com/docker/docker/builder/dockerfile/copy_unix.go
generated
vendored
20
vendor/github.com/docker/docker/builder/dockerfile/copy_unix.go
generated
vendored
@@ -6,13 +6,21 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
)
|
||||
|
||||
func fixPermissions(source, destination string, rootIDs idtools.IDPair) error {
|
||||
skipChownRoot, err := isExistingDirectory(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
func fixPermissions(source, destination string, rootIDs idtools.IDPair, overrideSkip bool) error {
|
||||
var (
|
||||
skipChownRoot bool
|
||||
err error
|
||||
)
|
||||
if !overrideSkip {
|
||||
destEndpoint := ©Endpoint{driver: containerfs.NewLocalDriver(), path: destination}
|
||||
skipChownRoot, err = isExistingDirectory(destEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// We Walk on the source rather than on the destination because we don't
|
||||
@@ -34,3 +42,7 @@ func fixPermissions(source, destination string, rootIDs idtools.IDPair) error {
|
||||
return os.Lchown(fullpath, rootIDs.UID, rootIDs.GID)
|
||||
})
|
||||
}
|
||||
|
||||
func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
39
vendor/github.com/docker/docker/builder/dockerfile/copy_windows.go
generated
vendored
39
vendor/github.com/docker/docker/builder/dockerfile/copy_windows.go
generated
vendored
@@ -1,8 +1,43 @@
|
||||
package dockerfile
|
||||
|
||||
import "github.com/docker/docker/pkg/idtools"
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
func fixPermissions(source, destination string, rootIDs idtools.IDPair) error {
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
)
|
||||
|
||||
var pathBlacklist = map[string]bool{
|
||||
"c:\\": true,
|
||||
"c:\\windows": true,
|
||||
}
|
||||
|
||||
func fixPermissions(source, destination string, rootIDs idtools.IDPair, overrideSkip bool) error {
|
||||
// chown is not supported on Windows
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error {
|
||||
// validate windows paths from other images + LCOW
|
||||
if imageSource == nil || platform != "windows" {
|
||||
return nil
|
||||
}
|
||||
|
||||
origPath = filepath.FromSlash(origPath)
|
||||
p := strings.ToLower(filepath.Clean(origPath))
|
||||
if !filepath.IsAbs(p) {
|
||||
if filepath.VolumeName(p) != "" {
|
||||
if p[len(p)-2:] == ":." { // case where clean returns weird c:. paths
|
||||
p = p[:len(p)-1]
|
||||
}
|
||||
p += "\\"
|
||||
} else {
|
||||
p = filepath.Join("c:\\", p)
|
||||
}
|
||||
}
|
||||
if _, blacklisted := pathBlacklist[p]; blacklisted {
|
||||
return errors.New("copy from c:\\ or c:\\windows is not allowed on windows")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
775
vendor/github.com/docker/docker/builder/dockerfile/dispatchers.go
generated
vendored
775
vendor/github.com/docker/docker/builder/dockerfile/dispatchers.go
generated
vendored
File diff suppressed because it is too large
Load Diff
445
vendor/github.com/docker/docker/builder/dockerfile/dispatchers_test.go
generated
vendored
445
vendor/github.com/docker/docker/builder/dockerfile/dispatchers_test.go
generated
vendored
@@ -1,198 +1,114 @@
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
"context"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/docker/docker/builder/dockerfile/instructions"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type commandWithFunction struct {
|
||||
name string
|
||||
function func(args []string) error
|
||||
}
|
||||
|
||||
func withArgs(f dispatcher) func([]string) error {
|
||||
return func(args []string) error {
|
||||
return f(dispatchRequest{args: args})
|
||||
}
|
||||
}
|
||||
|
||||
func withBuilderAndArgs(builder *Builder, f dispatcher) func([]string) error {
|
||||
return func(args []string) error {
|
||||
return f(defaultDispatchReq(builder, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func defaultDispatchReq(builder *Builder, args ...string) dispatchRequest {
|
||||
return dispatchRequest{
|
||||
builder: builder,
|
||||
args: args,
|
||||
flags: NewBFlags(),
|
||||
shlex: NewShellLex(parser.DefaultEscapeToken),
|
||||
state: &dispatchState{runConfig: &container.Config{}},
|
||||
}
|
||||
}
|
||||
|
||||
func newBuilderWithMockBackend() *Builder {
|
||||
mockBackend := &MockBackend{}
|
||||
ctx := context.Background()
|
||||
b := &Builder{
|
||||
options: &types.ImageBuildOptions{},
|
||||
options: &types.ImageBuildOptions{Platform: runtime.GOOS},
|
||||
docker: mockBackend,
|
||||
buildArgs: newBuildArgs(make(map[string]*string)),
|
||||
Stdout: new(bytes.Buffer),
|
||||
clientCtx: ctx,
|
||||
disableCommit: true,
|
||||
imageSources: newImageSources(ctx, builderOptions{
|
||||
Options: &types.ImageBuildOptions{},
|
||||
Options: &types.ImageBuildOptions{Platform: runtime.GOOS},
|
||||
Backend: mockBackend,
|
||||
}),
|
||||
buildStages: newBuildStages(),
|
||||
imageProber: newImageProber(mockBackend, nil, runtime.GOOS, false),
|
||||
containerManager: newContainerManager(mockBackend),
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func TestCommandsExactlyOneArgument(t *testing.T) {
|
||||
commands := []commandWithFunction{
|
||||
{"MAINTAINER", withArgs(maintainer)},
|
||||
{"WORKDIR", withArgs(workdir)},
|
||||
{"USER", withArgs(user)},
|
||||
{"STOPSIGNAL", withArgs(stopSignal)},
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
err := command.function([]string{})
|
||||
assert.EqualError(t, err, errExactlyOneArgument(command.name).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsAtLeastOneArgument(t *testing.T) {
|
||||
commands := []commandWithFunction{
|
||||
{"ENV", withArgs(env)},
|
||||
{"LABEL", withArgs(label)},
|
||||
{"ONBUILD", withArgs(onbuild)},
|
||||
{"HEALTHCHECK", withArgs(healthcheck)},
|
||||
{"EXPOSE", withArgs(expose)},
|
||||
{"VOLUME", withArgs(volume)},
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
err := command.function([]string{})
|
||||
assert.EqualError(t, err, errAtLeastOneArgument(command.name).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsAtLeastTwoArguments(t *testing.T) {
|
||||
commands := []commandWithFunction{
|
||||
{"ADD", withArgs(add)},
|
||||
{"COPY", withArgs(dispatchCopy)}}
|
||||
|
||||
for _, command := range commands {
|
||||
err := command.function([]string{"arg1"})
|
||||
assert.EqualError(t, err, errAtLeastTwoArguments(command.name).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsTooManyArguments(t *testing.T) {
|
||||
commands := []commandWithFunction{
|
||||
{"ENV", withArgs(env)},
|
||||
{"LABEL", withArgs(label)}}
|
||||
|
||||
for _, command := range commands {
|
||||
err := command.function([]string{"arg1", "arg2", "arg3"})
|
||||
assert.EqualError(t, err, errTooManyArguments(command.name).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsBlankNames(t *testing.T) {
|
||||
builder := newBuilderWithMockBackend()
|
||||
commands := []commandWithFunction{
|
||||
{"ENV", withBuilderAndArgs(builder, env)},
|
||||
{"LABEL", withBuilderAndArgs(builder, label)},
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
err := command.function([]string{"", ""})
|
||||
assert.EqualError(t, err, errBlankCommandNames(command.name).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnv2Variables(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
|
||||
args := []string{"var1", "val1", "var2", "val2"}
|
||||
req := defaultDispatchReq(b, args...)
|
||||
err := env(req)
|
||||
sb := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
envCommand := &instructions.EnvCommand{
|
||||
Env: instructions.KeyValuePairs{
|
||||
instructions.KeyValuePair{Key: "var1", Value: "val1"},
|
||||
instructions.KeyValuePair{Key: "var2", Value: "val2"},
|
||||
},
|
||||
}
|
||||
err := dispatch(sb, envCommand)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := []string{
|
||||
fmt.Sprintf("%s=%s", args[0], args[1]),
|
||||
fmt.Sprintf("%s=%s", args[2], args[3]),
|
||||
"var1=val1",
|
||||
"var2=val2",
|
||||
}
|
||||
assert.Equal(t, expected, req.state.runConfig.Env)
|
||||
assert.Equal(t, expected, sb.state.runConfig.Env)
|
||||
}
|
||||
|
||||
func TestEnvValueWithExistingRunConfigEnv(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
|
||||
args := []string{"var1", "val1"}
|
||||
req := defaultDispatchReq(b, args...)
|
||||
req.state.runConfig.Env = []string{"var1=old", "var2=fromenv"}
|
||||
err := env(req)
|
||||
sb := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
sb.state.runConfig.Env = []string{"var1=old", "var2=fromenv"}
|
||||
envCommand := &instructions.EnvCommand{
|
||||
Env: instructions.KeyValuePairs{
|
||||
instructions.KeyValuePair{Key: "var1", Value: "val1"},
|
||||
},
|
||||
}
|
||||
err := dispatch(sb, envCommand)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := []string{
|
||||
fmt.Sprintf("%s=%s", args[0], args[1]),
|
||||
"var1=val1",
|
||||
"var2=fromenv",
|
||||
}
|
||||
assert.Equal(t, expected, req.state.runConfig.Env)
|
||||
assert.Equal(t, expected, sb.state.runConfig.Env)
|
||||
}
|
||||
|
||||
func TestMaintainer(t *testing.T) {
|
||||
maintainerEntry := "Some Maintainer <maintainer@example.com>"
|
||||
|
||||
b := newBuilderWithMockBackend()
|
||||
req := defaultDispatchReq(b, maintainerEntry)
|
||||
err := maintainer(req)
|
||||
sb := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
cmd := &instructions.MaintainerCommand{Maintainer: maintainerEntry}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, maintainerEntry, req.state.maintainer)
|
||||
assert.Equal(t, maintainerEntry, sb.state.maintainer)
|
||||
}
|
||||
|
||||
func TestLabel(t *testing.T) {
|
||||
labelName := "label"
|
||||
labelValue := "value"
|
||||
|
||||
labelEntry := []string{labelName, labelValue}
|
||||
b := newBuilderWithMockBackend()
|
||||
req := defaultDispatchReq(b, labelEntry...)
|
||||
err := label(req)
|
||||
sb := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
cmd := &instructions.LabelCommand{
|
||||
Labels: instructions.KeyValuePairs{
|
||||
instructions.KeyValuePair{Key: labelName, Value: labelValue},
|
||||
},
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Contains(t, req.state.runConfig.Labels, labelName)
|
||||
assert.Equal(t, req.state.runConfig.Labels[labelName], labelValue)
|
||||
require.Contains(t, sb.state.runConfig.Labels, labelName)
|
||||
assert.Equal(t, sb.state.runConfig.Labels[labelName], labelValue)
|
||||
}
|
||||
|
||||
func TestFromScratch(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
req := defaultDispatchReq(b, "scratch")
|
||||
err := from(req)
|
||||
sb := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
cmd := &instructions.Stage{
|
||||
BaseName: "scratch",
|
||||
}
|
||||
err := initializeStage(sb, cmd)
|
||||
|
||||
if runtime.GOOS == "windows" && !system.LCOWSupported() {
|
||||
assert.EqualError(t, err, "Windows does not support FROM scratch")
|
||||
@@ -200,14 +116,10 @@ func TestFromScratch(t *testing.T) {
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, req.state.hasFromImage())
|
||||
assert.Equal(t, "", req.state.imageID)
|
||||
// Windows does not set the default path. TODO @jhowardmsft LCOW support. This will need revisiting as we get further into the implementation
|
||||
assert.True(t, sb.state.hasFromImage())
|
||||
assert.Equal(t, "", sb.state.imageID)
|
||||
expected := "PATH=" + system.DefaultPathEnv(runtime.GOOS)
|
||||
if runtime.GOOS == "windows" {
|
||||
expected = ""
|
||||
}
|
||||
assert.Equal(t, []string{expected}, req.state.runConfig.Env)
|
||||
assert.Equal(t, []string{expected}, sb.state.runConfig.Env)
|
||||
}
|
||||
|
||||
func TestFromWithArg(t *testing.T) {
|
||||
@@ -219,16 +131,27 @@ func TestFromWithArg(t *testing.T) {
|
||||
}
|
||||
b := newBuilderWithMockBackend()
|
||||
b.docker.(*MockBackend).getImageFunc = getImage
|
||||
args := newBuildArgs(make(map[string]*string))
|
||||
|
||||
require.NoError(t, arg(defaultDispatchReq(b, "THETAG="+tag)))
|
||||
req := defaultDispatchReq(b, "alpine${THETAG}")
|
||||
err := from(req)
|
||||
val := "sometag"
|
||||
metaArg := instructions.ArgCommand{
|
||||
Key: "THETAG",
|
||||
Value: &val,
|
||||
}
|
||||
cmd := &instructions.Stage{
|
||||
BaseName: "alpine:${THETAG}",
|
||||
}
|
||||
err := processMetaArg(metaArg, NewShellLex('\\'), args)
|
||||
|
||||
sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, req.state.imageID)
|
||||
assert.Equal(t, expected, req.state.baseImage.ImageID())
|
||||
assert.Len(t, b.buildArgs.GetAllAllowed(), 0)
|
||||
assert.Len(t, b.buildArgs.GetAllMeta(), 1)
|
||||
err = initializeStage(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, sb.state.imageID)
|
||||
assert.Equal(t, expected, sb.state.baseImage.ImageID())
|
||||
assert.Len(t, sb.state.buildArgs.GetAllAllowed(), 0)
|
||||
assert.Len(t, sb.state.buildArgs.GetAllMeta(), 1)
|
||||
}
|
||||
|
||||
func TestFromWithUndefinedArg(t *testing.T) {
|
||||
@@ -240,74 +163,74 @@ func TestFromWithUndefinedArg(t *testing.T) {
|
||||
}
|
||||
b := newBuilderWithMockBackend()
|
||||
b.docker.(*MockBackend).getImageFunc = getImage
|
||||
sb := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
|
||||
b.options.BuildArgs = map[string]*string{"THETAG": &tag}
|
||||
|
||||
req := defaultDispatchReq(b, "alpine${THETAG}")
|
||||
err := from(req)
|
||||
cmd := &instructions.Stage{
|
||||
BaseName: "alpine${THETAG}",
|
||||
}
|
||||
err := initializeStage(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, req.state.imageID)
|
||||
assert.Equal(t, expected, sb.state.imageID)
|
||||
}
|
||||
|
||||
func TestFromMultiStageWithScratchNamedStage(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Windows does not support scratch")
|
||||
}
|
||||
func TestFromMultiStageWithNamedStage(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
req := defaultDispatchReq(b, "scratch", "AS", "base")
|
||||
|
||||
require.NoError(t, from(req))
|
||||
assert.True(t, req.state.hasFromImage())
|
||||
|
||||
req.args = []string{"base"}
|
||||
require.NoError(t, from(req))
|
||||
assert.True(t, req.state.hasFromImage())
|
||||
}
|
||||
|
||||
func TestOnbuildIllegalTriggers(t *testing.T) {
|
||||
triggers := []struct{ command, expectedError string }{
|
||||
{"ONBUILD", "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed"},
|
||||
{"MAINTAINER", "MAINTAINER isn't allowed as an ONBUILD trigger"},
|
||||
{"FROM", "FROM isn't allowed as an ONBUILD trigger"}}
|
||||
|
||||
for _, trigger := range triggers {
|
||||
b := newBuilderWithMockBackend()
|
||||
|
||||
err := onbuild(defaultDispatchReq(b, trigger.command))
|
||||
testutil.ErrorContains(t, err, trigger.expectedError)
|
||||
}
|
||||
firstFrom := &instructions.Stage{BaseName: "someimg", Name: "base"}
|
||||
secondFrom := &instructions.Stage{BaseName: "base"}
|
||||
previousResults := newStagesBuildResults()
|
||||
firstSB := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), previousResults)
|
||||
secondSB := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), previousResults)
|
||||
err := initializeStage(firstSB, firstFrom)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, firstSB.state.hasFromImage())
|
||||
previousResults.indexed["base"] = firstSB.state.runConfig
|
||||
previousResults.flat = append(previousResults.flat, firstSB.state.runConfig)
|
||||
err = initializeStage(secondSB, secondFrom)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, secondSB.state.hasFromImage())
|
||||
}
|
||||
|
||||
func TestOnbuild(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
|
||||
req := defaultDispatchReq(b, "ADD", ".", "/app/src")
|
||||
req.original = "ONBUILD ADD . /app/src"
|
||||
req.state.runConfig = &container.Config{}
|
||||
|
||||
err := onbuild(req)
|
||||
sb := newDispatchRequest(b, '\\', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
cmd := &instructions.OnbuildCommand{
|
||||
Expression: "ADD . /app/src",
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "ADD . /app/src", req.state.runConfig.OnBuild[0])
|
||||
assert.Equal(t, "ADD . /app/src", sb.state.runConfig.OnBuild[0])
|
||||
}
|
||||
|
||||
func TestWorkdir(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
workingDir := "/app"
|
||||
if runtime.GOOS == "windows" {
|
||||
workingDir = "C:\app"
|
||||
workingDir = "C:\\app"
|
||||
}
|
||||
cmd := &instructions.WorkdirCommand{
|
||||
Path: workingDir,
|
||||
}
|
||||
|
||||
req := defaultDispatchReq(b, workingDir)
|
||||
err := workdir(req)
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, workingDir, req.state.runConfig.WorkingDir)
|
||||
assert.Equal(t, workingDir, sb.state.runConfig.WorkingDir)
|
||||
}
|
||||
|
||||
func TestCmd(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
command := "./executable"
|
||||
|
||||
req := defaultDispatchReq(b, command)
|
||||
err := cmd(req)
|
||||
cmd := &instructions.CmdCommand{
|
||||
ShellDependantCmdLine: instructions.ShellDependantCmdLine{
|
||||
CmdLine: strslice.StrSlice{command},
|
||||
PrependShell: true,
|
||||
},
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
var expectedCommand strslice.StrSlice
|
||||
@@ -317,42 +240,56 @@ func TestCmd(t *testing.T) {
|
||||
expectedCommand = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", command))
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedCommand, req.state.runConfig.Cmd)
|
||||
assert.True(t, req.state.cmdSet)
|
||||
assert.Equal(t, expectedCommand, sb.state.runConfig.Cmd)
|
||||
assert.True(t, sb.state.cmdSet)
|
||||
}
|
||||
|
||||
func TestHealthcheckNone(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
|
||||
req := defaultDispatchReq(b, "NONE")
|
||||
err := healthcheck(req)
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
cmd := &instructions.HealthCheckCommand{
|
||||
Health: &container.HealthConfig{
|
||||
Test: []string{"NONE"},
|
||||
},
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, req.state.runConfig.Healthcheck)
|
||||
assert.Equal(t, []string{"NONE"}, req.state.runConfig.Healthcheck.Test)
|
||||
require.NotNil(t, sb.state.runConfig.Healthcheck)
|
||||
assert.Equal(t, []string{"NONE"}, sb.state.runConfig.Healthcheck.Test)
|
||||
}
|
||||
|
||||
func TestHealthcheckCmd(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
|
||||
args := []string{"CMD", "curl", "-f", "http://localhost/", "||", "exit", "1"}
|
||||
req := defaultDispatchReq(b, args...)
|
||||
err := healthcheck(req)
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"}
|
||||
cmd := &instructions.HealthCheckCommand{
|
||||
Health: &container.HealthConfig{
|
||||
Test: expectedTest,
|
||||
},
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, req.state.runConfig.Healthcheck)
|
||||
expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"}
|
||||
assert.Equal(t, expectedTest, req.state.runConfig.Healthcheck.Test)
|
||||
require.NotNil(t, sb.state.runConfig.Healthcheck)
|
||||
assert.Equal(t, expectedTest, sb.state.runConfig.Healthcheck.Test)
|
||||
}
|
||||
|
||||
func TestEntrypoint(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
entrypointCmd := "/usr/sbin/nginx"
|
||||
|
||||
req := defaultDispatchReq(b, entrypointCmd)
|
||||
err := entrypoint(req)
|
||||
cmd := &instructions.EntrypointCommand{
|
||||
ShellDependantCmdLine: instructions.ShellDependantCmdLine{
|
||||
CmdLine: strslice.StrSlice{entrypointCmd},
|
||||
PrependShell: true,
|
||||
},
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, req.state.runConfig.Entrypoint)
|
||||
require.NotNil(t, sb.state.runConfig.Entrypoint)
|
||||
|
||||
var expectedEntrypoint strslice.StrSlice
|
||||
if runtime.GOOS == "windows" {
|
||||
@@ -360,99 +297,99 @@ func TestEntrypoint(t *testing.T) {
|
||||
} else {
|
||||
expectedEntrypoint = strslice.StrSlice(append([]string{"/bin/sh"}, "-c", entrypointCmd))
|
||||
}
|
||||
assert.Equal(t, expectedEntrypoint, req.state.runConfig.Entrypoint)
|
||||
assert.Equal(t, expectedEntrypoint, sb.state.runConfig.Entrypoint)
|
||||
}
|
||||
|
||||
func TestExpose(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
|
||||
exposedPort := "80"
|
||||
req := defaultDispatchReq(b, exposedPort)
|
||||
err := expose(req)
|
||||
cmd := &instructions.ExposeCommand{
|
||||
Ports: []string{exposedPort},
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, req.state.runConfig.ExposedPorts)
|
||||
require.Len(t, req.state.runConfig.ExposedPorts, 1)
|
||||
require.NotNil(t, sb.state.runConfig.ExposedPorts)
|
||||
require.Len(t, sb.state.runConfig.ExposedPorts, 1)
|
||||
|
||||
portsMapping, err := nat.ParsePortSpec(exposedPort)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, req.state.runConfig.ExposedPorts, portsMapping[0].Port)
|
||||
assert.Contains(t, sb.state.runConfig.ExposedPorts, portsMapping[0].Port)
|
||||
}
|
||||
|
||||
func TestUser(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
userCommand := "foo"
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
|
||||
req := defaultDispatchReq(b, userCommand)
|
||||
err := user(req)
|
||||
cmd := &instructions.UserCommand{
|
||||
User: "test",
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, userCommand, req.state.runConfig.User)
|
||||
assert.Equal(t, "test", sb.state.runConfig.User)
|
||||
}
|
||||
|
||||
func TestVolume(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
|
||||
exposedVolume := "/foo"
|
||||
|
||||
req := defaultDispatchReq(b, exposedVolume)
|
||||
err := volume(req)
|
||||
cmd := &instructions.VolumeCommand{
|
||||
Volumes: []string{exposedVolume},
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, req.state.runConfig.Volumes)
|
||||
assert.Len(t, req.state.runConfig.Volumes, 1)
|
||||
assert.Contains(t, req.state.runConfig.Volumes, exposedVolume)
|
||||
require.NotNil(t, sb.state.runConfig.Volumes)
|
||||
assert.Len(t, sb.state.runConfig.Volumes, 1)
|
||||
assert.Contains(t, sb.state.runConfig.Volumes, exposedVolume)
|
||||
}
|
||||
|
||||
func TestStopSignal(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Windows does not support stopsignal")
|
||||
return
|
||||
}
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
signal := "SIGKILL"
|
||||
|
||||
req := defaultDispatchReq(b, signal)
|
||||
err := stopSignal(req)
|
||||
cmd := &instructions.StopSignalCommand{
|
||||
Signal: signal,
|
||||
}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, signal, req.state.runConfig.StopSignal)
|
||||
assert.Equal(t, signal, sb.state.runConfig.StopSignal)
|
||||
}
|
||||
|
||||
func TestArg(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
|
||||
argName := "foo"
|
||||
argVal := "bar"
|
||||
argDef := fmt.Sprintf("%s=%s", argName, argVal)
|
||||
|
||||
err := arg(defaultDispatchReq(b, argDef))
|
||||
cmd := &instructions.ArgCommand{Key: argName, Value: &argVal}
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := map[string]string{argName: argVal}
|
||||
assert.Equal(t, expected, b.buildArgs.GetAllAllowed())
|
||||
assert.Equal(t, expected, sb.state.buildArgs.GetAllAllowed())
|
||||
}
|
||||
|
||||
func TestShell(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
|
||||
shellCmd := "powershell"
|
||||
req := defaultDispatchReq(b, shellCmd)
|
||||
req.attributes = map[string]bool{"json": true}
|
||||
cmd := &instructions.ShellCommand{Shell: strslice.StrSlice{shellCmd}}
|
||||
|
||||
err := shell(req)
|
||||
err := dispatch(sb, cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedShell := strslice.StrSlice([]string{shellCmd})
|
||||
assert.Equal(t, expectedShell, req.state.runConfig.Shell)
|
||||
}
|
||||
|
||||
func TestParseOptInterval(t *testing.T) {
|
||||
flInterval := &Flag{
|
||||
name: "interval",
|
||||
flagType: stringType,
|
||||
Value: "50ns",
|
||||
}
|
||||
_, err := parseOptInterval(flInterval)
|
||||
testutil.ErrorContains(t, err, "cannot be less than 1ms")
|
||||
|
||||
flInterval.Value = "1ms"
|
||||
_, err = parseOptInterval(flInterval)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedShell, sb.state.runConfig.Shell)
|
||||
}
|
||||
|
||||
func TestPrependEnvOnCmd(t *testing.T) {
|
||||
@@ -469,8 +406,10 @@ func TestPrependEnvOnCmd(t *testing.T) {
|
||||
|
||||
func TestRunWithBuildArgs(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
b.buildArgs.argsFromOptions["HTTP_PROXY"] = strPtr("FOO")
|
||||
args := newBuildArgs(make(map[string]*string))
|
||||
args.argsFromOptions["HTTP_PROXY"] = strPtr("FOO")
|
||||
b.disableCommit = false
|
||||
sb := newDispatchRequest(b, '`', nil, args, newStagesBuildResults())
|
||||
|
||||
runConfig := &container.Config{}
|
||||
origCmd := strslice.StrSlice([]string{"cmd", "in", "from", "image"})
|
||||
@@ -512,14 +451,18 @@ func TestRunWithBuildArgs(t *testing.T) {
|
||||
assert.Equal(t, strslice.StrSlice(nil), cfg.Config.Entrypoint)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
req := defaultDispatchReq(b, "abcdef")
|
||||
require.NoError(t, from(req))
|
||||
b.buildArgs.AddArg("one", strPtr("two"))
|
||||
|
||||
req.args = []string{"echo foo"}
|
||||
require.NoError(t, run(req))
|
||||
from := &instructions.Stage{BaseName: "abcdef"}
|
||||
err := initializeStage(sb, from)
|
||||
require.NoError(t, err)
|
||||
sb.state.buildArgs.AddArg("one", strPtr("two"))
|
||||
run := &instructions.RunCommand{
|
||||
ShellDependantCmdLine: instructions.ShellDependantCmdLine{
|
||||
CmdLine: strslice.StrSlice{"echo foo"},
|
||||
PrependShell: true,
|
||||
},
|
||||
}
|
||||
require.NoError(t, dispatch(sb, run))
|
||||
|
||||
// Check that runConfig.Cmd has not been modified by run
|
||||
assert.Equal(t, origCmd, req.state.runConfig.Cmd)
|
||||
assert.Equal(t, origCmd, sb.state.runConfig.Cmd)
|
||||
}
|
||||
|
||||
5
vendor/github.com/docker/docker/builder/dockerfile/dispatchers_unix.go
generated
vendored
5
vendor/github.com/docker/docker/builder/dockerfile/dispatchers_unix.go
generated
vendored
@@ -4,7 +4,6 @@ package dockerfile
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
@@ -23,10 +22,6 @@ func normalizeWorkdir(_ string, current string, requested string) (string, error
|
||||
return requested, nil
|
||||
}
|
||||
|
||||
func errNotJSON(command, _ string) error {
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form", command)
|
||||
}
|
||||
|
||||
// equalEnvKeys compare two strings and returns true if they are equal. On
|
||||
// Windows this comparison is case insensitive.
|
||||
func equalEnvKeys(from, to string) bool {
|
||||
|
||||
19
vendor/github.com/docker/docker/builder/dockerfile/dispatchers_windows.go
generated
vendored
19
vendor/github.com/docker/docker/builder/dockerfile/dispatchers_windows.go
generated
vendored
@@ -94,25 +94,6 @@ func normalizeWorkdirWindows(current string, requested string) (string, error) {
|
||||
return (strings.ToUpper(string(requested[0])) + requested[1:]), nil
|
||||
}
|
||||
|
||||
func errNotJSON(command, original string) error {
|
||||
// For Windows users, give a hint if it looks like it might contain
|
||||
// a path which hasn't been escaped such as ["c:\windows\system32\prog.exe", "-param"],
|
||||
// as JSON must be escaped. Unfortunate...
|
||||
//
|
||||
// Specifically looking for quote-driveletter-colon-backslash, there's no
|
||||
// double backslash and a [] pair. No, this is not perfect, but it doesn't
|
||||
// have to be. It's simply a hint to make life a little easier.
|
||||
extra := ""
|
||||
original = filepath.FromSlash(strings.ToLower(strings.Replace(strings.ToLower(original), strings.ToLower(command)+" ", "", -1)))
|
||||
if len(regexp.MustCompile(`"[a-z]:\\.*`).FindStringSubmatch(original)) > 0 &&
|
||||
!strings.Contains(original, `\\`) &&
|
||||
strings.Contains(original, "[") &&
|
||||
strings.Contains(original, "]") {
|
||||
extra = fmt.Sprintf(`. It looks like '%s' includes a file path without an escaped back-slash. JSON requires back-slashes to be escaped such as ["c:\\path\\to\\file.exe", "/parameter"]`, original)
|
||||
}
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form%s", command, extra)
|
||||
}
|
||||
|
||||
// equalEnvKeys compare two strings and returns true if they are equal. On
|
||||
// Windows this comparison is case insensitive.
|
||||
func equalEnvKeys(from, to string) bool {
|
||||
|
||||
408
vendor/github.com/docker/docker/builder/dockerfile/evaluator.go
generated
vendored
408
vendor/github.com/docker/docker/builder/dockerfile/evaluator.go
generated
vendored
@@ -20,169 +20,85 @@
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/docker/docker/builder/dockerfile/instructions"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/runconfig/opts"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Environment variable interpolation will happen on these statements only.
|
||||
var replaceEnvAllowed = map[string]bool{
|
||||
command.Env: true,
|
||||
command.Label: true,
|
||||
command.Add: true,
|
||||
command.Copy: true,
|
||||
command.Workdir: true,
|
||||
command.Expose: true,
|
||||
command.Volume: true,
|
||||
command.User: true,
|
||||
command.StopSignal: true,
|
||||
command.Arg: true,
|
||||
}
|
||||
|
||||
// Certain commands are allowed to have their args split into more
|
||||
// words after env var replacements. Meaning:
|
||||
// ENV foo="123 456"
|
||||
// EXPOSE $foo
|
||||
// should result in the same thing as:
|
||||
// EXPOSE 123 456
|
||||
// and not treat "123 456" as a single word.
|
||||
// Note that: EXPOSE "$foo" and EXPOSE $foo are not the same thing.
|
||||
// Quotes will cause it to still be treated as single word.
|
||||
var allowWordExpansion = map[string]bool{
|
||||
command.Expose: true,
|
||||
}
|
||||
|
||||
type dispatchRequest struct {
|
||||
builder *Builder // TODO: replace this with a smaller interface
|
||||
args []string
|
||||
attributes map[string]bool
|
||||
flags *BFlags
|
||||
original string
|
||||
shlex *ShellLex
|
||||
state *dispatchState
|
||||
source builder.Source
|
||||
}
|
||||
|
||||
func newDispatchRequestFromOptions(options dispatchOptions, builder *Builder, args []string) dispatchRequest {
|
||||
return dispatchRequest{
|
||||
builder: builder,
|
||||
args: args,
|
||||
attributes: options.node.Attributes,
|
||||
original: options.node.Original,
|
||||
flags: NewBFlagsWithArgs(options.node.Flags),
|
||||
shlex: options.shlex,
|
||||
state: options.state,
|
||||
source: options.source,
|
||||
}
|
||||
}
|
||||
|
||||
type dispatcher func(dispatchRequest) error
|
||||
|
||||
var evaluateTable map[string]dispatcher
|
||||
|
||||
func init() {
|
||||
evaluateTable = map[string]dispatcher{
|
||||
command.Add: add,
|
||||
command.Arg: arg,
|
||||
command.Cmd: cmd,
|
||||
command.Copy: dispatchCopy, // copy() is a go builtin
|
||||
command.Entrypoint: entrypoint,
|
||||
command.Env: env,
|
||||
command.Expose: expose,
|
||||
command.From: from,
|
||||
command.Healthcheck: healthcheck,
|
||||
command.Label: label,
|
||||
command.Maintainer: maintainer,
|
||||
command.Onbuild: onbuild,
|
||||
command.Run: run,
|
||||
command.Shell: shell,
|
||||
command.StopSignal: stopSignal,
|
||||
command.User: user,
|
||||
command.Volume: volume,
|
||||
command.Workdir: workdir,
|
||||
}
|
||||
}
|
||||
|
||||
func formatStep(stepN int, stepTotal int) string {
|
||||
return fmt.Sprintf("%d/%d", stepN+1, stepTotal)
|
||||
}
|
||||
|
||||
// This method is the entrypoint to all statement handling routines.
|
||||
//
|
||||
// Almost all nodes will have this structure:
|
||||
// Child[Node, Node, Node] where Child is from parser.Node.Children and each
|
||||
// node comes from parser.Node.Next. This forms a "line" with a statement and
|
||||
// arguments and we process them in this normalized form by hitting
|
||||
// evaluateTable with the leaf nodes of the command and the Builder object.
|
||||
//
|
||||
// ONBUILD is a special case; in this case the parser will emit:
|
||||
// Child[Node, Child[Node, Node...]] where the first node is the literal
|
||||
// "onbuild" and the child entrypoint is the command of the ONBUILD statement,
|
||||
// such as `RUN` in ONBUILD RUN foo. There is special case logic in here to
|
||||
// deal with that, at least until it becomes more of a general concern with new
|
||||
// features.
|
||||
func (b *Builder) dispatch(options dispatchOptions) (*dispatchState, error) {
|
||||
node := options.node
|
||||
cmd := node.Value
|
||||
upperCasedCmd := strings.ToUpper(cmd)
|
||||
|
||||
// To ensure the user is given a decent error message if the platform
|
||||
// on which the daemon is running does not support a builder command.
|
||||
if err := platformSupports(strings.ToLower(cmd)); err != nil {
|
||||
buildsFailed.WithValues(metricsCommandNotSupportedError).Inc()
|
||||
return nil, validationError{err}
|
||||
}
|
||||
|
||||
msg := bytes.NewBufferString(fmt.Sprintf("Step %s : %s%s",
|
||||
options.stepMsg, upperCasedCmd, formatFlags(node.Flags)))
|
||||
|
||||
args := []string{}
|
||||
ast := node
|
||||
if cmd == command.Onbuild {
|
||||
var err error
|
||||
ast, args, err = handleOnBuildNode(node, msg)
|
||||
func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
|
||||
if c, ok := cmd.(instructions.PlatformSpecific); ok {
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
err := c.CheckPlatform(optionsOS)
|
||||
if err != nil {
|
||||
return nil, validationError{err}
|
||||
return validationError{err}
|
||||
}
|
||||
}
|
||||
runConfigEnv := d.state.runConfig.Env
|
||||
envs := append(runConfigEnv, d.state.buildArgs.FilterAllowed(runConfigEnv)...)
|
||||
|
||||
if ex, ok := cmd.(instructions.SupportsSingleWordExpansion); ok {
|
||||
err := ex.Expand(func(word string) (string, error) {
|
||||
return d.shlex.ProcessWord(word, envs)
|
||||
})
|
||||
if err != nil {
|
||||
return validationError{err}
|
||||
}
|
||||
}
|
||||
|
||||
runConfigEnv := options.state.runConfig.Env
|
||||
envs := append(runConfigEnv, b.buildArgs.FilterAllowed(runConfigEnv)...)
|
||||
processFunc := createProcessWordFunc(options.shlex, cmd, envs)
|
||||
words, err := getDispatchArgsFromNode(ast, processFunc, msg)
|
||||
if err != nil {
|
||||
buildsFailed.WithValues(metricsErrorProcessingCommandsError).Inc()
|
||||
return nil, validationError{err}
|
||||
defer func() {
|
||||
if d.builder.options.ForceRemove {
|
||||
d.builder.containerManager.RemoveAll(d.builder.Stdout)
|
||||
return
|
||||
}
|
||||
if d.builder.options.Remove && err == nil {
|
||||
d.builder.containerManager.RemoveAll(d.builder.Stdout)
|
||||
return
|
||||
}
|
||||
}()
|
||||
switch c := cmd.(type) {
|
||||
case *instructions.EnvCommand:
|
||||
return dispatchEnv(d, c)
|
||||
case *instructions.MaintainerCommand:
|
||||
return dispatchMaintainer(d, c)
|
||||
case *instructions.LabelCommand:
|
||||
return dispatchLabel(d, c)
|
||||
case *instructions.AddCommand:
|
||||
return dispatchAdd(d, c)
|
||||
case *instructions.CopyCommand:
|
||||
return dispatchCopy(d, c)
|
||||
case *instructions.OnbuildCommand:
|
||||
return dispatchOnbuild(d, c)
|
||||
case *instructions.WorkdirCommand:
|
||||
return dispatchWorkdir(d, c)
|
||||
case *instructions.RunCommand:
|
||||
return dispatchRun(d, c)
|
||||
case *instructions.CmdCommand:
|
||||
return dispatchCmd(d, c)
|
||||
case *instructions.HealthCheckCommand:
|
||||
return dispatchHealthcheck(d, c)
|
||||
case *instructions.EntrypointCommand:
|
||||
return dispatchEntrypoint(d, c)
|
||||
case *instructions.ExposeCommand:
|
||||
return dispatchExpose(d, c, envs)
|
||||
case *instructions.UserCommand:
|
||||
return dispatchUser(d, c)
|
||||
case *instructions.VolumeCommand:
|
||||
return dispatchVolume(d, c)
|
||||
case *instructions.StopSignalCommand:
|
||||
return dispatchStopSignal(d, c)
|
||||
case *instructions.ArgCommand:
|
||||
return dispatchArg(d, c)
|
||||
case *instructions.ShellCommand:
|
||||
return dispatchShell(d, c)
|
||||
}
|
||||
args = append(args, words...)
|
||||
|
||||
fmt.Fprintln(b.Stdout, msg.String())
|
||||
|
||||
f, ok := evaluateTable[cmd]
|
||||
if !ok {
|
||||
buildsFailed.WithValues(metricsUnknownInstructionError).Inc()
|
||||
return nil, validationError{errors.Errorf("unknown instruction: %s", upperCasedCmd)}
|
||||
}
|
||||
options.state.updateRunConfig()
|
||||
err = f(newDispatchRequestFromOptions(options, b, args))
|
||||
return options.state, err
|
||||
}
|
||||
|
||||
type dispatchOptions struct {
|
||||
state *dispatchState
|
||||
stepMsg string
|
||||
node *parser.Node
|
||||
shlex *ShellLex
|
||||
source builder.Source
|
||||
return errors.Errorf("unsupported command type: %v", reflect.TypeOf(cmd))
|
||||
}
|
||||
|
||||
// dispatchState is a data object which is modified by dispatchers
|
||||
@@ -193,10 +109,95 @@ type dispatchState struct {
|
||||
imageID string
|
||||
baseImage builder.Image
|
||||
stageName string
|
||||
buildArgs *buildArgs
|
||||
}
|
||||
|
||||
func newDispatchState() *dispatchState {
|
||||
return &dispatchState{runConfig: &container.Config{}}
|
||||
func newDispatchState(baseArgs *buildArgs) *dispatchState {
|
||||
args := baseArgs.Clone()
|
||||
args.ResetAllowed()
|
||||
return &dispatchState{runConfig: &container.Config{}, buildArgs: args}
|
||||
}
|
||||
|
||||
type stagesBuildResults struct {
|
||||
flat []*container.Config
|
||||
indexed map[string]*container.Config
|
||||
}
|
||||
|
||||
func newStagesBuildResults() *stagesBuildResults {
|
||||
return &stagesBuildResults{
|
||||
indexed: make(map[string]*container.Config),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *stagesBuildResults) getByName(name string) (*container.Config, bool) {
|
||||
c, ok := r.indexed[strings.ToLower(name)]
|
||||
return c, ok
|
||||
}
|
||||
|
||||
func (r *stagesBuildResults) validateIndex(i int) error {
|
||||
if i == len(r.flat) {
|
||||
return errors.New("refers to current build stage")
|
||||
}
|
||||
if i < 0 || i > len(r.flat) {
|
||||
return errors.New("index out of bounds")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *stagesBuildResults) get(nameOrIndex string) (*container.Config, error) {
|
||||
if c, ok := r.getByName(nameOrIndex); ok {
|
||||
return c, nil
|
||||
}
|
||||
ix, err := strconv.ParseInt(nameOrIndex, 10, 0)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := r.validateIndex(int(ix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.flat[ix], nil
|
||||
}
|
||||
|
||||
func (r *stagesBuildResults) checkStageNameAvailable(name string) error {
|
||||
if name != "" {
|
||||
if _, ok := r.getByName(name); ok {
|
||||
return errors.Errorf("%s stage name already used", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *stagesBuildResults) commitStage(name string, config *container.Config) error {
|
||||
if name != "" {
|
||||
if _, ok := r.getByName(name); ok {
|
||||
return errors.Errorf("%s stage name already used", name)
|
||||
}
|
||||
r.indexed[strings.ToLower(name)] = config
|
||||
}
|
||||
r.flat = append(r.flat, config)
|
||||
return nil
|
||||
}
|
||||
|
||||
func commitStage(state *dispatchState, stages *stagesBuildResults) error {
|
||||
return stages.commitStage(state.stageName, state.runConfig)
|
||||
}
|
||||
|
||||
type dispatchRequest struct {
|
||||
state *dispatchState
|
||||
shlex *ShellLex
|
||||
builder *Builder
|
||||
source builder.Source
|
||||
stages *stagesBuildResults
|
||||
}
|
||||
|
||||
func newDispatchRequest(builder *Builder, escapeToken rune, source builder.Source, buildArgs *buildArgs, stages *stagesBuildResults) dispatchRequest {
|
||||
return dispatchRequest{
|
||||
state: newDispatchState(buildArgs),
|
||||
shlex: NewShellLex(escapeToken),
|
||||
builder: builder,
|
||||
source: source,
|
||||
stages: stages,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *dispatchState) updateRunConfig() {
|
||||
@@ -208,120 +209,31 @@ func (s *dispatchState) hasFromImage() bool {
|
||||
return s.imageID != "" || (s.baseImage != nil && s.baseImage.ImageID() == "")
|
||||
}
|
||||
|
||||
func (s *dispatchState) isCurrentStage(target string) bool {
|
||||
if target == "" {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(s.stageName, target)
|
||||
}
|
||||
|
||||
func (s *dispatchState) beginStage(stageName string, image builder.Image) {
|
||||
s.stageName = stageName
|
||||
s.imageID = image.ImageID()
|
||||
|
||||
if image.RunConfig() != nil {
|
||||
s.runConfig = image.RunConfig()
|
||||
// copy avoids referencing the same instance when 2 stages have the same base
|
||||
s.runConfig = copyRunConfig(image.RunConfig())
|
||||
} else {
|
||||
s.runConfig = &container.Config{}
|
||||
}
|
||||
s.baseImage = image
|
||||
s.setDefaultPath()
|
||||
s.runConfig.OpenStdin = false
|
||||
s.runConfig.StdinOnce = false
|
||||
}
|
||||
|
||||
// Add the default PATH to runConfig.ENV if one exists for the platform and there
|
||||
// Add the default PATH to runConfig.ENV if one exists for the operating system and there
|
||||
// is no PATH set. Note that Windows containers on Windows won't have one as it's set by HCS
|
||||
func (s *dispatchState) setDefaultPath() {
|
||||
// TODO @jhowardmsft LCOW Support - This will need revisiting later
|
||||
platform := runtime.GOOS
|
||||
if system.LCOWSupported() {
|
||||
platform = "linux"
|
||||
}
|
||||
if system.DefaultPathEnv(platform) == "" {
|
||||
defaultPath := system.DefaultPathEnv(s.baseImage.OperatingSystem())
|
||||
if defaultPath == "" {
|
||||
return
|
||||
}
|
||||
envMap := opts.ConvertKVStringsToMap(s.runConfig.Env)
|
||||
if _, ok := envMap["PATH"]; !ok {
|
||||
s.runConfig.Env = append(s.runConfig.Env, "PATH="+system.DefaultPathEnv(platform))
|
||||
s.runConfig.Env = append(s.runConfig.Env, "PATH="+defaultPath)
|
||||
}
|
||||
}
|
||||
|
||||
func handleOnBuildNode(ast *parser.Node, msg *bytes.Buffer) (*parser.Node, []string, error) {
|
||||
if ast.Next == nil {
|
||||
return nil, nil, validationError{errors.New("ONBUILD requires at least one argument")}
|
||||
}
|
||||
ast = ast.Next.Children[0]
|
||||
msg.WriteString(" " + ast.Value + formatFlags(ast.Flags))
|
||||
return ast, []string{ast.Value}, nil
|
||||
}
|
||||
|
||||
func formatFlags(flags []string) string {
|
||||
if len(flags) > 0 {
|
||||
return " " + strings.Join(flags, " ")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getDispatchArgsFromNode(ast *parser.Node, processFunc processWordFunc, msg *bytes.Buffer) ([]string, error) {
|
||||
args := []string{}
|
||||
for i := 0; ast.Next != nil; i++ {
|
||||
ast = ast.Next
|
||||
words, err := processFunc(ast.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, words...)
|
||||
msg.WriteString(" " + ast.Value)
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
type processWordFunc func(string) ([]string, error)
|
||||
|
||||
func createProcessWordFunc(shlex *ShellLex, cmd string, envs []string) processWordFunc {
|
||||
switch {
|
||||
case !replaceEnvAllowed[cmd]:
|
||||
return func(word string) ([]string, error) {
|
||||
return []string{word}, nil
|
||||
}
|
||||
case allowWordExpansion[cmd]:
|
||||
return func(word string) ([]string, error) {
|
||||
return shlex.ProcessWords(word, envs)
|
||||
}
|
||||
default:
|
||||
return func(word string) ([]string, error) {
|
||||
word, err := shlex.ProcessWord(word, envs)
|
||||
return []string{word}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkDispatch does a simple check for syntax errors of the Dockerfile.
|
||||
// Because some of the instructions can only be validated through runtime,
|
||||
// arg, env, etc., this syntax check will not be complete and could not replace
|
||||
// the runtime check. Instead, this function is only a helper that allows
|
||||
// user to find out the obvious error in Dockerfile earlier on.
|
||||
func checkDispatch(ast *parser.Node) error {
|
||||
cmd := ast.Value
|
||||
upperCasedCmd := strings.ToUpper(cmd)
|
||||
|
||||
// To ensure the user is given a decent error message if the platform
|
||||
// on which the daemon is running does not support a builder command.
|
||||
if err := platformSupports(strings.ToLower(cmd)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The instruction itself is ONBUILD, we will make sure it follows with at
|
||||
// least one argument
|
||||
if upperCasedCmd == "ONBUILD" {
|
||||
if ast.Next == nil {
|
||||
buildsFailed.WithValues(metricsMissingOnbuildArgumentsError).Inc()
|
||||
return errors.New("ONBUILD requires at least one argument")
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := evaluateTable[cmd]; ok {
|
||||
return nil
|
||||
}
|
||||
buildsFailed.WithValues(metricsUnknownInstructionError).Inc()
|
||||
return errors.Errorf("unknown instruction: %s", upperCasedCmd)
|
||||
}
|
||||
|
||||
168
vendor/github.com/docker/docker/builder/dockerfile/evaluator_test.go
generated
vendored
168
vendor/github.com/docker/docker/builder/dockerfile/evaluator_test.go
generated
vendored
@@ -1,21 +1,19 @@
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/docker/docker/builder/dockerfile/instructions"
|
||||
"github.com/docker/docker/builder/remotecontext"
|
||||
"github.com/docker/docker/internal/testutil"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
)
|
||||
|
||||
type dispatchTestCase struct {
|
||||
name, dockerfile, expectedError string
|
||||
files map[string]string
|
||||
name, expectedError string
|
||||
cmd instructions.Command
|
||||
files map[string]string
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -23,108 +21,73 @@ func init() {
|
||||
}
|
||||
|
||||
func initDispatchTestCases() []dispatchTestCase {
|
||||
dispatchTestCases := []dispatchTestCase{{
|
||||
name: "copyEmptyWhitespace",
|
||||
dockerfile: `COPY
|
||||
quux \
|
||||
bar`,
|
||||
expectedError: "COPY requires at least two arguments",
|
||||
},
|
||||
dispatchTestCases := []dispatchTestCase{
|
||||
{
|
||||
name: "ONBUILD forbidden FROM",
|
||||
dockerfile: "ONBUILD FROM scratch",
|
||||
expectedError: "FROM isn't allowed as an ONBUILD trigger",
|
||||
files: nil,
|
||||
},
|
||||
{
|
||||
name: "ONBUILD forbidden MAINTAINER",
|
||||
dockerfile: "ONBUILD MAINTAINER docker.io",
|
||||
expectedError: "MAINTAINER isn't allowed as an ONBUILD trigger",
|
||||
files: nil,
|
||||
},
|
||||
{
|
||||
name: "ARG two arguments",
|
||||
dockerfile: "ARG foo bar",
|
||||
expectedError: "ARG requires exactly one argument",
|
||||
files: nil,
|
||||
},
|
||||
{
|
||||
name: "MAINTAINER unknown flag",
|
||||
dockerfile: "MAINTAINER --boo joe@example.com",
|
||||
expectedError: "Unknown flag: boo",
|
||||
files: nil,
|
||||
},
|
||||
{
|
||||
name: "ADD multiple files to file",
|
||||
dockerfile: "ADD file1.txt file2.txt test",
|
||||
name: "ADD multiple files to file",
|
||||
cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{
|
||||
"file1.txt",
|
||||
"file2.txt",
|
||||
"test",
|
||||
}},
|
||||
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "JSON ADD multiple files to file",
|
||||
dockerfile: `ADD ["file1.txt", "file2.txt", "test"]`,
|
||||
name: "Wildcard ADD multiple files to file",
|
||||
cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{
|
||||
"file*.txt",
|
||||
"test",
|
||||
}},
|
||||
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "Wildcard ADD multiple files to file",
|
||||
dockerfile: "ADD file*.txt test",
|
||||
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "Wildcard JSON ADD multiple files to file",
|
||||
dockerfile: `ADD ["file*.txt", "test"]`,
|
||||
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "COPY multiple files to file",
|
||||
dockerfile: "COPY file1.txt file2.txt test",
|
||||
name: "COPY multiple files to file",
|
||||
cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{
|
||||
"file1.txt",
|
||||
"file2.txt",
|
||||
"test",
|
||||
}},
|
||||
expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "JSON COPY multiple files to file",
|
||||
dockerfile: `COPY ["file1.txt", "file2.txt", "test"]`,
|
||||
expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "ADD multiple files to file with whitespace",
|
||||
dockerfile: `ADD [ "test file1.txt", "test file2.txt", "test" ]`,
|
||||
name: "ADD multiple files to file with whitespace",
|
||||
cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{
|
||||
"test file1.txt",
|
||||
"test file2.txt",
|
||||
"test",
|
||||
}},
|
||||
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "COPY multiple files to file with whitespace",
|
||||
dockerfile: `COPY [ "test file1.txt", "test file2.txt", "test" ]`,
|
||||
name: "COPY multiple files to file with whitespace",
|
||||
cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{
|
||||
"test file1.txt",
|
||||
"test file2.txt",
|
||||
"test",
|
||||
}},
|
||||
expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
|
||||
files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"},
|
||||
},
|
||||
{
|
||||
name: "COPY wildcard no files",
|
||||
dockerfile: `COPY file*.txt /tmp/`,
|
||||
name: "COPY wildcard no files",
|
||||
cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{
|
||||
"file*.txt",
|
||||
"/tmp/",
|
||||
}},
|
||||
expectedError: "COPY failed: no source files were specified",
|
||||
files: nil,
|
||||
},
|
||||
{
|
||||
name: "COPY url",
|
||||
dockerfile: `COPY https://index.docker.io/robots.txt /`,
|
||||
name: "COPY url",
|
||||
cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{
|
||||
"https://index.docker.io/robots.txt",
|
||||
"/",
|
||||
}},
|
||||
expectedError: "source can't be a URL for COPY",
|
||||
files: nil,
|
||||
},
|
||||
{
|
||||
name: "Chaining ONBUILD",
|
||||
dockerfile: `ONBUILD ONBUILD RUN touch foobar`,
|
||||
expectedError: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
|
||||
files: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid instruction",
|
||||
dockerfile: `foo bar`,
|
||||
expectedError: "unknown instruction: FOO",
|
||||
files: nil,
|
||||
}}
|
||||
|
||||
return dispatchTestCases
|
||||
@@ -170,41 +133,8 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
|
||||
}
|
||||
}()
|
||||
|
||||
r := strings.NewReader(testCase.dockerfile)
|
||||
result, err := parser.Parse(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when parsing Dockerfile: %s", err)
|
||||
}
|
||||
|
||||
options := &types.ImageBuildOptions{
|
||||
BuildArgs: make(map[string]*string),
|
||||
}
|
||||
|
||||
b := &Builder{
|
||||
options: options,
|
||||
Stdout: ioutil.Discard,
|
||||
buildArgs: newBuildArgs(options.BuildArgs),
|
||||
}
|
||||
|
||||
shlex := NewShellLex(parser.DefaultEscapeToken)
|
||||
n := result.AST
|
||||
state := &dispatchState{runConfig: &container.Config{}}
|
||||
opts := dispatchOptions{
|
||||
state: state,
|
||||
stepMsg: formatStep(0, len(n.Children)),
|
||||
node: n.Children[0],
|
||||
shlex: shlex,
|
||||
source: context,
|
||||
}
|
||||
state, err = b.dispatch(opts)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("No error when executing test %s", testCase.name)
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), testCase.expectedError) {
|
||||
t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", testCase.expectedError, err.Error())
|
||||
}
|
||||
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', context, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
err = dispatch(sb, testCase.cmd)
|
||||
testutil.ErrorContains(t, err, testCase.expectedError)
|
||||
}
|
||||
|
||||
9
vendor/github.com/docker/docker/builder/dockerfile/evaluator_unix.go
generated
vendored
9
vendor/github.com/docker/docker/builder/dockerfile/evaluator_unix.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package dockerfile
|
||||
|
||||
// platformSupports is a short-term function to give users a quality error
|
||||
// message if a Dockerfile uses a command not supported on the platform.
|
||||
func platformSupports(command string) error {
|
||||
return nil
|
||||
}
|
||||
13
vendor/github.com/docker/docker/builder/dockerfile/evaluator_windows.go
generated
vendored
13
vendor/github.com/docker/docker/builder/dockerfile/evaluator_windows.go
generated
vendored
@@ -1,13 +0,0 @@
|
||||
package dockerfile
|
||||
|
||||
import "fmt"
|
||||
|
||||
// platformSupports is gives users a quality error message if a Dockerfile uses
|
||||
// a command not supported on the platform.
|
||||
func platformSupports(command string) error {
|
||||
switch command {
|
||||
case "stopsignal":
|
||||
return fmt.Errorf("The daemon on this platform does not support the command '%s'", command)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
91
vendor/github.com/docker/docker/builder/dockerfile/imagecontext.go
generated
vendored
91
vendor/github.com/docker/docker/builder/dockerfile/imagecontext.go
generated
vendored
@@ -1,91 +1,18 @@
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/remotecontext"
|
||||
dockerimage "github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type buildStage struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func newBuildStage(imageID string) *buildStage {
|
||||
return &buildStage{id: imageID}
|
||||
}
|
||||
|
||||
func (b *buildStage) ImageID() string {
|
||||
return b.id
|
||||
}
|
||||
|
||||
func (b *buildStage) update(imageID string) {
|
||||
b.id = imageID
|
||||
}
|
||||
|
||||
// buildStages tracks each stage of a build so they can be retrieved by index
|
||||
// or by name.
|
||||
type buildStages struct {
|
||||
sequence []*buildStage
|
||||
byName map[string]*buildStage
|
||||
}
|
||||
|
||||
func newBuildStages() *buildStages {
|
||||
return &buildStages{byName: make(map[string]*buildStage)}
|
||||
}
|
||||
|
||||
func (s *buildStages) getByName(name string) (*buildStage, bool) {
|
||||
stage, ok := s.byName[strings.ToLower(name)]
|
||||
return stage, ok
|
||||
}
|
||||
|
||||
func (s *buildStages) get(indexOrName string) (*buildStage, error) {
|
||||
index, err := strconv.Atoi(indexOrName)
|
||||
if err == nil {
|
||||
if err := s.validateIndex(index); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.sequence[index], nil
|
||||
}
|
||||
if im, ok := s.byName[strings.ToLower(indexOrName)]; ok {
|
||||
return im, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *buildStages) validateIndex(i int) error {
|
||||
if i < 0 || i >= len(s.sequence)-1 {
|
||||
if i == len(s.sequence)-1 {
|
||||
return errors.New("refers to current build stage")
|
||||
}
|
||||
return errors.New("index out of bounds")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *buildStages) add(name string, image builder.Image) error {
|
||||
stage := newBuildStage(image.ImageID())
|
||||
name = strings.ToLower(name)
|
||||
if len(name) > 0 {
|
||||
if _, ok := s.byName[name]; ok {
|
||||
return errors.Errorf("duplicate name %s", name)
|
||||
}
|
||||
s.byName[name] = stage
|
||||
}
|
||||
s.sequence = append(s.sequence, stage)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *buildStages) update(imageID string) {
|
||||
s.sequence[len(s.sequence)-1].update(imageID)
|
||||
}
|
||||
|
||||
type getAndMountFunc func(string, bool) (builder.Image, builder.ReleaseableLayer, error)
|
||||
|
||||
// imageSources mounts images and provides a cache for mounted images. It tracks
|
||||
@@ -94,11 +21,8 @@ type imageSources struct {
|
||||
byImageID map[string]*imageMount
|
||||
mounts []*imageMount
|
||||
getImage getAndMountFunc
|
||||
cache pathCache // TODO: remove
|
||||
}
|
||||
|
||||
// TODO @jhowardmsft LCOW Support: Eventually, platform can be moved to options.Options.Platform,
|
||||
// and removed from builderOptions, but that can't be done yet as it would affect the API.
|
||||
func newImageSources(ctx context.Context, options builderOptions) *imageSources {
|
||||
getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ReleaseableLayer, error) {
|
||||
pullOption := backend.PullOptionNoPull
|
||||
@@ -109,11 +33,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
|
||||
pullOption = backend.PullOptionPreferLocal
|
||||
}
|
||||
}
|
||||
optionsPlatform := system.ParsePlatform(options.Options.Platform)
|
||||
return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{
|
||||
PullOption: pullOption,
|
||||
AuthConfig: options.Options.AuthConfigs,
|
||||
Output: options.ProgressWriter.Output,
|
||||
Platform: options.Platform,
|
||||
OS: optionsPlatform.OS,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -150,7 +75,13 @@ func (m *imageSources) Unmount() (retErr error) {
|
||||
func (m *imageSources) Add(im *imageMount) {
|
||||
switch im.image {
|
||||
case nil:
|
||||
im.image = &dockerimage.Image{}
|
||||
// set the OS for scratch images
|
||||
os := runtime.GOOS
|
||||
// Windows does not support scratch except for LCOW
|
||||
if runtime.GOOS == "windows" {
|
||||
os = "linux"
|
||||
}
|
||||
im.image = &dockerimage.Image{V1Image: dockerimage.V1Image{OS: os}}
|
||||
default:
|
||||
m.byImageID[im.image.ImageID()] = im
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dockerfile
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package dockerfile
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -34,10 +34,10 @@ func TestBuilderFlags(t *testing.T) {
|
||||
t.Fatalf("Test3 of %q was supposed to work: %s", bf.Args, err)
|
||||
}
|
||||
|
||||
if flStr1.IsUsed() == true {
|
||||
if flStr1.IsUsed() {
|
||||
t.Fatal("Test3 - str1 was not used!")
|
||||
}
|
||||
if flBool1.IsUsed() == true {
|
||||
if flBool1.IsUsed() {
|
||||
t.Fatal("Test3 - bool1 was not used!")
|
||||
}
|
||||
|
||||
@@ -58,10 +58,10 @@ func TestBuilderFlags(t *testing.T) {
|
||||
if flBool1.IsTrue() {
|
||||
t.Fatal("Bool1 was supposed to default to: false")
|
||||
}
|
||||
if flStr1.IsUsed() == true {
|
||||
if flStr1.IsUsed() {
|
||||
t.Fatal("Str1 was not used!")
|
||||
}
|
||||
if flBool1.IsUsed() == true {
|
||||
if flBool1.IsUsed() {
|
||||
t.Fatal("Bool1 was not used!")
|
||||
}
|
||||
|
||||
396
vendor/github.com/docker/docker/builder/dockerfile/instructions/commands.go
generated
vendored
Normal file
396
vendor/github.com/docker/docker/builder/dockerfile/instructions/commands.go
generated
vendored
Normal file
@@ -0,0 +1,396 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
)
|
||||
|
||||
// KeyValuePair represent an arbitrary named value (useful in slice insted of map[string] string to preserve ordering)
|
||||
type KeyValuePair struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (kvp *KeyValuePair) String() string {
|
||||
return kvp.Key + "=" + kvp.Value
|
||||
}
|
||||
|
||||
// Command is implemented by every command present in a dockerfile
|
||||
type Command interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// KeyValuePairs is a slice of KeyValuePair
|
||||
type KeyValuePairs []KeyValuePair
|
||||
|
||||
// withNameAndCode is the base of every command in a Dockerfile (String() returns its source code)
|
||||
type withNameAndCode struct {
|
||||
code string
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *withNameAndCode) String() string {
|
||||
return c.code
|
||||
}
|
||||
|
||||
// Name of the command
|
||||
func (c *withNameAndCode) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func newWithNameAndCode(req parseRequest) withNameAndCode {
|
||||
return withNameAndCode{code: strings.TrimSpace(req.original), name: req.command}
|
||||
}
|
||||
|
||||
// SingleWordExpander is a provider for variable expansion where 1 word => 1 output
|
||||
type SingleWordExpander func(word string) (string, error)
|
||||
|
||||
// SupportsSingleWordExpansion interface marks a command as supporting variable expansion
|
||||
type SupportsSingleWordExpansion interface {
|
||||
Expand(expander SingleWordExpander) error
|
||||
}
|
||||
|
||||
// PlatformSpecific adds platform checks to a command
|
||||
type PlatformSpecific interface {
|
||||
CheckPlatform(platform string) error
|
||||
}
|
||||
|
||||
func expandKvp(kvp KeyValuePair, expander SingleWordExpander) (KeyValuePair, error) {
|
||||
key, err := expander(kvp.Key)
|
||||
if err != nil {
|
||||
return KeyValuePair{}, err
|
||||
}
|
||||
value, err := expander(kvp.Value)
|
||||
if err != nil {
|
||||
return KeyValuePair{}, err
|
||||
}
|
||||
return KeyValuePair{Key: key, Value: value}, nil
|
||||
}
|
||||
func expandKvpsInPlace(kvps KeyValuePairs, expander SingleWordExpander) error {
|
||||
for i, kvp := range kvps {
|
||||
newKvp, err := expandKvp(kvp, expander)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kvps[i] = newKvp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func expandSliceInPlace(values []string, expander SingleWordExpander) error {
|
||||
for i, v := range values {
|
||||
newValue, err := expander(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values[i] = newValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnvCommand : ENV key1 value1 [keyN valueN...]
|
||||
type EnvCommand struct {
|
||||
withNameAndCode
|
||||
Env KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *EnvCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandKvpsInPlace(c.Env, expander)
|
||||
}
|
||||
|
||||
// MaintainerCommand : MAINTAINER maintainer_name
|
||||
type MaintainerCommand struct {
|
||||
withNameAndCode
|
||||
Maintainer string
|
||||
}
|
||||
|
||||
// LabelCommand : LABEL some json data describing the image
|
||||
//
|
||||
// Sets the Label variable foo to bar,
|
||||
//
|
||||
type LabelCommand struct {
|
||||
withNameAndCode
|
||||
Labels KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *LabelCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandKvpsInPlace(c.Labels, expander)
|
||||
}
|
||||
|
||||
// SourcesAndDest represent a list of source files and a destination
|
||||
type SourcesAndDest []string
|
||||
|
||||
// Sources list the source paths
|
||||
func (s SourcesAndDest) Sources() []string {
|
||||
res := make([]string, len(s)-1)
|
||||
copy(res, s[:len(s)-1])
|
||||
return res
|
||||
}
|
||||
|
||||
// Dest path of the operation
|
||||
func (s SourcesAndDest) Dest() string {
|
||||
return s[len(s)-1]
|
||||
}
|
||||
|
||||
// AddCommand : ADD foo /path
|
||||
//
|
||||
// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
|
||||
// exist here. If you do not wish to have this automatic handling, use COPY.
|
||||
//
|
||||
type AddCommand struct {
|
||||
withNameAndCode
|
||||
SourcesAndDest
|
||||
Chown string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *AddCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.SourcesAndDest, expander)
|
||||
}
|
||||
|
||||
// CopyCommand : COPY foo /path
|
||||
//
|
||||
// Same as 'ADD' but without the tar and remote url handling.
|
||||
//
|
||||
type CopyCommand struct {
|
||||
withNameAndCode
|
||||
SourcesAndDest
|
||||
From string
|
||||
Chown string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *CopyCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.SourcesAndDest, expander)
|
||||
}
|
||||
|
||||
// OnbuildCommand : ONBUILD <some other command>
|
||||
type OnbuildCommand struct {
|
||||
withNameAndCode
|
||||
Expression string
|
||||
}
|
||||
|
||||
// WorkdirCommand : WORKDIR /tmp
|
||||
//
|
||||
// Set the working directory for future RUN/CMD/etc statements.
|
||||
//
|
||||
type WorkdirCommand struct {
|
||||
withNameAndCode
|
||||
Path string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *WorkdirCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Path = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShellDependantCmdLine represents a cmdline optionaly prepended with the shell
|
||||
type ShellDependantCmdLine struct {
|
||||
CmdLine strslice.StrSlice
|
||||
PrependShell bool
|
||||
}
|
||||
|
||||
// RunCommand : RUN some command yo
|
||||
//
|
||||
// run a command and commit the image. Args are automatically prepended with
|
||||
// the current SHELL which defaults to 'sh -c' under linux or 'cmd /S /C' under
|
||||
// Windows, in the event there is only one argument The difference in processing:
|
||||
//
|
||||
// RUN echo hi # sh -c echo hi (Linux)
|
||||
// RUN echo hi # cmd /S /C echo hi (Windows)
|
||||
// RUN [ "echo", "hi" ] # echo hi
|
||||
//
|
||||
type RunCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// CmdCommand : CMD foo
|
||||
//
|
||||
// Set the default command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
type CmdCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// HealthCheckCommand : HEALTHCHECK foo
|
||||
//
|
||||
// Set the default healthcheck command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
type HealthCheckCommand struct {
|
||||
withNameAndCode
|
||||
Health *container.HealthConfig
|
||||
}
|
||||
|
||||
// EntrypointCommand : ENTRYPOINT /usr/sbin/nginx
|
||||
//
|
||||
// Set the entrypoint to /usr/sbin/nginx. Will accept the CMD as the arguments
|
||||
// to /usr/sbin/nginx. Uses the default shell if not in JSON format.
|
||||
//
|
||||
// Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
|
||||
// is initialized at newBuilder time instead of through argument parsing.
|
||||
//
|
||||
type EntrypointCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// ExposeCommand : EXPOSE 6667/tcp 7000/tcp
|
||||
//
|
||||
// Expose ports for links and port mappings. This all ends up in
|
||||
// req.runConfig.ExposedPorts for runconfig.
|
||||
//
|
||||
type ExposeCommand struct {
|
||||
withNameAndCode
|
||||
Ports []string
|
||||
}
|
||||
|
||||
// UserCommand : USER foo
|
||||
//
|
||||
// Set the user to 'foo' for future commands and when running the
|
||||
// ENTRYPOINT/CMD at container run time.
|
||||
//
|
||||
type UserCommand struct {
|
||||
withNameAndCode
|
||||
User string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *UserCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.User)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.User = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// VolumeCommand : VOLUME /foo
|
||||
//
|
||||
// Expose the volume /foo for use. Will also accept the JSON array form.
|
||||
//
|
||||
type VolumeCommand struct {
|
||||
withNameAndCode
|
||||
Volumes []string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *VolumeCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.Volumes, expander)
|
||||
}
|
||||
|
||||
// StopSignalCommand : STOPSIGNAL signal
|
||||
//
|
||||
// Set the signal that will be used to kill the container.
|
||||
type StopSignalCommand struct {
|
||||
withNameAndCode
|
||||
Signal string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *StopSignalCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Signal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signal = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckPlatform checks that the command is supported in the target platform
|
||||
func (c *StopSignalCommand) CheckPlatform(platform string) error {
|
||||
if platform == "windows" {
|
||||
return errors.New("The daemon on this platform does not support the command stopsignal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ArgCommand : ARG name[=value]
|
||||
//
|
||||
// Adds the variable foo to the trusted list of variables that can be passed
|
||||
// to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
|
||||
// Dockerfile author may optionally set a default value of this variable.
|
||||
type ArgCommand struct {
|
||||
withNameAndCode
|
||||
Key string
|
||||
Value *string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *ArgCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Key = p
|
||||
if c.Value != nil {
|
||||
p, err = expander(*c.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Value = &p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShellCommand : SHELL powershell -command
|
||||
//
|
||||
// Set the non-default shell to use.
|
||||
type ShellCommand struct {
|
||||
withNameAndCode
|
||||
Shell strslice.StrSlice
|
||||
}
|
||||
|
||||
// Stage represents a single stage in a multi-stage build
|
||||
type Stage struct {
|
||||
Name string
|
||||
Commands []Command
|
||||
BaseName string
|
||||
SourceCode string
|
||||
}
|
||||
|
||||
// AddCommand to the stage
|
||||
func (s *Stage) AddCommand(cmd Command) {
|
||||
// todo: validate cmd type
|
||||
s.Commands = append(s.Commands, cmd)
|
||||
}
|
||||
|
||||
// IsCurrentStage check if the stage name is the current stage
|
||||
func IsCurrentStage(s []Stage, name string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
return s[len(s)-1].Name == name
|
||||
}
|
||||
|
||||
// CurrentStage return the last stage in a slice
|
||||
func CurrentStage(s []Stage) (*Stage, error) {
|
||||
if len(s) == 0 {
|
||||
return nil, errors.New("No build stage in current context")
|
||||
}
|
||||
return &s[len(s)-1], nil
|
||||
}
|
||||
|
||||
// HasStage looks for the presence of a given stage name
|
||||
func HasStage(s []Stage, name string) (int, bool) {
|
||||
for i, stage := range s {
|
||||
if stage.Name == name {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
9
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_unix.go
generated
vendored
Normal file
9
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_unix.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// +build !windows
|
||||
|
||||
package instructions
|
||||
|
||||
import "fmt"
|
||||
|
||||
func errNotJSON(command, _ string) error {
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form", command)
|
||||
}
|
||||
27
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_windows.go
generated
vendored
Normal file
27
vendor/github.com/docker/docker/builder/dockerfile/instructions/errors_windows.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func errNotJSON(command, original string) error {
|
||||
// For Windows users, give a hint if it looks like it might contain
|
||||
// a path which hasn't been escaped such as ["c:\windows\system32\prog.exe", "-param"],
|
||||
// as JSON must be escaped. Unfortunate...
|
||||
//
|
||||
// Specifically looking for quote-driveletter-colon-backslash, there's no
|
||||
// double backslash and a [] pair. No, this is not perfect, but it doesn't
|
||||
// have to be. It's simply a hint to make life a little easier.
|
||||
extra := ""
|
||||
original = filepath.FromSlash(strings.ToLower(strings.Replace(strings.ToLower(original), strings.ToLower(command)+" ", "", -1)))
|
||||
if len(regexp.MustCompile(`"[a-z]:\\.*`).FindStringSubmatch(original)) > 0 &&
|
||||
!strings.Contains(original, `\\`) &&
|
||||
strings.Contains(original, "[") &&
|
||||
strings.Contains(original, "]") {
|
||||
extra = fmt.Sprintf(`. It looks like '%s' includes a file path without an escaped back-slash. JSON requires back-slashes to be escaped such as ["c:\\path\\to\\file.exe", "/parameter"]`, original)
|
||||
}
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form%s", command, extra)
|
||||
}
|
||||
635
vendor/github.com/docker/docker/builder/dockerfile/instructions/parse.go
generated
vendored
Normal file
635
vendor/github.com/docker/docker/builder/dockerfile/instructions/parse.go
generated
vendored
Normal file
@@ -0,0 +1,635 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type parseRequest struct {
|
||||
command string
|
||||
args []string
|
||||
attributes map[string]bool
|
||||
flags *BFlags
|
||||
original string
|
||||
}
|
||||
|
||||
func nodeArgs(node *parser.Node) []string {
|
||||
result := []string{}
|
||||
for ; node.Next != nil; node = node.Next {
|
||||
arg := node.Next
|
||||
if len(arg.Children) == 0 {
|
||||
result = append(result, arg.Value)
|
||||
} else if len(arg.Children) == 1 {
|
||||
//sub command
|
||||
result = append(result, arg.Children[0].Value)
|
||||
result = append(result, nodeArgs(arg.Children[0])...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func newParseRequestFromNode(node *parser.Node) parseRequest {
|
||||
return parseRequest{
|
||||
command: node.Value,
|
||||
args: nodeArgs(node),
|
||||
attributes: node.Attributes,
|
||||
original: node.Original,
|
||||
flags: NewBFlagsWithArgs(node.Flags),
|
||||
}
|
||||
}
|
||||
|
||||
// ParseInstruction converts an AST to a typed instruction (either a command or a build stage beginning when encountering a `FROM` statement)
|
||||
func ParseInstruction(node *parser.Node) (interface{}, error) {
|
||||
req := newParseRequestFromNode(node)
|
||||
switch node.Value {
|
||||
case command.Env:
|
||||
return parseEnv(req)
|
||||
case command.Maintainer:
|
||||
return parseMaintainer(req)
|
||||
case command.Label:
|
||||
return parseLabel(req)
|
||||
case command.Add:
|
||||
return parseAdd(req)
|
||||
case command.Copy:
|
||||
return parseCopy(req)
|
||||
case command.From:
|
||||
return parseFrom(req)
|
||||
case command.Onbuild:
|
||||
return parseOnBuild(req)
|
||||
case command.Workdir:
|
||||
return parseWorkdir(req)
|
||||
case command.Run:
|
||||
return parseRun(req)
|
||||
case command.Cmd:
|
||||
return parseCmd(req)
|
||||
case command.Healthcheck:
|
||||
return parseHealthcheck(req)
|
||||
case command.Entrypoint:
|
||||
return parseEntrypoint(req)
|
||||
case command.Expose:
|
||||
return parseExpose(req)
|
||||
case command.User:
|
||||
return parseUser(req)
|
||||
case command.Volume:
|
||||
return parseVolume(req)
|
||||
case command.StopSignal:
|
||||
return parseStopSignal(req)
|
||||
case command.Arg:
|
||||
return parseArg(req)
|
||||
case command.Shell:
|
||||
return parseShell(req)
|
||||
}
|
||||
|
||||
return nil, &UnknownInstruction{Instruction: node.Value, Line: node.StartLine}
|
||||
}
|
||||
|
||||
// ParseCommand converts an AST to a typed Command
|
||||
func ParseCommand(node *parser.Node) (Command, error) {
|
||||
s, err := ParseInstruction(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c, ok := s.(Command); ok {
|
||||
return c, nil
|
||||
}
|
||||
return nil, errors.Errorf("%T is not a command type", s)
|
||||
}
|
||||
|
||||
// UnknownInstruction represents an error occurring when a command is unresolvable
|
||||
type UnknownInstruction struct {
|
||||
Line int
|
||||
Instruction string
|
||||
}
|
||||
|
||||
func (e *UnknownInstruction) Error() string {
|
||||
return fmt.Sprintf("unknown instruction: %s", strings.ToUpper(e.Instruction))
|
||||
}
|
||||
|
||||
// IsUnknownInstruction checks if the error is an UnknownInstruction or a parseError containing an UnknownInstruction
|
||||
func IsUnknownInstruction(err error) bool {
|
||||
_, ok := err.(*UnknownInstruction)
|
||||
if !ok {
|
||||
var pe *parseError
|
||||
if pe, ok = err.(*parseError); ok {
|
||||
_, ok = pe.inner.(*UnknownInstruction)
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
type parseError struct {
|
||||
inner error
|
||||
node *parser.Node
|
||||
}
|
||||
|
||||
func (e *parseError) Error() string {
|
||||
return fmt.Sprintf("Dockerfile parse error line %d: %v", e.node.StartLine, e.inner.Error())
|
||||
}
|
||||
|
||||
// Parse a docker file into a collection of buildable stages
|
||||
func Parse(ast *parser.Node) (stages []Stage, metaArgs []ArgCommand, err error) {
|
||||
for _, n := range ast.Children {
|
||||
cmd, err := ParseInstruction(n)
|
||||
if err != nil {
|
||||
return nil, nil, &parseError{inner: err, node: n}
|
||||
}
|
||||
if len(stages) == 0 {
|
||||
// meta arg case
|
||||
if a, isArg := cmd.(*ArgCommand); isArg {
|
||||
metaArgs = append(metaArgs, *a)
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch c := cmd.(type) {
|
||||
case *Stage:
|
||||
stages = append(stages, *c)
|
||||
case Command:
|
||||
stage, err := CurrentStage(stages)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
stage.AddCommand(c)
|
||||
default:
|
||||
return nil, nil, errors.Errorf("%T is not a command type", cmd)
|
||||
}
|
||||
|
||||
}
|
||||
return stages, metaArgs, nil
|
||||
}
|
||||
|
||||
func parseKvps(args []string, cmdName string) (KeyValuePairs, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, errAtLeastOneArgument(cmdName)
|
||||
}
|
||||
if len(args)%2 != 0 {
|
||||
// should never get here, but just in case
|
||||
return nil, errTooManyArguments(cmdName)
|
||||
}
|
||||
var res KeyValuePairs
|
||||
for j := 0; j < len(args); j += 2 {
|
||||
if len(args[j]) == 0 {
|
||||
return nil, errBlankCommandNames(cmdName)
|
||||
}
|
||||
name := args[j]
|
||||
value := args[j+1]
|
||||
res = append(res, KeyValuePair{Key: name, Value: value})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parseEnv(req parseRequest) (*EnvCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envs, err := parseKvps(req.args, "ENV")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EnvCommand{
|
||||
Env: envs,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseMaintainer(req parseRequest) (*MaintainerCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("MAINTAINER")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MaintainerCommand{
|
||||
Maintainer: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseLabel(req parseRequest) (*LabelCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := parseKvps(req.args, "LABEL")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LabelCommand{
|
||||
Labels: labels,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseAdd(req parseRequest) (*AddCommand, error) {
|
||||
if len(req.args) < 2 {
|
||||
return nil, errNoDestinationArgument("ADD")
|
||||
}
|
||||
flChown := req.flags.AddString("chown", "")
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AddCommand{
|
||||
SourcesAndDest: SourcesAndDest(req.args),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
Chown: flChown.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseCopy(req parseRequest) (*CopyCommand, error) {
|
||||
if len(req.args) < 2 {
|
||||
return nil, errNoDestinationArgument("COPY")
|
||||
}
|
||||
flChown := req.flags.AddString("chown", "")
|
||||
flFrom := req.flags.AddString("from", "")
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CopyCommand{
|
||||
SourcesAndDest: SourcesAndDest(req.args),
|
||||
From: flFrom.Value,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
Chown: flChown.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseFrom(req parseRequest) (*Stage, error) {
|
||||
stageName, err := parseBuildStageName(req.args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code := strings.TrimSpace(req.original)
|
||||
|
||||
return &Stage{
|
||||
BaseName: req.args[0],
|
||||
Name: stageName,
|
||||
SourceCode: code,
|
||||
Commands: []Command{},
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseBuildStageName(args []string) (string, error) {
|
||||
stageName := ""
|
||||
switch {
|
||||
case len(args) == 3 && strings.EqualFold(args[1], "as"):
|
||||
stageName = strings.ToLower(args[2])
|
||||
if ok, _ := regexp.MatchString("^[a-z][a-z0-9-_\\.]*$", stageName); !ok {
|
||||
return "", errors.Errorf("invalid name for build stage: %q, name can't start with a number or contain symbols", stageName)
|
||||
}
|
||||
case len(args) != 1:
|
||||
return "", errors.New("FROM requires either one or three arguments")
|
||||
}
|
||||
|
||||
return stageName, nil
|
||||
}
|
||||
|
||||
func parseOnBuild(req parseRequest) (*OnbuildCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("ONBUILD")
|
||||
}
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggerInstruction := strings.ToUpper(strings.TrimSpace(req.args[0]))
|
||||
switch strings.ToUpper(triggerInstruction) {
|
||||
case "ONBUILD":
|
||||
return nil, errors.New("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
||||
case "MAINTAINER", "FROM":
|
||||
return nil, fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
|
||||
}
|
||||
|
||||
original := regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(req.original, "")
|
||||
return &OnbuildCommand{
|
||||
Expression: original,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseWorkdir(req parseRequest) (*WorkdirCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("WORKDIR")
|
||||
}
|
||||
|
||||
err := req.flags.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WorkdirCommand{
|
||||
Path: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseShellDependentCommand(req parseRequest, emptyAsNil bool) ShellDependantCmdLine {
|
||||
args := handleJSONArgs(req.args, req.attributes)
|
||||
cmd := strslice.StrSlice(args)
|
||||
if emptyAsNil && len(cmd) == 0 {
|
||||
cmd = nil
|
||||
}
|
||||
return ShellDependantCmdLine{
|
||||
CmdLine: cmd,
|
||||
PrependShell: !req.attributes["json"],
|
||||
}
|
||||
}
|
||||
|
||||
func parseRun(req parseRequest) (*RunCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RunCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, false),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseCmd(req parseRequest) (*CmdCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CmdCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, false),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseEntrypoint(req parseRequest) (*EntrypointCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &EntrypointCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, true),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// parseOptInterval(flag) is the duration of flag.Value, or 0 if
|
||||
// empty. An error is reported if the value is given and less than minimum duration.
|
||||
func parseOptInterval(f *Flag) (time.Duration, error) {
|
||||
s := f.Value
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
d, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if d < container.MinimumDuration {
|
||||
return 0, fmt.Errorf("Interval %#v cannot be less than %s", f.name, container.MinimumDuration)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
func parseHealthcheck(req parseRequest) (*HealthCheckCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("HEALTHCHECK")
|
||||
}
|
||||
cmd := &HealthCheckCommand{
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
typ := strings.ToUpper(req.args[0])
|
||||
args := req.args[1:]
|
||||
if typ == "NONE" {
|
||||
if len(args) != 0 {
|
||||
return nil, errors.New("HEALTHCHECK NONE takes no arguments")
|
||||
}
|
||||
test := strslice.StrSlice{typ}
|
||||
cmd.Health = &container.HealthConfig{
|
||||
Test: test,
|
||||
}
|
||||
} else {
|
||||
|
||||
healthcheck := container.HealthConfig{}
|
||||
|
||||
flInterval := req.flags.AddString("interval", "")
|
||||
flTimeout := req.flags.AddString("timeout", "")
|
||||
flStartPeriod := req.flags.AddString("start-period", "")
|
||||
flRetries := req.flags.AddString("retries", "")
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case "CMD":
|
||||
cmdSlice := handleJSONArgs(args, req.attributes)
|
||||
if len(cmdSlice) == 0 {
|
||||
return nil, errors.New("Missing command after HEALTHCHECK CMD")
|
||||
}
|
||||
|
||||
if !req.attributes["json"] {
|
||||
typ = "CMD-SHELL"
|
||||
}
|
||||
|
||||
healthcheck.Test = strslice.StrSlice(append([]string{typ}, cmdSlice...))
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown type %#v in HEALTHCHECK (try CMD)", typ)
|
||||
}
|
||||
|
||||
interval, err := parseOptInterval(flInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.Interval = interval
|
||||
|
||||
timeout, err := parseOptInterval(flTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.Timeout = timeout
|
||||
|
||||
startPeriod, err := parseOptInterval(flStartPeriod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.StartPeriod = startPeriod
|
||||
|
||||
if flRetries.Value != "" {
|
||||
retries, err := strconv.ParseInt(flRetries.Value, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if retries < 1 {
|
||||
return nil, fmt.Errorf("--retries must be at least 1 (not %d)", retries)
|
||||
}
|
||||
healthcheck.Retries = int(retries)
|
||||
} else {
|
||||
healthcheck.Retries = 0
|
||||
}
|
||||
|
||||
cmd.Health = &healthcheck
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func parseExpose(req parseRequest) (*ExposeCommand, error) {
|
||||
portsTab := req.args
|
||||
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("EXPOSE")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Strings(portsTab)
|
||||
return &ExposeCommand{
|
||||
Ports: portsTab,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseUser(req parseRequest) (*UserCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("USER")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UserCommand{
|
||||
User: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseVolume(req parseRequest) (*VolumeCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("VOLUME")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &VolumeCommand{
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
for _, v := range req.args {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return nil, errors.New("VOLUME specified can not be an empty string")
|
||||
}
|
||||
cmd.Volumes = append(cmd.Volumes, v)
|
||||
}
|
||||
return cmd, nil
|
||||
|
||||
}
|
||||
|
||||
func parseStopSignal(req parseRequest) (*StopSignalCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("STOPSIGNAL")
|
||||
}
|
||||
sig := req.args[0]
|
||||
|
||||
cmd := &StopSignalCommand{
|
||||
Signal: sig,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
return cmd, nil
|
||||
|
||||
}
|
||||
|
||||
func parseArg(req parseRequest) (*ArgCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("ARG")
|
||||
}
|
||||
|
||||
var (
|
||||
name string
|
||||
newValue *string
|
||||
)
|
||||
|
||||
arg := req.args[0]
|
||||
// 'arg' can just be a name or name-value pair. Note that this is different
|
||||
// from 'env' that handles the split of name and value at the parser level.
|
||||
// The reason for doing it differently for 'arg' is that we support just
|
||||
// defining an arg and not assign it a value (while 'env' always expects a
|
||||
// name-value pair). If possible, it will be good to harmonize the two.
|
||||
if strings.Contains(arg, "=") {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
if len(parts[0]) == 0 {
|
||||
return nil, errBlankCommandNames("ARG")
|
||||
}
|
||||
|
||||
name = parts[0]
|
||||
newValue = &parts[1]
|
||||
} else {
|
||||
name = arg
|
||||
}
|
||||
|
||||
return &ArgCommand{
|
||||
Key: name,
|
||||
Value: newValue,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseShell(req parseRequest) (*ShellCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shellSlice := handleJSONArgs(req.args, req.attributes)
|
||||
switch {
|
||||
case len(shellSlice) == 0:
|
||||
// SHELL []
|
||||
return nil, errAtLeastOneArgument("SHELL")
|
||||
case req.attributes["json"]:
|
||||
// SHELL ["powershell", "-command"]
|
||||
|
||||
return &ShellCommand{
|
||||
Shell: strslice.StrSlice(shellSlice),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
default:
|
||||
// SHELL powershell -command - not JSON
|
||||
return nil, errNotJSON("SHELL", req.original)
|
||||
}
|
||||
}
|
||||
|
||||
func errAtLeastOneArgument(command string) error {
|
||||
return errors.Errorf("%s requires at least one argument", command)
|
||||
}
|
||||
|
||||
func errExactlyOneArgument(command string) error {
|
||||
return errors.Errorf("%s requires exactly one argument", command)
|
||||
}
|
||||
|
||||
func errNoDestinationArgument(command string) error {
|
||||
return errors.Errorf("%s requires at least two arguments, but only one was provided. Destination could not be determined.", command)
|
||||
}
|
||||
|
||||
func errBlankCommandNames(command string) error {
|
||||
return errors.Errorf("%s names can not be blank", command)
|
||||
}
|
||||
|
||||
func errTooManyArguments(command string) error {
|
||||
return errors.Errorf("Bad input to %s, too many arguments", command)
|
||||
}
|
||||
200
vendor/github.com/docker/docker/builder/dockerfile/instructions/parse_test.go
generated
vendored
Normal file
200
vendor/github.com/docker/docker/builder/dockerfile/instructions/parse_test.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/builder/dockerfile/command"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/docker/docker/internal/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCommandsExactlyOneArgument(t *testing.T) {
|
||||
commands := []string{
|
||||
"MAINTAINER",
|
||||
"WORKDIR",
|
||||
"USER",
|
||||
"STOPSIGNAL",
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
ast, err := parser.Parse(strings.NewReader(command))
|
||||
require.NoError(t, err)
|
||||
_, err = ParseInstruction(ast.AST.Children[0])
|
||||
assert.EqualError(t, err, errExactlyOneArgument(command).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsAtLeastOneArgument(t *testing.T) {
|
||||
commands := []string{
|
||||
"ENV",
|
||||
"LABEL",
|
||||
"ONBUILD",
|
||||
"HEALTHCHECK",
|
||||
"EXPOSE",
|
||||
"VOLUME",
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
ast, err := parser.Parse(strings.NewReader(command))
|
||||
require.NoError(t, err)
|
||||
_, err = ParseInstruction(ast.AST.Children[0])
|
||||
assert.EqualError(t, err, errAtLeastOneArgument(command).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsNoDestinationArgument(t *testing.T) {
|
||||
commands := []string{
|
||||
"ADD",
|
||||
"COPY",
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
ast, err := parser.Parse(strings.NewReader(command + " arg1"))
|
||||
require.NoError(t, err)
|
||||
_, err = ParseInstruction(ast.AST.Children[0])
|
||||
assert.EqualError(t, err, errNoDestinationArgument(command).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsTooManyArguments(t *testing.T) {
|
||||
commands := []string{
|
||||
"ENV",
|
||||
"LABEL",
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
node := &parser.Node{
|
||||
Original: command + "arg1 arg2 arg3",
|
||||
Value: strings.ToLower(command),
|
||||
Next: &parser.Node{
|
||||
Value: "arg1",
|
||||
Next: &parser.Node{
|
||||
Value: "arg2",
|
||||
Next: &parser.Node{
|
||||
Value: "arg3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := ParseInstruction(node)
|
||||
assert.EqualError(t, err, errTooManyArguments(command).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandsBlankNames(t *testing.T) {
|
||||
commands := []string{
|
||||
"ENV",
|
||||
"LABEL",
|
||||
}
|
||||
|
||||
for _, command := range commands {
|
||||
node := &parser.Node{
|
||||
Original: command + " =arg2",
|
||||
Value: strings.ToLower(command),
|
||||
Next: &parser.Node{
|
||||
Value: "",
|
||||
Next: &parser.Node{
|
||||
Value: "arg2",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := ParseInstruction(node)
|
||||
assert.EqualError(t, err, errBlankCommandNames(command).Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthCheckCmd(t *testing.T) {
|
||||
node := &parser.Node{
|
||||
Value: command.Healthcheck,
|
||||
Next: &parser.Node{
|
||||
Value: "CMD",
|
||||
Next: &parser.Node{
|
||||
Value: "hello",
|
||||
Next: &parser.Node{
|
||||
Value: "world",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cmd, err := ParseInstruction(node)
|
||||
assert.NoError(t, err)
|
||||
hc, ok := cmd.(*HealthCheckCommand)
|
||||
assert.True(t, ok)
|
||||
expected := []string{"CMD-SHELL", "hello world"}
|
||||
assert.Equal(t, expected, hc.Health.Test)
|
||||
}
|
||||
|
||||
func TestParseOptInterval(t *testing.T) {
|
||||
flInterval := &Flag{
|
||||
name: "interval",
|
||||
flagType: stringType,
|
||||
Value: "50ns",
|
||||
}
|
||||
_, err := parseOptInterval(flInterval)
|
||||
testutil.ErrorContains(t, err, "cannot be less than 1ms")
|
||||
|
||||
flInterval.Value = "1ms"
|
||||
_, err = parseOptInterval(flInterval)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestErrorCases(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
dockerfile string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "copyEmptyWhitespace",
|
||||
dockerfile: `COPY
|
||||
quux \
|
||||
bar`,
|
||||
expectedError: "COPY requires at least two arguments",
|
||||
},
|
||||
{
|
||||
name: "ONBUILD forbidden FROM",
|
||||
dockerfile: "ONBUILD FROM scratch",
|
||||
expectedError: "FROM isn't allowed as an ONBUILD trigger",
|
||||
},
|
||||
{
|
||||
name: "ONBUILD forbidden MAINTAINER",
|
||||
dockerfile: "ONBUILD MAINTAINER docker.io",
|
||||
expectedError: "MAINTAINER isn't allowed as an ONBUILD trigger",
|
||||
},
|
||||
{
|
||||
name: "ARG two arguments",
|
||||
dockerfile: "ARG foo bar",
|
||||
expectedError: "ARG requires exactly one argument",
|
||||
},
|
||||
{
|
||||
name: "MAINTAINER unknown flag",
|
||||
dockerfile: "MAINTAINER --boo joe@example.com",
|
||||
expectedError: "Unknown flag: boo",
|
||||
},
|
||||
{
|
||||
name: "Chaining ONBUILD",
|
||||
dockerfile: `ONBUILD ONBUILD RUN touch foobar`,
|
||||
expectedError: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
|
||||
},
|
||||
{
|
||||
name: "Invalid instruction",
|
||||
dockerfile: `foo bar`,
|
||||
expectedError: "unknown instruction: FOO",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
r := strings.NewReader(c.dockerfile)
|
||||
ast, err := parser.Parse(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when parsing Dockerfile: %s", err)
|
||||
}
|
||||
n := ast.AST.Children[0]
|
||||
_, err = ParseInstruction(n)
|
||||
testutil.ErrorContains(t, err, c.expectedError)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package dockerfile
|
||||
package instructions
|
||||
|
||||
import "strings"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dockerfile
|
||||
package instructions
|
||||
|
||||
import "testing"
|
||||
|
||||
313
vendor/github.com/docker/docker/builder/dockerfile/internals.go
generated
vendored
313
vendor/github.com/docker/docker/builder/dockerfile/internals.go
generated
vendored
@@ -7,16 +7,75 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/go-connections/nat"
|
||||
lcUser "github.com/opencontainers/runc/libcontainer/user"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Archiver defines an interface for copying files from one destination to
|
||||
// another using Tar/Untar.
|
||||
type Archiver interface {
|
||||
TarUntar(src, dst string) error
|
||||
UntarPath(src, dst string) error
|
||||
CopyWithTar(src, dst string) error
|
||||
CopyFileWithTar(src, dst string) error
|
||||
IDMappings() *idtools.IDMappings
|
||||
}
|
||||
|
||||
// The builder will use the following interfaces if the container fs implements
|
||||
// these for optimized copies to and from the container.
|
||||
type extractor interface {
|
||||
ExtractArchive(src io.Reader, dst string, opts *archive.TarOptions) error
|
||||
}
|
||||
|
||||
type archiver interface {
|
||||
ArchivePath(src string, opts *archive.TarOptions) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// helper functions to get tar/untar func
|
||||
func untarFunc(i interface{}) containerfs.UntarFunc {
|
||||
if ea, ok := i.(extractor); ok {
|
||||
return ea.ExtractArchive
|
||||
}
|
||||
return chrootarchive.Untar
|
||||
}
|
||||
|
||||
func tarFunc(i interface{}) containerfs.TarFunc {
|
||||
if ap, ok := i.(archiver); ok {
|
||||
return ap.ArchivePath
|
||||
}
|
||||
return archive.TarWithOptions
|
||||
}
|
||||
|
||||
func (b *Builder) getArchiver(src, dst containerfs.Driver) Archiver {
|
||||
t, u := tarFunc(src), untarFunc(dst)
|
||||
return &containerfs.Archiver{
|
||||
SrcDriver: src,
|
||||
DstDriver: dst,
|
||||
Tar: t,
|
||||
Untar: u,
|
||||
IDMappingsVar: b.idMappings,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
|
||||
if b.disableCommit {
|
||||
return nil
|
||||
@@ -25,7 +84,8 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
|
||||
return errors.New("Please provide a source image with `from` prior to commit")
|
||||
}
|
||||
|
||||
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, b.platform))
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, optionsPlatform.OS))
|
||||
hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd)
|
||||
if err != nil || hit {
|
||||
return err
|
||||
@@ -60,12 +120,12 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta
|
||||
}
|
||||
|
||||
dispatchState.imageID = imageID
|
||||
b.buildStages.update(imageID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error {
|
||||
newLayer, err := imageMount.Layer().Commit(b.platform)
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
newLayer, err := imageMount.Layer().Commit(optionsPlatform.OS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -100,17 +160,23 @@ func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runC
|
||||
|
||||
state.imageID = exportedImage.ImageID()
|
||||
b.imageSources.Add(newImageMount(exportedImage, newLayer))
|
||||
b.buildStages.update(state.imageID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error {
|
||||
srcHash := getSourceHashFromInfos(inst.infos)
|
||||
|
||||
var chownComment string
|
||||
if inst.chownStr != "" {
|
||||
chownComment = fmt.Sprintf("--chown=%s", inst.chownStr)
|
||||
}
|
||||
commentStr := fmt.Sprintf("%s %s%s in %s ", inst.cmdName, chownComment, srcHash, inst.dest)
|
||||
|
||||
// TODO: should this have been using origPaths instead of srcHash in the comment?
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
runConfigWithCommentCmd := copyRunConfig(
|
||||
state.runConfig,
|
||||
withCmdCommentString(fmt.Sprintf("%s %s in %s ", inst.cmdName, srcHash, inst.dest), b.platform))
|
||||
withCmdCommentString(commentStr, optionsPlatform.OS))
|
||||
hit, err := b.probeCache(state, runConfigWithCommentCmd)
|
||||
if err != nil || hit {
|
||||
return err
|
||||
@@ -120,16 +186,29 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
|
||||
}
|
||||
destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount)
|
||||
|
||||
destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.options.Platform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := copyFileOptions{
|
||||
decompress: inst.allowLocalDecompression,
|
||||
archiver: b.archiver,
|
||||
chownPair := b.idMappings.RootPair()
|
||||
// if a chown was requested, perform the steps to get the uid, gid
|
||||
// translated (if necessary because of user namespaces), and replace
|
||||
// the root pair with the chown pair for copy operations
|
||||
if inst.chownStr != "" {
|
||||
chownPair, err = parseChownFlag(inst.chownStr, destInfo.root.Path(), b.idMappings)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to convert uid/gid chown string to host mapping")
|
||||
}
|
||||
}
|
||||
|
||||
for _, info := range inst.infos {
|
||||
opts := copyFileOptions{
|
||||
decompress: inst.allowLocalDecompression,
|
||||
archiver: b.getArchiver(info.root, destInfo.root),
|
||||
chownPair: chownPair,
|
||||
}
|
||||
if err := performCopyForInfo(destInfo, info, opts); err != nil {
|
||||
return errors.Wrapf(err, "failed to copy files")
|
||||
}
|
||||
@@ -137,10 +216,86 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
|
||||
return b.exportImage(state, imageMount, runConfigWithCommentCmd)
|
||||
}
|
||||
|
||||
func createDestInfo(workingDir string, inst copyInstruction, imageMount *imageMount) (copyInfo, error) {
|
||||
func parseChownFlag(chown, ctrRootPath string, idMappings *idtools.IDMappings) (idtools.IDPair, error) {
|
||||
var userStr, grpStr string
|
||||
parts := strings.Split(chown, ":")
|
||||
if len(parts) > 2 {
|
||||
return idtools.IDPair{}, errors.New("invalid chown string format: " + chown)
|
||||
}
|
||||
if len(parts) == 1 {
|
||||
// if no group specified, use the user spec as group as well
|
||||
userStr, grpStr = parts[0], parts[0]
|
||||
} else {
|
||||
userStr, grpStr = parts[0], parts[1]
|
||||
}
|
||||
|
||||
passwdPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "passwd"), ctrRootPath)
|
||||
if err != nil {
|
||||
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/passwd path in container rootfs")
|
||||
}
|
||||
groupPath, err := symlink.FollowSymlinkInScope(filepath.Join(ctrRootPath, "etc", "group"), ctrRootPath)
|
||||
if err != nil {
|
||||
return idtools.IDPair{}, errors.Wrapf(err, "can't resolve /etc/group path in container rootfs")
|
||||
}
|
||||
uid, err := lookupUser(userStr, passwdPath)
|
||||
if err != nil {
|
||||
return idtools.IDPair{}, errors.Wrapf(err, "can't find uid for user "+userStr)
|
||||
}
|
||||
gid, err := lookupGroup(grpStr, groupPath)
|
||||
if err != nil {
|
||||
return idtools.IDPair{}, errors.Wrapf(err, "can't find gid for group "+grpStr)
|
||||
}
|
||||
|
||||
// convert as necessary because of user namespaces
|
||||
chownPair, err := idMappings.ToHost(idtools.IDPair{UID: uid, GID: gid})
|
||||
if err != nil {
|
||||
return idtools.IDPair{}, errors.Wrapf(err, "unable to convert uid/gid to host mapping")
|
||||
}
|
||||
return chownPair, nil
|
||||
}
|
||||
|
||||
func lookupUser(userStr, filepath string) (int, error) {
|
||||
// if the string is actually a uid integer, parse to int and return
|
||||
// as we don't need to translate with the help of files
|
||||
uid, err := strconv.Atoi(userStr)
|
||||
if err == nil {
|
||||
return uid, nil
|
||||
}
|
||||
users, err := lcUser.ParsePasswdFileFilter(filepath, func(u lcUser.User) bool {
|
||||
return u.Name == userStr
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(users) == 0 {
|
||||
return 0, errors.New("no such user: " + userStr)
|
||||
}
|
||||
return users[0].Uid, nil
|
||||
}
|
||||
|
||||
func lookupGroup(groupStr, filepath string) (int, error) {
|
||||
// if the string is actually a gid integer, parse to int and return
|
||||
// as we don't need to translate with the help of files
|
||||
gid, err := strconv.Atoi(groupStr)
|
||||
if err == nil {
|
||||
return gid, nil
|
||||
}
|
||||
groups, err := lcUser.ParseGroupFileFilter(filepath, func(g lcUser.Group) bool {
|
||||
return g.Name == groupStr
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(groups) == 0 {
|
||||
return 0, errors.New("no such group: " + groupStr)
|
||||
}
|
||||
return groups[0].Gid, nil
|
||||
}
|
||||
|
||||
func createDestInfo(workingDir string, inst copyInstruction, imageMount *imageMount, platform string) (copyInfo, error) {
|
||||
// Twiddle the destination when it's a relative path - meaning, make it
|
||||
// relative to the WORKINGDIR
|
||||
dest, err := normalizeDest(workingDir, inst.dest)
|
||||
dest, err := normalizeDest(workingDir, inst.dest, platform)
|
||||
if err != nil {
|
||||
return copyInfo{}, errors.Wrapf(err, "invalid %s", inst.cmdName)
|
||||
}
|
||||
@@ -153,6 +308,63 @@ func createDestInfo(workingDir string, inst copyInstruction, imageMount *imageMo
|
||||
return newCopyInfoFromSource(destMount, dest, ""), nil
|
||||
}
|
||||
|
||||
// normalizeDest normalises the destination of a COPY/ADD command in a
|
||||
// platform semantically consistent way.
|
||||
func normalizeDest(workingDir, requested string, platform string) (string, error) {
|
||||
dest := fromSlash(requested, platform)
|
||||
endsInSlash := strings.HasSuffix(dest, string(separator(platform)))
|
||||
|
||||
if platform != "windows" {
|
||||
if !path.IsAbs(requested) {
|
||||
dest = path.Join("/", filepath.ToSlash(workingDir), dest)
|
||||
// Make sure we preserve any trailing slash
|
||||
if endsInSlash {
|
||||
dest += "/"
|
||||
}
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
// We are guaranteed that the working directory is already consistent,
|
||||
// However, Windows also has, for now, the limitation that ADD/COPY can
|
||||
// only be done to the system drive, not any drives that might be present
|
||||
// as a result of a bind mount.
|
||||
//
|
||||
// So... if the path requested is Linux-style absolute (/foo or \\foo),
|
||||
// we assume it is the system drive. If it is a Windows-style absolute
|
||||
// (DRIVE:\\foo), error if DRIVE is not C. And finally, ensure we
|
||||
// strip any configured working directories drive letter so that it
|
||||
// can be subsequently legitimately converted to a Windows volume-style
|
||||
// pathname.
|
||||
|
||||
// Not a typo - filepath.IsAbs, not system.IsAbs on this next check as
|
||||
// we only want to validate where the DriveColon part has been supplied.
|
||||
if filepath.IsAbs(dest) {
|
||||
if strings.ToUpper(string(dest[0])) != "C" {
|
||||
return "", fmt.Errorf("Windows does not support destinations not on the system drive (C:)")
|
||||
}
|
||||
dest = dest[2:] // Strip the drive letter
|
||||
}
|
||||
|
||||
// Cannot handle relative where WorkingDir is not the system drive.
|
||||
if len(workingDir) > 0 {
|
||||
if ((len(workingDir) > 1) && !system.IsAbs(workingDir[2:])) || (len(workingDir) == 1) {
|
||||
return "", fmt.Errorf("Current WorkingDir %s is not platform consistent", workingDir)
|
||||
}
|
||||
if !system.IsAbs(dest) {
|
||||
if string(workingDir[0]) != "C" {
|
||||
return "", fmt.Errorf("Windows does not support relative paths when WORKDIR is not the system drive")
|
||||
}
|
||||
dest = filepath.Join(string(os.PathSeparator), workingDir[2:], dest)
|
||||
// Make sure we preserve any trailing slash
|
||||
if endsInSlash {
|
||||
dest += string(os.PathSeparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
// For backwards compat, if there's just one info then use it as the
|
||||
// cache look-up string, otherwise hash 'em all into one
|
||||
func getSourceHashFromInfos(infos []copyInfo) string {
|
||||
@@ -174,14 +386,6 @@ func hashStringSlice(prefix string, slice []string) string {
|
||||
|
||||
type runConfigModifier func(*container.Config)
|
||||
|
||||
func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config {
|
||||
copy := *runConfig
|
||||
for _, modifier := range modifiers {
|
||||
modifier(©)
|
||||
}
|
||||
return ©
|
||||
}
|
||||
|
||||
func withCmd(cmd []string) runConfigModifier {
|
||||
return func(runConfig *container.Config) {
|
||||
runConfig.Cmd = cmd
|
||||
@@ -227,11 +431,53 @@ func withEntrypointOverride(cmd []string, entrypoint []string) runConfigModifier
|
||||
}
|
||||
}
|
||||
|
||||
func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config {
|
||||
copy := *runConfig
|
||||
copy.Cmd = copyStringSlice(runConfig.Cmd)
|
||||
copy.Env = copyStringSlice(runConfig.Env)
|
||||
copy.Entrypoint = copyStringSlice(runConfig.Entrypoint)
|
||||
copy.OnBuild = copyStringSlice(runConfig.OnBuild)
|
||||
copy.Shell = copyStringSlice(runConfig.Shell)
|
||||
|
||||
if copy.Volumes != nil {
|
||||
copy.Volumes = make(map[string]struct{}, len(runConfig.Volumes))
|
||||
for k, v := range runConfig.Volumes {
|
||||
copy.Volumes[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if copy.ExposedPorts != nil {
|
||||
copy.ExposedPorts = make(nat.PortSet, len(runConfig.ExposedPorts))
|
||||
for k, v := range runConfig.ExposedPorts {
|
||||
copy.ExposedPorts[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if copy.Labels != nil {
|
||||
copy.Labels = make(map[string]string, len(runConfig.Labels))
|
||||
for k, v := range runConfig.Labels {
|
||||
copy.Labels[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, modifier := range modifiers {
|
||||
modifier(©)
|
||||
}
|
||||
return ©
|
||||
}
|
||||
|
||||
func copyStringSlice(orig []string) []string {
|
||||
if orig == nil {
|
||||
return nil
|
||||
}
|
||||
return append([]string{}, orig...)
|
||||
}
|
||||
|
||||
// getShell is a helper function which gets the right shell for prefixing the
|
||||
// shell-form of RUN, ENTRYPOINT and CMD instructions
|
||||
func getShell(c *container.Config, platform string) []string {
|
||||
func getShell(c *container.Config, os string) []string {
|
||||
if 0 == len(c.Shell) {
|
||||
return append([]string{}, defaultShellForPlatform(platform)[:]...)
|
||||
return append([]string{}, defaultShellForOS(os)[:]...)
|
||||
}
|
||||
return append([]string{}, c.Shell[:]...)
|
||||
}
|
||||
@@ -243,8 +489,7 @@ func (b *Builder) probeCache(dispatchState *dispatchState, runConfig *container.
|
||||
}
|
||||
fmt.Fprint(b.Stdout, " ---> Using cache\n")
|
||||
|
||||
dispatchState.imageID = string(cachedID)
|
||||
b.buildStages.update(dispatchState.imageID)
|
||||
dispatchState.imageID = cachedID
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -256,13 +501,15 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai
|
||||
}
|
||||
// Set a log config to override any default value set on the daemon
|
||||
hostConfig := &container.HostConfig{LogConfig: defaultLogConfig}
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, b.platform)
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS)
|
||||
return container.ID, err
|
||||
}
|
||||
|
||||
func (b *Builder) create(runConfig *container.Config) (string, error) {
|
||||
hostConfig := hostConfigFromOptions(b.options)
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, b.platform)
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -298,3 +545,19 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf
|
||||
ExtraHosts: options.ExtraHosts,
|
||||
}
|
||||
}
|
||||
|
||||
// fromSlash works like filepath.FromSlash but with a given OS platform field
|
||||
func fromSlash(path, platform string) string {
|
||||
if platform == "windows" {
|
||||
return strings.Replace(path, "/", "\\", -1)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// separator returns a OS path separator for the given OS platform
|
||||
func separator(platform string) byte {
|
||||
if platform == "windows" {
|
||||
return '\\'
|
||||
}
|
||||
return '/'
|
||||
}
|
||||
|
||||
171
vendor/github.com/docker/docker/builder/dockerfile/internals_test.go
generated
vendored
171
vendor/github.com/docker/docker/builder/dockerfile/internals_test.go
generated
vendored
@@ -2,6 +2,8 @@ package dockerfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
@@ -11,6 +13,8 @@ import (
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/remotecontext"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -100,7 +104,7 @@ func TestCopyRunConfig(t *testing.T) {
|
||||
doc: "Set the command to a comment",
|
||||
modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)},
|
||||
expected: &container.Config{
|
||||
Cmd: append(defaultShellForPlatform(runtime.GOOS), "#(nop) ", "comment"),
|
||||
Cmd: append(defaultShellForOS(runtime.GOOS), "#(nop) ", "comment"),
|
||||
Env: defaultEnv,
|
||||
},
|
||||
},
|
||||
@@ -129,3 +133,168 @@ func TestCopyRunConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func fullMutableRunConfig() *container.Config {
|
||||
return &container.Config{
|
||||
Cmd: []string{"command", "arg1"},
|
||||
Env: []string{"env1=foo", "env2=bar"},
|
||||
ExposedPorts: nat.PortSet{
|
||||
"1000/tcp": {},
|
||||
"1001/tcp": {},
|
||||
},
|
||||
Volumes: map[string]struct{}{
|
||||
"one": {},
|
||||
"two": {},
|
||||
},
|
||||
Entrypoint: []string{"entry", "arg1"},
|
||||
OnBuild: []string{"first", "next"},
|
||||
Labels: map[string]string{
|
||||
"label1": "value1",
|
||||
"label2": "value2",
|
||||
},
|
||||
Shell: []string{"shell", "-c"},
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeepCopyRunConfig(t *testing.T) {
|
||||
runConfig := fullMutableRunConfig()
|
||||
copy := copyRunConfig(runConfig)
|
||||
assert.Equal(t, fullMutableRunConfig(), copy)
|
||||
|
||||
copy.Cmd[1] = "arg2"
|
||||
copy.Env[1] = "env2=new"
|
||||
copy.ExposedPorts["10002"] = struct{}{}
|
||||
copy.Volumes["three"] = struct{}{}
|
||||
copy.Entrypoint[1] = "arg2"
|
||||
copy.OnBuild[0] = "start"
|
||||
copy.Labels["label3"] = "value3"
|
||||
copy.Shell[0] = "sh"
|
||||
assert.Equal(t, fullMutableRunConfig(), runConfig)
|
||||
}
|
||||
|
||||
func TestChownFlagParsing(t *testing.T) {
|
||||
testFiles := map[string]string{
|
||||
"passwd": `root:x:0:0::/bin:/bin/false
|
||||
bin:x:1:1::/bin:/bin/false
|
||||
wwwwww:x:21:33::/bin:/bin/false
|
||||
unicorn:x:1001:1002::/bin:/bin/false
|
||||
`,
|
||||
"group": `root:x:0:
|
||||
bin:x:1:
|
||||
wwwwww:x:33:
|
||||
unicorn:x:1002:
|
||||
somegrp:x:5555:
|
||||
othergrp:x:6666:
|
||||
`,
|
||||
}
|
||||
// test mappings for validating use of maps
|
||||
idMaps := []idtools.IDMap{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: 100000,
|
||||
Size: 65536,
|
||||
},
|
||||
}
|
||||
remapped := idtools.NewIDMappingsFromMaps(idMaps, idMaps)
|
||||
unmapped := &idtools.IDMappings{}
|
||||
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-chown-parse-test")
|
||||
defer cleanup()
|
||||
|
||||
if err := os.Mkdir(filepath.Join(contextDir, "etc"), 0755); err != nil {
|
||||
t.Fatalf("error creating test directory: %v", err)
|
||||
}
|
||||
|
||||
for filename, content := range testFiles {
|
||||
createTestTempFile(t, filepath.Join(contextDir, "etc"), filename, content, 0644)
|
||||
}
|
||||
|
||||
// positive tests
|
||||
for _, testcase := range []struct {
|
||||
name string
|
||||
chownStr string
|
||||
idMapping *idtools.IDMappings
|
||||
expected idtools.IDPair
|
||||
}{
|
||||
{
|
||||
name: "UIDNoMap",
|
||||
chownStr: "1",
|
||||
idMapping: unmapped,
|
||||
expected: idtools.IDPair{UID: 1, GID: 1},
|
||||
},
|
||||
{
|
||||
name: "UIDGIDNoMap",
|
||||
chownStr: "0:1",
|
||||
idMapping: unmapped,
|
||||
expected: idtools.IDPair{UID: 0, GID: 1},
|
||||
},
|
||||
{
|
||||
name: "UIDWithMap",
|
||||
chownStr: "0",
|
||||
idMapping: remapped,
|
||||
expected: idtools.IDPair{UID: 100000, GID: 100000},
|
||||
},
|
||||
{
|
||||
name: "UIDGIDWithMap",
|
||||
chownStr: "1:33",
|
||||
idMapping: remapped,
|
||||
expected: idtools.IDPair{UID: 100001, GID: 100033},
|
||||
},
|
||||
{
|
||||
name: "UserNoMap",
|
||||
chownStr: "bin:5555",
|
||||
idMapping: unmapped,
|
||||
expected: idtools.IDPair{UID: 1, GID: 5555},
|
||||
},
|
||||
{
|
||||
name: "GroupWithMap",
|
||||
chownStr: "0:unicorn",
|
||||
idMapping: remapped,
|
||||
expected: idtools.IDPair{UID: 100000, GID: 101002},
|
||||
},
|
||||
{
|
||||
name: "UserOnlyWithMap",
|
||||
chownStr: "unicorn",
|
||||
idMapping: remapped,
|
||||
expected: idtools.IDPair{UID: 101001, GID: 101002},
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
idPair, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
|
||||
require.NoError(t, err, "Failed to parse chown flag: %q", testcase.chownStr)
|
||||
assert.Equal(t, testcase.expected, idPair, "chown flag mapping failure")
|
||||
})
|
||||
}
|
||||
|
||||
// error tests
|
||||
for _, testcase := range []struct {
|
||||
name string
|
||||
chownStr string
|
||||
idMapping *idtools.IDMappings
|
||||
descr string
|
||||
}{
|
||||
{
|
||||
name: "BadChownFlagFormat",
|
||||
chownStr: "bob:1:555",
|
||||
idMapping: unmapped,
|
||||
descr: "invalid chown string format: bob:1:555",
|
||||
},
|
||||
{
|
||||
name: "UserNoExist",
|
||||
chownStr: "bob",
|
||||
idMapping: unmapped,
|
||||
descr: "can't find uid for user bob: no such user: bob",
|
||||
},
|
||||
{
|
||||
name: "GroupNoExist",
|
||||
chownStr: "root:bob",
|
||||
idMapping: unmapped,
|
||||
descr: "can't find gid for group bob: no such group: bob",
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
_, err := parseChownFlag(testcase.chownStr, contextDir, testcase.idMapping)
|
||||
assert.EqualError(t, err, testcase.descr, "Expected error string doesn't match")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
42
vendor/github.com/docker/docker/builder/dockerfile/internals_unix.go
generated
vendored
42
vendor/github.com/docker/docker/builder/dockerfile/internals_unix.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
// normalizeDest normalizes the destination of a COPY/ADD command in a
|
||||
// platform semantically consistent way.
|
||||
func normalizeDest(workingDir, requested string) (string, error) {
|
||||
dest := filepath.FromSlash(requested)
|
||||
endsInSlash := strings.HasSuffix(requested, string(os.PathSeparator))
|
||||
if !system.IsAbs(requested) {
|
||||
dest = filepath.Join(string(os.PathSeparator), filepath.FromSlash(workingDir), dest)
|
||||
// Make sure we preserve any trailing slash
|
||||
if endsInSlash {
|
||||
dest += string(os.PathSeparator)
|
||||
}
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
func containsWildcards(name string) bool {
|
||||
for i := 0; i < len(name); i++ {
|
||||
ch := name[i]
|
||||
if ch == '\\' {
|
||||
i++
|
||||
} else if ch == '*' || ch == '?' || ch == '[' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validateCopySourcePath(imageSource *imageMount, origPath string) error {
|
||||
return nil
|
||||
}
|
||||
95
vendor/github.com/docker/docker/builder/dockerfile/internals_windows.go
generated
vendored
95
vendor/github.com/docker/docker/builder/dockerfile/internals_windows.go
generated
vendored
@@ -1,95 +0,0 @@
|
||||
package dockerfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// normalizeDest normalizes the destination of a COPY/ADD command in a
|
||||
// platform semantically consistent way.
|
||||
func normalizeDest(workingDir, requested string) (string, error) {
|
||||
dest := filepath.FromSlash(requested)
|
||||
endsInSlash := strings.HasSuffix(dest, string(os.PathSeparator))
|
||||
|
||||
// We are guaranteed that the working directory is already consistent,
|
||||
// However, Windows also has, for now, the limitation that ADD/COPY can
|
||||
// only be done to the system drive, not any drives that might be present
|
||||
// as a result of a bind mount.
|
||||
//
|
||||
// So... if the path requested is Linux-style absolute (/foo or \\foo),
|
||||
// we assume it is the system drive. If it is a Windows-style absolute
|
||||
// (DRIVE:\\foo), error if DRIVE is not C. And finally, ensure we
|
||||
// strip any configured working directories drive letter so that it
|
||||
// can be subsequently legitimately converted to a Windows volume-style
|
||||
// pathname.
|
||||
|
||||
// Not a typo - filepath.IsAbs, not system.IsAbs on this next check as
|
||||
// we only want to validate where the DriveColon part has been supplied.
|
||||
if filepath.IsAbs(dest) {
|
||||
if strings.ToUpper(string(dest[0])) != "C" {
|
||||
return "", fmt.Errorf("Windows does not support destinations not on the system drive (C:)")
|
||||
}
|
||||
dest = dest[2:] // Strip the drive letter
|
||||
}
|
||||
|
||||
// Cannot handle relative where WorkingDir is not the system drive.
|
||||
if len(workingDir) > 0 {
|
||||
if ((len(workingDir) > 1) && !system.IsAbs(workingDir[2:])) || (len(workingDir) == 1) {
|
||||
return "", fmt.Errorf("Current WorkingDir %s is not platform consistent", workingDir)
|
||||
}
|
||||
if !system.IsAbs(dest) {
|
||||
if string(workingDir[0]) != "C" {
|
||||
return "", fmt.Errorf("Windows does not support relative paths when WORKDIR is not the system drive")
|
||||
}
|
||||
dest = filepath.Join(string(os.PathSeparator), workingDir[2:], dest)
|
||||
// Make sure we preserve any trailing slash
|
||||
if endsInSlash {
|
||||
dest += string(os.PathSeparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
func containsWildcards(name string) bool {
|
||||
for i := 0; i < len(name); i++ {
|
||||
ch := name[i]
|
||||
if ch == '*' || ch == '?' || ch == '[' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var pathBlacklist = map[string]bool{
|
||||
"c:\\": true,
|
||||
"c:\\windows": true,
|
||||
}
|
||||
|
||||
func validateCopySourcePath(imageSource *imageMount, origPath string) error {
|
||||
// validate windows paths from other images
|
||||
if imageSource == nil {
|
||||
return nil
|
||||
}
|
||||
origPath = filepath.FromSlash(origPath)
|
||||
p := strings.ToLower(filepath.Clean(origPath))
|
||||
if !filepath.IsAbs(p) {
|
||||
if filepath.VolumeName(p) != "" {
|
||||
if p[len(p)-2:] == ":." { // case where clean returns weird c:. paths
|
||||
p = p[:len(p)-1]
|
||||
}
|
||||
p += "\\"
|
||||
} else {
|
||||
p = filepath.Join("c:\\", p)
|
||||
}
|
||||
}
|
||||
if _, blacklisted := pathBlacklist[p]; blacklisted {
|
||||
return errors.New("copy from c:\\ or c:\\windows is not allowed on windows")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
4
vendor/github.com/docker/docker/builder/dockerfile/internals_windows_test.go
generated
vendored
4
vendor/github.com/docker/docker/builder/dockerfile/internals_windows_test.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/docker/docker/internal/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestNormalizeDest(t *testing.T) {
|
||||
}
|
||||
for _, testcase := range tests {
|
||||
msg := fmt.Sprintf("Input: %s, %s", testcase.current, testcase.requested)
|
||||
actual, err := normalizeDest(testcase.current, testcase.requested)
|
||||
actual, err := normalizeDest(testcase.current, testcase.requested, "windows")
|
||||
if testcase.etext == "" {
|
||||
if !assert.NoError(t, err, msg) {
|
||||
continue
|
||||
|
||||
10
vendor/github.com/docker/docker/builder/dockerfile/mockbackend_test.go
generated
vendored
10
vendor/github.com/docker/docker/builder/dockerfile/mockbackend_test.go
generated
vendored
@@ -3,6 +3,7 @@ package dockerfile
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"github.com/docker/docker/builder"
|
||||
containerpkg "github.com/docker/docker/container"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -95,6 +97,10 @@ func (i *mockImage) RunConfig() *container.Config {
|
||||
return i.config
|
||||
}
|
||||
|
||||
func (i *mockImage) OperatingSystem() string {
|
||||
return runtime.GOOS
|
||||
}
|
||||
|
||||
func (i *mockImage) MarshalJSON() ([]byte, error) {
|
||||
type rawImage mockImage
|
||||
return json.Marshal(rawImage(*i))
|
||||
@@ -117,8 +123,8 @@ func (l *mockLayer) Release() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *mockLayer) Mount() (string, error) {
|
||||
return "mountPath", nil
|
||||
func (l *mockLayer) Mount() (containerfs.ContainerFS, error) {
|
||||
return containerfs.NewLocalContainerFS("mountPath"), nil
|
||||
}
|
||||
|
||||
func (l *mockLayer) Commit(string) (builder.ReleaseableLayer, error) {
|
||||
|
||||
40
vendor/github.com/docker/docker/builder/dockerfile/parser/parser.go
generated
vendored
40
vendor/github.com/docker/docker/builder/dockerfile/parser/parser.go
generated
vendored
@@ -91,9 +91,6 @@ var (
|
||||
// DefaultEscapeToken is the default escape token
|
||||
const DefaultEscapeToken = '\\'
|
||||
|
||||
// defaultPlatformToken is the platform assumed for the build if not explicitly provided
|
||||
var defaultPlatformToken = runtime.GOOS
|
||||
|
||||
// Directive is the structure used during a build run to hold the state of
|
||||
// parsing directives.
|
||||
type Directive struct {
|
||||
@@ -143,7 +140,7 @@ func (d *Directive) possibleParserDirective(line string) error {
|
||||
if len(tecMatch) != 0 {
|
||||
for i, n := range tokenEscapeCommand.SubexpNames() {
|
||||
if n == "escapechar" {
|
||||
if d.escapeSeen == true {
|
||||
if d.escapeSeen {
|
||||
return errors.New("only one escape parser directive can be used")
|
||||
}
|
||||
d.escapeSeen = true
|
||||
@@ -152,14 +149,13 @@ func (d *Directive) possibleParserDirective(line string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @jhowardmsft LCOW Support: Eventually this check can be removed,
|
||||
// but only recognise a platform token if running in LCOW mode.
|
||||
// Only recognise a platform token if LCOW is supported
|
||||
if system.LCOWSupported() {
|
||||
tpcMatch := tokenPlatformCommand.FindStringSubmatch(strings.ToLower(line))
|
||||
if len(tpcMatch) != 0 {
|
||||
for i, n := range tokenPlatformCommand.SubexpNames() {
|
||||
if n == "platform" {
|
||||
if d.platformSeen == true {
|
||||
if d.platformSeen {
|
||||
return errors.New("only one platform parser directive can be used")
|
||||
}
|
||||
d.platformSeen = true
|
||||
@@ -177,7 +173,6 @@ func (d *Directive) possibleParserDirective(line string) error {
|
||||
func NewDefaultDirective() *Directive {
|
||||
directive := Directive{}
|
||||
directive.setEscapeToken(string(DefaultEscapeToken))
|
||||
directive.setPlatformToken(defaultPlatformToken)
|
||||
return &directive
|
||||
}
|
||||
|
||||
@@ -242,8 +237,10 @@ func newNodeFromLine(line string, directive *Directive) (*Node, error) {
|
||||
type Result struct {
|
||||
AST *Node
|
||||
EscapeToken rune
|
||||
Platform string
|
||||
Warnings []string
|
||||
// TODO @jhowardmsft - see https://github.com/moby/moby/issues/34617
|
||||
// This next field will be removed in a future update for LCOW support.
|
||||
OS string
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
// PrintWarnings to the writer
|
||||
@@ -290,6 +287,10 @@ func Parse(rwc io.Reader) (*Result, error) {
|
||||
}
|
||||
currentLine++
|
||||
|
||||
if isComment(scanner.Bytes()) {
|
||||
// original line was a comment (processLine strips comments)
|
||||
continue
|
||||
}
|
||||
if isEmptyContinuationLine(bytesRead) {
|
||||
hasEmptyContinuationLine = true
|
||||
continue
|
||||
@@ -319,8 +320,8 @@ func Parse(rwc io.Reader) (*Result, error) {
|
||||
AST: root,
|
||||
Warnings: warnings,
|
||||
EscapeToken: d.escapeToken,
|
||||
Platform: d.platformToken,
|
||||
}, nil
|
||||
OS: d.platformToken,
|
||||
}, handleScannerError(scanner.Err())
|
||||
}
|
||||
|
||||
func trimComments(src []byte) []byte {
|
||||
@@ -331,8 +332,12 @@ func trimWhitespace(src []byte) []byte {
|
||||
return bytes.TrimLeftFunc(src, unicode.IsSpace)
|
||||
}
|
||||
|
||||
func isComment(line []byte) bool {
|
||||
return tokenComment.Match(trimWhitespace(line))
|
||||
}
|
||||
|
||||
func isEmptyContinuationLine(line []byte) bool {
|
||||
return len(trimComments(trimWhitespace(line))) == 0
|
||||
return len(trimWhitespace(line)) == 0
|
||||
}
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
@@ -353,3 +358,12 @@ func processLine(d *Directive, token []byte, stripLeftWhitespace bool) ([]byte,
|
||||
}
|
||||
return trimComments(token), d.possibleParserDirective(string(token))
|
||||
}
|
||||
|
||||
func handleScannerError(err error) error {
|
||||
switch err {
|
||||
case bufio.ErrTooLong:
|
||||
return errors.Errorf("dockerfile line greater than max allowed size of %d", bufio.MaxScanTokenSize-1)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
20
vendor/github.com/docker/docker/builder/dockerfile/parser/parser_test.go
generated
vendored
20
vendor/github.com/docker/docker/builder/dockerfile/parser/parser_test.go
generated
vendored
@@ -1,12 +1,14 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -141,6 +143,13 @@ RUN something \
|
||||
RUN another \
|
||||
|
||||
thing
|
||||
RUN non-indented \
|
||||
# this is a comment
|
||||
after-comment
|
||||
|
||||
RUN indented \
|
||||
# this is an indented comment
|
||||
comment
|
||||
`)
|
||||
|
||||
result, err := Parse(dockerfile)
|
||||
@@ -152,3 +161,14 @@ RUN another \
|
||||
assert.Contains(t, warnings[1], "RUN another thing")
|
||||
assert.Contains(t, warnings[2], "will become errors in a future release")
|
||||
}
|
||||
|
||||
func TestParseReturnsScannerErrors(t *testing.T) {
|
||||
label := strings.Repeat("a", bufio.MaxScanTokenSize)
|
||||
|
||||
dockerfile := strings.NewReader(fmt.Sprintf(`
|
||||
FROM image
|
||||
LABEL test=%s
|
||||
`, label))
|
||||
_, err := Parse(dockerfile)
|
||||
assert.EqualError(t, err, "dockerfile line greater than max allowed size of 65535")
|
||||
}
|
||||
|
||||
47
vendor/github.com/docker/docker/builder/fscache/fscache.go
generated
vendored
47
vendor/github.com/docker/docker/builder/fscache/fscache.go
generated
vendored
@@ -1,7 +1,10 @@
|
||||
package fscache
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"hash"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -11,8 +14,10 @@ import (
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/remotecontext"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/directory"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/moby/buildkit/session/filesync"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -212,7 +217,6 @@ func syncFrom(ctx context.Context, cs *cachedSourceRef, transport Transport, id
|
||||
}
|
||||
|
||||
type fsCacheStore struct {
|
||||
root string
|
||||
mu sync.Mutex
|
||||
sources map[string]*cachedSource
|
||||
db *bolt.DB
|
||||
@@ -578,6 +582,10 @@ func (dc *detectChanges) MarkSupported(v bool) {
|
||||
dc.supported = v
|
||||
}
|
||||
|
||||
func (dc *detectChanges) ContentHasher() fsutil.ContentHasher {
|
||||
return newTarsumHash
|
||||
}
|
||||
|
||||
type wrappedContext struct {
|
||||
builder.Source
|
||||
closer func() error
|
||||
@@ -607,3 +615,40 @@ func (s sortableCacheSources) Less(i, j int) bool {
|
||||
func (s sortableCacheSources) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func newTarsumHash(stat *fsutil.Stat) (hash.Hash, error) {
|
||||
fi := &fsutil.StatInfo{stat}
|
||||
p := stat.Path
|
||||
if fi.IsDir() {
|
||||
p += string(os.PathSeparator)
|
||||
}
|
||||
h, err := archive.FileInfoHeader(p, fi, stat.Linkname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.Name = p
|
||||
h.Uid = int(stat.Uid)
|
||||
h.Gid = int(stat.Gid)
|
||||
h.Linkname = stat.Linkname
|
||||
if stat.Xattrs != nil {
|
||||
h.Xattrs = make(map[string]string)
|
||||
for k, v := range stat.Xattrs {
|
||||
h.Xattrs[k] = string(v)
|
||||
}
|
||||
}
|
||||
|
||||
tsh := &tarsumHash{h: h, Hash: sha256.New()}
|
||||
tsh.Reset()
|
||||
return tsh, nil
|
||||
}
|
||||
|
||||
// Reset resets the Hash to its initial state.
|
||||
func (tsh *tarsumHash) Reset() {
|
||||
tsh.Hash.Reset()
|
||||
tarsum.WriteV1Header(tsh.h, tsh.Hash)
|
||||
}
|
||||
|
||||
type tarsumHash struct {
|
||||
hash.Hash
|
||||
h *tar.Header
|
||||
}
|
||||
|
||||
16
vendor/github.com/docker/docker/builder/fscache/fscache_test.go
generated
vendored
16
vendor/github.com/docker/docker/builder/fscache/fscache_test.go
generated
vendored
@@ -36,25 +36,25 @@ func TestFSCache(t *testing.T) {
|
||||
src1, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo", "data", "bar"})
|
||||
assert.Nil(t, err)
|
||||
|
||||
dt, err := ioutil.ReadFile(filepath.Join(src1.Root(), "foo"))
|
||||
dt, err := ioutil.ReadFile(filepath.Join(src1.Root().Path(), "foo"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(dt), "data")
|
||||
|
||||
// same id doesn't recalculate anything
|
||||
src2, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo", "data2", "bar"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, src1.Root(), src2.Root())
|
||||
assert.Equal(t, src1.Root().Path(), src2.Root().Path())
|
||||
|
||||
dt, err = ioutil.ReadFile(filepath.Join(src1.Root(), "foo"))
|
||||
dt, err = ioutil.ReadFile(filepath.Join(src1.Root().Path(), "foo"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(dt), "data")
|
||||
assert.Nil(t, src2.Close())
|
||||
|
||||
src3, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo2", "data2", "bar"})
|
||||
assert.Nil(t, err)
|
||||
assert.NotEqual(t, src1.Root(), src3.Root())
|
||||
assert.NotEqual(t, src1.Root().Path(), src3.Root().Path())
|
||||
|
||||
dt, err = ioutil.ReadFile(filepath.Join(src3.Root(), "foo2"))
|
||||
dt, err = ioutil.ReadFile(filepath.Join(src3.Root().Path(), "foo2"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(dt), "data2")
|
||||
|
||||
@@ -71,12 +71,12 @@ func TestFSCache(t *testing.T) {
|
||||
// new upload with the same shared key shoutl overwrite
|
||||
src4, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo3", "data3", "bar"})
|
||||
assert.Nil(t, err)
|
||||
assert.NotEqual(t, src1.Root(), src3.Root())
|
||||
assert.NotEqual(t, src1.Root().Path(), src3.Root().Path())
|
||||
|
||||
dt, err = ioutil.ReadFile(filepath.Join(src3.Root(), "foo3"))
|
||||
dt, err = ioutil.ReadFile(filepath.Join(src3.Root().Path(), "foo3"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, string(dt), "data3")
|
||||
assert.Equal(t, src4.Root(), src3.Root())
|
||||
assert.Equal(t, src4.Root().Path(), src3.Root().Path())
|
||||
assert.Nil(t, src4.Close())
|
||||
|
||||
s, err = fscache.DiskUsage()
|
||||
|
||||
25
vendor/github.com/docker/docker/builder/remotecontext/archive.go
generated
vendored
25
vendor/github.com/docker/docker/builder/remotecontext/archive.go
generated
vendored
@@ -8,19 +8,19 @@ import (
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type archiveContext struct {
|
||||
root string
|
||||
root containerfs.ContainerFS
|
||||
sums tarsum.FileInfoSums
|
||||
}
|
||||
|
||||
func (c *archiveContext) Close() error {
|
||||
return os.RemoveAll(c.root)
|
||||
return c.root.RemoveAll(c.root.Path())
|
||||
}
|
||||
|
||||
func convertPathError(err error, cleanpath string) error {
|
||||
@@ -52,7 +52,8 @@ func FromArchive(tarStream io.Reader) (builder.Source, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tsc := &archiveContext{root: root}
|
||||
// Assume local file system. Since it's coming from a tar file.
|
||||
tsc := &archiveContext{root: containerfs.NewLocalContainerFS(root)}
|
||||
|
||||
// Make sure we clean-up upon error. In the happy case the caller
|
||||
// is expected to manage the clean-up
|
||||
@@ -78,11 +79,10 @@ func FromArchive(tarStream io.Reader) (builder.Source, error) {
|
||||
}
|
||||
|
||||
tsc.sums = sum.GetSums()
|
||||
|
||||
return tsc, nil
|
||||
}
|
||||
|
||||
func (c *archiveContext) Root() string {
|
||||
func (c *archiveContext) Root() containerfs.ContainerFS {
|
||||
return c.root
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ func (c *archiveContext) Remove(path string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.RemoveAll(fullpath)
|
||||
return c.root.RemoveAll(fullpath)
|
||||
}
|
||||
|
||||
func (c *archiveContext) Hash(path string) (string, error) {
|
||||
@@ -100,7 +100,7 @@ func (c *archiveContext) Hash(path string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(c.root, fullpath)
|
||||
rel, err := c.root.Rel(c.root.Path(), fullpath)
|
||||
if err != nil {
|
||||
return "", convertPathError(err, cleanpath)
|
||||
}
|
||||
@@ -115,14 +115,11 @@ func (c *archiveContext) Hash(path string) (string, error) {
|
||||
return path, nil // backwards compat TODO: see if really needed
|
||||
}
|
||||
|
||||
func normalize(path, root string) (cleanPath, fullPath string, err error) {
|
||||
cleanPath = filepath.Clean(string(os.PathSeparator) + path)[1:]
|
||||
fullPath, err = symlink.FollowSymlinkInScope(filepath.Join(root, path), root)
|
||||
func normalize(path string, root containerfs.ContainerFS) (cleanPath, fullPath string, err error) {
|
||||
cleanPath = root.Clean(string(root.Separator()) + path)[1:]
|
||||
fullPath, err = root.ResolveScopedPath(path, true)
|
||||
if err != nil {
|
||||
return "", "", errors.Wrapf(err, "forbidden path outside the build context: %s (%s)", path, cleanPath)
|
||||
}
|
||||
if _, err := os.Lstat(fullPath); err != nil {
|
||||
return "", "", errors.WithStack(convertPathError(err, path))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
44
vendor/github.com/docker/docker/builder/remotecontext/detect.go
generated
vendored
44
vendor/github.com/docker/docker/builder/remotecontext/detect.go
generated
vendored
@@ -5,15 +5,14 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/continuity/driver"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/dockerfile/parser"
|
||||
"github.com/docker/docker/builder/dockerignore"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -98,26 +97,23 @@ func newGitRemote(gitURL string, dockerfilePath string) (builder.Source, *parser
|
||||
}
|
||||
|
||||
func newURLRemote(url string, dockerfilePath string, progressReader func(in io.ReadCloser) io.ReadCloser) (builder.Source, *parser.Result, error) {
|
||||
var dockerfile io.ReadCloser
|
||||
dockerfileFoundErr := errors.New("found-dockerfile")
|
||||
c, err := MakeRemoteContext(url, map[string]func(io.ReadCloser) (io.ReadCloser, error){
|
||||
mimeTypes.TextPlain: func(rc io.ReadCloser) (io.ReadCloser, error) {
|
||||
dockerfile = rc
|
||||
return nil, dockerfileFoundErr
|
||||
},
|
||||
// fallback handler (tar context)
|
||||
"": func(rc io.ReadCloser) (io.ReadCloser, error) {
|
||||
return progressReader(rc), nil
|
||||
},
|
||||
})
|
||||
switch {
|
||||
case err == dockerfileFoundErr:
|
||||
res, err := parser.Parse(dockerfile)
|
||||
return nil, res, err
|
||||
case err != nil:
|
||||
contentType, content, err := downloadRemote(url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return withDockerfileFromContext(c.(modifiableContext), dockerfilePath)
|
||||
defer content.Close()
|
||||
|
||||
switch contentType {
|
||||
case mimeTypes.TextPlain:
|
||||
res, err := parser.Parse(progressReader(content))
|
||||
return nil, res, err
|
||||
default:
|
||||
source, err := FromArchive(progressReader(content))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return withDockerfileFromContext(source.(modifiableContext), dockerfilePath)
|
||||
}
|
||||
}
|
||||
|
||||
func removeDockerfile(c modifiableContext, filesToRemove ...string) error {
|
||||
@@ -157,12 +153,12 @@ func readAndParseDockerfile(name string, rc io.Reader) (*parser.Result, error) {
|
||||
return parser.Parse(br)
|
||||
}
|
||||
|
||||
func openAt(remote builder.Source, path string) (*os.File, error) {
|
||||
func openAt(remote builder.Source, path string) (driver.File, error) {
|
||||
fullPath, err := FullPath(remote, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Open(fullPath)
|
||||
return remote.Root().Open(fullPath)
|
||||
}
|
||||
|
||||
// StatAt is a helper for calling Stat on a path from a source
|
||||
@@ -171,12 +167,12 @@ func StatAt(remote builder.Source, path string) (os.FileInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Stat(fullPath)
|
||||
return remote.Root().Stat(fullPath)
|
||||
}
|
||||
|
||||
// FullPath is a helper for getting a full path for a path from a source
|
||||
func FullPath(remote builder.Source, path string) (string, error) {
|
||||
fullPath, err := symlink.FollowSymlinkInScope(filepath.Join(remote.Root(), path), remote.Root())
|
||||
fullPath, err := remote.Root().ResolveScopedPath(path, true)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Forbidden path outside the build context: %s (%s)", path, fullPath) // backwards compat with old error
|
||||
}
|
||||
|
||||
12
vendor/github.com/docker/docker/builder/remotecontext/detect_test.go
generated
vendored
12
vendor/github.com/docker/docker/builder/remotecontext/detect_test.go
generated
vendored
@@ -5,11 +5,11 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,7 +21,7 @@ const (
|
||||
const shouldStayFilename = "should_stay"
|
||||
|
||||
func extractFilenames(files []os.FileInfo) []string {
|
||||
filenames := make([]string, len(files), len(files))
|
||||
filenames := make([]string, len(files))
|
||||
|
||||
for i, file := range files {
|
||||
filenames[i] = file.Name()
|
||||
@@ -53,7 +53,7 @@ func checkDirectory(t *testing.T, dir string, expectedFiles []string) {
|
||||
}
|
||||
|
||||
func executeProcess(t *testing.T, contextDir string) {
|
||||
modifiableCtx := &stubRemote{root: contextDir}
|
||||
modifiableCtx := &stubRemote{root: containerfs.NewLocalContainerFS(contextDir)}
|
||||
|
||||
err := removeDockerfile(modifiableCtx, builder.DefaultDockerfileName)
|
||||
|
||||
@@ -105,19 +105,19 @@ func TestProcessShouldLeaveAllFiles(t *testing.T) {
|
||||
|
||||
// TODO: remove after moving to a separate pkg
|
||||
type stubRemote struct {
|
||||
root string
|
||||
root containerfs.ContainerFS
|
||||
}
|
||||
|
||||
func (r *stubRemote) Hash(path string) (string, error) {
|
||||
return "", errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (r *stubRemote) Root() string {
|
||||
func (r *stubRemote) Root() containerfs.ContainerFS {
|
||||
return r.root
|
||||
}
|
||||
func (r *stubRemote) Close() error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
func (r *stubRemote) Remove(p string) error {
|
||||
return os.Remove(filepath.Join(r.root, p))
|
||||
return r.root.Remove(r.root.Join(r.root.Path(), p))
|
||||
}
|
||||
|
||||
12
vendor/github.com/docker/docker/builder/remotecontext/git.go
generated
vendored
12
vendor/github.com/docker/docker/builder/remotecontext/git.go
generated
vendored
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/remotecontext/git"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// MakeGitContext returns a Context from gitURL that is cloned in a temporary directory.
|
||||
@@ -21,9 +22,14 @@ func MakeGitContext(gitURL string) (builder.Source, error) {
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// TODO: print errors?
|
||||
c.Close()
|
||||
os.RemoveAll(root)
|
||||
err := c.Close()
|
||||
if err != nil {
|
||||
logrus.WithField("action", "MakeGitContext").WithField("module", "builder").WithField("url", gitURL).WithError(err).Error("error while closing git context")
|
||||
}
|
||||
err = os.RemoveAll(root)
|
||||
if err != nil {
|
||||
logrus.WithField("action", "MakeGitContext").WithField("module", "builder").WithField("url", gitURL).WithError(err).Error("error while removing path and children of root")
|
||||
}
|
||||
}()
|
||||
return FromArchive(c)
|
||||
}
|
||||
|
||||
33
vendor/github.com/docker/docker/builder/remotecontext/lazycontext.go
generated
vendored
33
vendor/github.com/docker/docker/builder/remotecontext/lazycontext.go
generated
vendored
@@ -3,11 +3,10 @@ package remotecontext
|
||||
import (
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -15,7 +14,7 @@ import (
|
||||
// NewLazySource creates a new LazyContext. LazyContext defines a hashed build
|
||||
// context based on a root directory. Individual files are hashed first time
|
||||
// they are asked. It is not safe to call methods of LazyContext concurrently.
|
||||
func NewLazySource(root string) (builder.Source, error) {
|
||||
func NewLazySource(root containerfs.ContainerFS) (builder.Source, error) {
|
||||
return &lazySource{
|
||||
root: root,
|
||||
sums: make(map[string]string),
|
||||
@@ -23,11 +22,11 @@ func NewLazySource(root string) (builder.Source, error) {
|
||||
}
|
||||
|
||||
type lazySource struct {
|
||||
root string
|
||||
root containerfs.ContainerFS
|
||||
sums map[string]string
|
||||
}
|
||||
|
||||
func (c *lazySource) Root() string {
|
||||
func (c *lazySource) Root() containerfs.ContainerFS {
|
||||
return c.root
|
||||
}
|
||||
|
||||
@@ -41,16 +40,18 @@ func (c *lazySource) Hash(path string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(fullPath)
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
relPath, err := Rel(c.root, fullPath)
|
||||
if err != nil {
|
||||
return "", errors.WithStack(convertPathError(err, cleanPath))
|
||||
}
|
||||
|
||||
fi, err := os.Lstat(fullPath)
|
||||
if err != nil {
|
||||
// Backwards compatibility: a missing file returns a path as hash.
|
||||
// This is reached in the case of a broken symlink.
|
||||
return relPath, nil
|
||||
}
|
||||
|
||||
sum, ok := c.sums[relPath]
|
||||
if !ok {
|
||||
sum, err = c.prepareHash(relPath, fi)
|
||||
@@ -63,13 +64,13 @@ func (c *lazySource) Hash(path string) (string, error) {
|
||||
}
|
||||
|
||||
func (c *lazySource) prepareHash(relPath string, fi os.FileInfo) (string, error) {
|
||||
p := filepath.Join(c.root, relPath)
|
||||
p := c.root.Join(c.root.Path(), relPath)
|
||||
h, err := NewFileHash(p, relPath, fi)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to create hash for %s", relPath)
|
||||
}
|
||||
if fi.Mode().IsRegular() && fi.Size() > 0 {
|
||||
f, err := os.Open(p)
|
||||
f, err := c.root.Open(p)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to open %s", relPath)
|
||||
}
|
||||
@@ -85,10 +86,10 @@ func (c *lazySource) prepareHash(relPath string, fi os.FileInfo) (string, error)
|
||||
|
||||
// Rel makes a path relative to base path. Same as `filepath.Rel` but can also
|
||||
// handle UUID paths in windows.
|
||||
func Rel(basepath, targpath string) (string, error) {
|
||||
func Rel(basepath containerfs.ContainerFS, targpath string) (string, error) {
|
||||
// filepath.Rel can't handle UUID paths in windows
|
||||
if runtime.GOOS == "windows" {
|
||||
pfx := basepath + `\`
|
||||
if basepath.OS() == "windows" {
|
||||
pfx := basepath.Path() + `\`
|
||||
if strings.HasPrefix(targpath, pfx) {
|
||||
p := strings.TrimPrefix(targpath, pfx)
|
||||
if p == "" {
|
||||
@@ -97,5 +98,5 @@ func Rel(basepath, targpath string) (string, error) {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return filepath.Rel(basepath, targpath)
|
||||
return basepath.Rel(basepath.Path(), targpath)
|
||||
}
|
||||
|
||||
63
vendor/github.com/docker/docker/builder/remotecontext/remote.go
generated
vendored
63
vendor/github.com/docker/docker/builder/remotecontext/remote.go
generated
vendored
@@ -10,7 +10,7 @@ import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -22,50 +22,23 @@ const acceptableRemoteMIME = `(?:application/(?:(?:x\-)?tar|octet\-stream|((?:x\
|
||||
|
||||
var mimeRe = regexp.MustCompile(acceptableRemoteMIME)
|
||||
|
||||
// MakeRemoteContext downloads a context from remoteURL and returns it.
|
||||
//
|
||||
// If contentTypeHandlers is non-nil, then the Content-Type header is read along with a maximum of
|
||||
// maxPreambleLength bytes from the body to help detecting the MIME type.
|
||||
// Look at acceptableRemoteMIME for more details.
|
||||
//
|
||||
// If a match is found, then the body is sent to the contentType handler and a (potentially compressed) tar stream is expected
|
||||
// to be returned. If no match is found, it is assumed the body is a tar stream (compressed or not).
|
||||
// In either case, an (assumed) tar stream is passed to FromArchive whose result is returned.
|
||||
func MakeRemoteContext(remoteURL string, contentTypeHandlers map[string]func(io.ReadCloser) (io.ReadCloser, error)) (builder.Source, error) {
|
||||
f, err := GetWithStatusError(remoteURL)
|
||||
// downloadRemote context from a url and returns it, along with the parsed content type
|
||||
func downloadRemote(remoteURL string) (string, io.ReadCloser, error) {
|
||||
response, err := GetWithStatusError(remoteURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error downloading remote context %s: %v", remoteURL, err)
|
||||
}
|
||||
defer f.Body.Close()
|
||||
|
||||
var contextReader io.ReadCloser
|
||||
if contentTypeHandlers != nil {
|
||||
contentType := f.Header.Get("Content-Type")
|
||||
clen := f.ContentLength
|
||||
|
||||
contentType, contextReader, err = inspectResponse(contentType, f.Body, clen)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error detecting content type for remote %s: %v", remoteURL, err)
|
||||
}
|
||||
defer contextReader.Close()
|
||||
|
||||
// This loop tries to find a content-type handler for the detected content-type.
|
||||
// If it could not find one from the caller-supplied map, it tries the empty content-type `""`
|
||||
// which is interpreted as a fallback handler (usually used for raw tar contexts).
|
||||
for _, ct := range []string{contentType, ""} {
|
||||
if fn, ok := contentTypeHandlers[ct]; ok {
|
||||
defer contextReader.Close()
|
||||
if contextReader, err = fn(contextReader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return "", nil, fmt.Errorf("error downloading remote context %s: %v", remoteURL, err)
|
||||
}
|
||||
|
||||
// Pass through - this is a pre-packaged context, presumably
|
||||
// with a Dockerfile with the right name inside it.
|
||||
return FromArchive(contextReader)
|
||||
contentType, contextReader, err := inspectResponse(
|
||||
response.Header.Get("Content-Type"),
|
||||
response.Body,
|
||||
response.ContentLength)
|
||||
if err != nil {
|
||||
response.Body.Close()
|
||||
return "", nil, fmt.Errorf("error detecting content type for remote %s: %v", remoteURL, err)
|
||||
}
|
||||
|
||||
return contentType, ioutils.NewReadCloserWrapper(contextReader, response.Body.Close), nil
|
||||
}
|
||||
|
||||
// GetWithStatusError does an http.Get() and returns an error if the
|
||||
@@ -110,13 +83,13 @@ func GetWithStatusError(address string) (resp *http.Response, err error) {
|
||||
// - an io.Reader for the response body
|
||||
// - an error value which will be non-nil either when something goes wrong while
|
||||
// reading bytes from r or when the detected content-type is not acceptable.
|
||||
func inspectResponse(ct string, r io.ReadCloser, clen int64) (string, io.ReadCloser, error) {
|
||||
func inspectResponse(ct string, r io.Reader, clen int64) (string, io.Reader, error) {
|
||||
plen := clen
|
||||
if plen <= 0 || plen > maxPreambleLength {
|
||||
plen = maxPreambleLength
|
||||
}
|
||||
|
||||
preamble := make([]byte, plen, plen)
|
||||
preamble := make([]byte, plen)
|
||||
rlen, err := r.Read(preamble)
|
||||
if rlen == 0 {
|
||||
return ct, r, errors.New("empty response")
|
||||
@@ -126,7 +99,7 @@ func inspectResponse(ct string, r io.ReadCloser, clen int64) (string, io.ReadClo
|
||||
}
|
||||
|
||||
preambleR := bytes.NewReader(preamble[:rlen])
|
||||
bodyReader := ioutil.NopCloser(io.MultiReader(preambleR, r))
|
||||
bodyReader := io.MultiReader(preambleR, r)
|
||||
// Some web servers will use application/octet-stream as the default
|
||||
// content type for files without an extension (e.g. 'Dockerfile')
|
||||
// so if we receive this value we better check for text content
|
||||
|
||||
51
vendor/github.com/docker/docker/builder/remotecontext/remote_test.go
generated
vendored
51
vendor/github.com/docker/docker/builder/remotecontext/remote_test.go
generated
vendored
@@ -10,8 +10,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/testutil"
|
||||
"github.com/docker/docker/internal/testutil"
|
||||
"github.com/gotestyourself/gotestyourself/fs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -174,11 +174,10 @@ func TestUnknownContentLength(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeRemoteContext(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-tarsum-test")
|
||||
defer cleanup()
|
||||
|
||||
createTestTempFile(t, contextDir, builder.DefaultDockerfileName, dockerfileContents, 0777)
|
||||
func TestDownloadRemote(t *testing.T) {
|
||||
contextDir := fs.NewDir(t, "test-builder-download-remote",
|
||||
fs.WithFile(builder.DefaultDockerfileName, dockerfileContents))
|
||||
defer contextDir.Remove()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
@@ -187,39 +186,15 @@ func TestMakeRemoteContext(t *testing.T) {
|
||||
serverURL.Path = "/" + builder.DefaultDockerfileName
|
||||
remoteURL := serverURL.String()
|
||||
|
||||
mux.Handle("/", http.FileServer(http.Dir(contextDir)))
|
||||
mux.Handle("/", http.FileServer(http.Dir(contextDir.Path())))
|
||||
|
||||
remoteContext, err := MakeRemoteContext(remoteURL, map[string]func(io.ReadCloser) (io.ReadCloser, error){
|
||||
mimeTypes.TextPlain: func(rc io.ReadCloser) (io.ReadCloser, error) {
|
||||
dockerfile, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentType, content, err := downloadRemote(remoteURL)
|
||||
require.NoError(t, err)
|
||||
|
||||
r, err := archive.Generate(builder.DefaultDockerfileName, string(dockerfile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.NopCloser(r), nil
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when executing DetectContextFromRemoteURL: %s", err)
|
||||
}
|
||||
|
||||
if remoteContext == nil {
|
||||
t.Fatal("Remote context should not be nil")
|
||||
}
|
||||
|
||||
h, err := remoteContext.Hash(builder.DefaultDockerfileName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to compute hash %s", err)
|
||||
}
|
||||
|
||||
if expected, actual := "7b6b6b66bee9e2102fbdc2228be6c980a2a23adf371962a37286a49f7de0f7cc", h; expected != actual {
|
||||
t.Fatalf("There should be file named %s %s in fileInfoSums", expected, actual)
|
||||
}
|
||||
assert.Equal(t, mimeTypes.TextPlain, contentType)
|
||||
raw, err := ioutil.ReadAll(content)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dockerfileContents, string(raw))
|
||||
}
|
||||
|
||||
func TestGetWithStatusError(t *testing.T) {
|
||||
|
||||
37
vendor/github.com/docker/docker/builder/remotecontext/tarsum.go
generated
vendored
37
vendor/github.com/docker/docker/builder/remotecontext/tarsum.go
generated
vendored
@@ -1,25 +1,24 @@
|
||||
package remotecontext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
iradix "github.com/hashicorp/go-immutable-radix"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/fsutil"
|
||||
)
|
||||
|
||||
type hashed interface {
|
||||
Hash() string
|
||||
Digest() digest.Digest
|
||||
}
|
||||
|
||||
// CachableSource is a source that contains cache records for its contents
|
||||
type CachableSource struct {
|
||||
mu sync.Mutex
|
||||
root string
|
||||
root containerfs.ContainerFS
|
||||
tree *iradix.Tree
|
||||
txn *iradix.Txn
|
||||
}
|
||||
@@ -28,7 +27,7 @@ type CachableSource struct {
|
||||
func NewCachableSource(root string) *CachableSource {
|
||||
ts := &CachableSource{
|
||||
tree: iradix.New(),
|
||||
root: root,
|
||||
root: containerfs.NewLocalContainerFS(root),
|
||||
}
|
||||
return ts
|
||||
}
|
||||
@@ -67,7 +66,7 @@ func (cs *CachableSource) Scan() error {
|
||||
return err
|
||||
}
|
||||
txn := iradix.New().Txn()
|
||||
err = filepath.Walk(cs.root, func(path string, info os.FileInfo, err error) error {
|
||||
err = cs.root.Walk(cs.root.Path(), func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to walk %s", path)
|
||||
}
|
||||
@@ -110,7 +109,7 @@ func (cs *CachableSource) HandleChange(kind fsutil.ChangeKind, p string, fi os.F
|
||||
}
|
||||
|
||||
hfi := &fileInfo{
|
||||
sum: h.Hash(),
|
||||
sum: h.Digest().Hex(),
|
||||
}
|
||||
cs.txn.Insert([]byte(p), hfi)
|
||||
cs.mu.Unlock()
|
||||
@@ -133,35 +132,19 @@ func (cs *CachableSource) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *CachableSource) normalize(path string) (cleanpath, fullpath string, err error) {
|
||||
cleanpath = filepath.Clean(string(os.PathSeparator) + path)[1:]
|
||||
fullpath, err = symlink.FollowSymlinkInScope(filepath.Join(cs.root, path), cs.root)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("Forbidden path outside the context: %s (%s)", path, fullpath)
|
||||
}
|
||||
_, err = os.Lstat(fullpath)
|
||||
if err != nil {
|
||||
return "", "", convertPathError(err, path)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Hash returns a hash for a single file in the source
|
||||
func (cs *CachableSource) Hash(path string) (string, error) {
|
||||
n := cs.getRoot()
|
||||
sum := ""
|
||||
// TODO: check this for symlinks
|
||||
v, ok := n.Get([]byte(path))
|
||||
if !ok {
|
||||
sum = path
|
||||
} else {
|
||||
sum = v.(*fileInfo).sum
|
||||
return path, nil
|
||||
}
|
||||
return sum, nil
|
||||
return v.(*fileInfo).sum, nil
|
||||
}
|
||||
|
||||
// Root returns a root directory for the source
|
||||
func (cs *CachableSource) Root() string {
|
||||
func (cs *CachableSource) Root() containerfs.ContainerFS {
|
||||
return cs.root
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user