mirror of
https://github.com/moby/moby.git
synced 2026-01-13 03:31:39 +00:00
Compare commits
1 Commits
docs
...
v1.10.0-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
677c593073 |
51
.github/ISSUE_TEMPLATE.md
vendored
51
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,51 +0,0 @@
|
||||
<!--
|
||||
If you are reporting a new issue, make sure that we do not have any duplicates
|
||||
already open. You can ensure this by searching the issue list for this
|
||||
repository. If there is a duplicate, please close your issue and add a comment
|
||||
to the existing issue instead.
|
||||
|
||||
If you suspect your issue is a bug, please edit your issue description to
|
||||
include the BUG REPORT INFORMATION shown below. If you fail to provide this
|
||||
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
|
||||
|
||||
---------------------------------------------------
|
||||
BUG REPORT INFORMATION
|
||||
---------------------------------------------------
|
||||
Use the commands below to provide key information from your environment:
|
||||
You do NOT have to include this information if this is a FEATURE REQUEST
|
||||
-->
|
||||
|
||||
**Output of `docker version`:**
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
|
||||
|
||||
**Output of `docker info`:**
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
|
||||
**Additional environment details (AWS, VirtualBox, physical, etc.):**
|
||||
|
||||
|
||||
|
||||
**Steps to reproduce the issue:**
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
|
||||
**Describe the results you received:**
|
||||
|
||||
|
||||
**Describe the results you expected:**
|
||||
|
||||
|
||||
**Additional information you deem important (e.g. issue happens only occasionally):**
|
||||
23
.github/PULL_REQUEST_TEMPLATE.md
vendored
23
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,23 +0,0 @@
|
||||
<!--
|
||||
Please make sure you've read and understood our contributing guidelines;
|
||||
https://github.com/docker/docker/blob/master/CONTRIBUTING.md
|
||||
|
||||
** Make sure all your commits include a signature generated with `git commit -s` **
|
||||
|
||||
For additional information on our contributing process, read our contributing
|
||||
guide https://docs.docker.com/opensource/code/
|
||||
|
||||
If this is a bug fix, make sure your description includes "fixes #xxxx", or
|
||||
"closes #xxxx"
|
||||
|
||||
Please provide the following information:
|
||||
-->
|
||||
|
||||
**- What I did**
|
||||
|
||||
**- How I did it**
|
||||
|
||||
**- How to verify it**
|
||||
|
||||
**- A picture of a cute animal (not mandatory but encouraged)**
|
||||
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -7,8 +7,18 @@
|
||||
*.test
|
||||
.*.swp
|
||||
.DS_Store
|
||||
.bashrc
|
||||
.dotcloud
|
||||
.flymake*
|
||||
.git/
|
||||
.gopath/
|
||||
.hg/
|
||||
.vagrant*
|
||||
Vagrantfile
|
||||
a.out
|
||||
autogen/
|
||||
bin
|
||||
build_src
|
||||
bundles/
|
||||
docker/docker
|
||||
dockerversion/version_autogen.go
|
||||
@@ -24,4 +34,5 @@ docs/changed-files
|
||||
man/man1
|
||||
man/man5
|
||||
man/man8
|
||||
pyenv
|
||||
vendor/pkg/
|
||||
|
||||
82
.mailmap
82
.mailmap
@@ -93,8 +93,7 @@ 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>
|
||||
<alexl@redhat.com> <alexander.larsson@gmail.com>
|
||||
Alexander Morozov <lk4d4@docker.com> <lk4d4math@gmail.com>
|
||||
Alexander Morozov <lk4d4@docker.com>
|
||||
Alexandr Morozov <lk4d4math@gmail.com>
|
||||
<git.nivoc@neverbox.com> <kuehnle@online.de>
|
||||
O.S. Tezer <ostezer@gmail.com>
|
||||
<ostezer@gmail.com> <ostezer@users.noreply.github.com>
|
||||
@@ -107,9 +106,7 @@ Roberto G. Hashioka <roberto.hashioka@docker.com> <roberto_hashioka@hotmail.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com>
|
||||
Sridhar Ratnakumar <sridharr@activestate.com> <github@srid.name>
|
||||
Liang-Chi Hsieh <viirya@gmail.com>
|
||||
Aleksa Sarai <asarai@suse.de>
|
||||
Aleksa Sarai <asarai@suse.de> <asarai@suse.com>
|
||||
Aleksa Sarai <asarai@suse.de> <cyphar@cyphar.com>
|
||||
Aleksa Sarai <cyphar@cyphar.com>
|
||||
Will Weaver <monkey@buildingbananas.com>
|
||||
Timothy Hobbs <timothyhobbs@seznam.cz>
|
||||
Nathan LeClaire <nathan.leclaire@docker.com> <nathan.leclaire@gmail.com>
|
||||
@@ -120,27 +117,24 @@ Nathan LeClaire <nathan.leclaire@docker.com> <nathanleclaire@gmail.com>
|
||||
<marc@marc-abramowitz.com> <msabramo@gmail.com>
|
||||
Matthew Heon <mheon@redhat.com> <mheon@mheonlaptop.redhat.com>
|
||||
<bernat@luffy.cx> <vincent@bernat.im>
|
||||
<bernat@luffy.cx> <Vincent.Bernat@exoscale.ch>
|
||||
<p@pwaller.net> <peter@scraperwiki.com>
|
||||
<andrew.weiss@outlook.com> <andrew.weiss@microsoft.com>
|
||||
Francisco Carriedo <fcarriedo@gmail.com>
|
||||
<julienbordellier@gmail.com> <git@julienbordellier.com>
|
||||
<ahmetb@microsoft.com> <ahmetalpbalkan@gmail.com>
|
||||
<lk4d4@docker.com> <lk4d4math@gmail.com>
|
||||
<arnaud.porterie@docker.com> <icecrime@gmail.com>
|
||||
<baloo@gandi.net> <superbaloo+registrations.github@superbaloo.net>
|
||||
Brian Goff <cpuguy83@gmail.com>
|
||||
<cpuguy83@gmail.com> <bgoff@cpuguy83-mbp.home>
|
||||
<eric@windisch.us> <ewindisch@docker.com>
|
||||
<ewindisch@docker.com> <eric@windisch.us>
|
||||
<frank.rosquin+github@gmail.com> <frank.rosquin@gmail.com>
|
||||
Hollie Teal <hollie@docker.com>
|
||||
<hollie@docker.com> <hollie.teal@docker.com>
|
||||
<hollie@docker.com> <hollietealok@users.noreply.github.com>
|
||||
<huu@prismskylabs.com> <whoshuu@gmail.com>
|
||||
Jessica Frazelle <jess@mesosphere.com>
|
||||
Jessica Frazelle <jess@mesosphere.com> <jfrazelle@users.noreply.github.com>
|
||||
Jessica Frazelle <jess@mesosphere.com> <acidburn@docker.com>
|
||||
Jessica Frazelle <jess@mesosphere.com> <jess@docker.com>
|
||||
Jessica Frazelle <jess@mesosphere.com> <princess@docker.com>
|
||||
Jessica Frazelle <jess@docker.com> Jessie Frazelle <jfrazelle@users.noreply.github.com>
|
||||
<jess@docker.com> <jfrazelle@users.noreply.github.com>
|
||||
<konrad.wilhelm.kleine@gmail.com> <kwk@users.noreply.github.com>
|
||||
<tintypemolly@gmail.com> <tintypemolly@Ohui-MacBook-Pro.local>
|
||||
<estesp@linux.vnet.ibm.com> <estesp@gmail.com>
|
||||
@@ -148,8 +142,6 @@ Jessica Frazelle <jess@mesosphere.com> <princess@docker.com>
|
||||
Thomas LEVEIL <thomasleveil@gmail.com> Thomas LÉVEIL <thomasleveil@users.noreply.github.com>
|
||||
<oi@truffles.me.uk> <timruffles@googlemail.com>
|
||||
<Vincent.Bernat@exoscale.ch> <bernat@luffy.cx>
|
||||
Antonio Murdaca <antonio.murdaca@gmail.com> <amurdaca@redhat.com>
|
||||
Antonio Murdaca <antonio.murdaca@gmail.com> <runcom@redhat.com>
|
||||
Antonio Murdaca <antonio.murdaca@gmail.com> <me@runcom.ninja>
|
||||
Antonio Murdaca <antonio.murdaca@gmail.com> <runcom@linux.com>
|
||||
Antonio Murdaca <antonio.murdaca@gmail.com> <runcom@users.noreply.github.com>
|
||||
@@ -159,9 +151,8 @@ Deshi Xiao <dxiao@redhat.com> <xiaods@gmail.com>
|
||||
Doug Davis <dug@us.ibm.com> <duglin@users.noreply.github.com>
|
||||
Jacob Atzen <jacob@jacobatzen.dk> <jatzen@gmail.com>
|
||||
Jeff Nickoloff <jeff.nickoloff@gmail.com> <jeff@allingeek.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <john.howard@microsoft.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> <jhoward@microsoft.com>
|
||||
<jess@docker.com> <princess@docker.com>
|
||||
John Howard (VM) <John.Howard@microsoft.com> John Howard <jhoward@microsoft.com>
|
||||
Madhu Venugopal <madhu@socketplane.io> <madhu@docker.com>
|
||||
Mary Anthony <mary.anthony@docker.com> <mary@docker.com>
|
||||
Mary Anthony <mary.anthony@docker.com> moxiegirl <mary@docker.com>
|
||||
@@ -178,60 +169,3 @@ 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>
|
||||
Adrien Gallouët <adrien@gallouet.fr> <angt@users.noreply.github.com>
|
||||
<aanm90@gmail.com> <martins@noironetworks.com>
|
||||
Anuj Bahuguna <anujbahuguna.dev@gmail.com>
|
||||
Anusha Ragunathan <anusha.ragunathan@docker.com> <anusha@docker.com>
|
||||
Avi Miller <avi.miller@oracle.com> <avi.miller@gmail.com>
|
||||
Brent Salisbury <brent.salisbury@docker.com> <brent@docker.com>
|
||||
Chander G <chandergovind@gmail.com>
|
||||
Chun Chen <ramichen@tencent.com> <chenchun.feed@gmail.com>
|
||||
Ying Li <cyli@twistedmatrix.com>
|
||||
Daehyeok Mun <daehyeok@gmail.com> <daehyeok@daehyeok-ui-MacBook-Air.local>
|
||||
<dqminh@cloudflare.com> <dqminh89@gmail.com>
|
||||
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>
|
||||
Frederick F. Kautz IV <fkautz@redhat.com> <fkautz@alumni.cmu.edu>
|
||||
Ben Golub <ben.golub@dotcloud.com>
|
||||
Harold Cooper <hrldcpr@gmail.com>
|
||||
hsinko <21551195@zju.edu.cn> <hsinko@users.noreply.github.com>
|
||||
Josh Hawn <josh.hawn@docker.com> <jlhawn@berkeley.edu>
|
||||
Justin Cormack <justin.cormack@docker.com>
|
||||
<justin.cormack@docker.com> <justin.cormack@unikernel.com>
|
||||
<justin.cormack@docker.com> <justin@specialbusservice.com>
|
||||
Kamil Domański <kamil@domanski.co>
|
||||
Lei Jitang <leijitang@huawei.com>
|
||||
<leijitang@huawei.com> <leijitang@gmail.com>
|
||||
Linus Heckemann <lheckemann@twig-world.com>
|
||||
<lheckemann@twig-world.com> <anonymouse2048@gmail.com>
|
||||
Lynda O'Leary <lyndaoleary29@gmail.com>
|
||||
<lyndaoleary29@gmail.com> <lyndaoleary@hotmail.com>
|
||||
Marianna Tessel <mtesselh@gmail.com>
|
||||
Michael Huettermann <michael@huettermann.net>
|
||||
Moysés Borges <moysesb@gmail.com>
|
||||
<moysesb@gmail.com> <moyses.furtado@wplex.com.br>
|
||||
Nigel Poulton <nigelpoulton@hotmail.com>
|
||||
Qiang Huang <h.huangqiang@huawei.com>
|
||||
<h.huangqiang@huawei.com> <qhuang@10.0.2.15>
|
||||
Boaz Shuster <ripcurld.github@gmail.com>
|
||||
Shuwei Hao <haosw@cn.ibm.com>
|
||||
<haosw@cn.ibm.com> <haoshuwei24@gmail.com>
|
||||
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>
|
||||
Stephen Day <stephen.day@docker.com>
|
||||
<stephen.day@docker.com> <stevvooe@users.noreply.github.com>
|
||||
Toli Kuznets <toli@docker.com>
|
||||
Tristan Carel <tristan@cogniteev.com>
|
||||
<tristan@cogniteev.com> <tristan.carel@gmail.com>
|
||||
Vincent Demeester <vincent@sbr.pm>
|
||||
<vincent@sbr.pm> <vincent+github@demeester.fr>
|
||||
Vishnu Kannan <vishnuk@google.com>
|
||||
xlgao-zju <xlgao@zju.edu.cn> xlgao <xlgao@zju.edu.cn>
|
||||
yuchangchun <yuchangchun1@huawei.com> y00277921 <yuchangchun1@huawei.com>
|
||||
<zij@case.edu> <zjaffee@us.ibm.com>
|
||||
|
||||
|
||||
302
CHANGELOG.md
302
CHANGELOG.md
@@ -2,299 +2,39 @@
|
||||
|
||||
Items starting with `DEPRECATE` are important deprecation notices. For more
|
||||
information on the list of deprecated flags and APIs please have a look at
|
||||
https://docs.docker.com/engine/deprecated/ where target removal dates can also
|
||||
https://docs.docker.com/misc/deprecated/ where target removal dates can also
|
||||
be found.
|
||||
|
||||
## 1.11.0 (2016-04-13)
|
||||
|
||||
**IMPORTANT**: With Docker 1.11, a Linux docker installation is now made of 4 binaries (`docker`, [`docker-containerd`](https://github.com/docker/containerd), [`docker-containerd-shim`](https://github.com/docker/containerd) and [`docker-runc`](https://github.com/opencontainers/runc)). If you have scripts relying on docker being a single static binaries, please make sure to update them. Interaction with the daemon stay the same otherwise, the usage of the other binaries should be transparent. A Windows docker installation remains a single binary, `docker.exe`.
|
||||
|
||||
### Builder
|
||||
|
||||
- Fix a bug where Docker would not used the correct uid/gid when processing the `WORKDIR` command ([#21033](https://github.com/docker/docker/pull/21033))
|
||||
- Fix a bug where copy operations with userns would not use the proper uid/gid ([#20782](https://github.com/docker/docker/pull/20782), [#21162](https://github.com/docker/docker/pull/21162))
|
||||
|
||||
### Client
|
||||
|
||||
* Usage of the `:` separator for security option has been deprecated. `=` should be used instead ([#21232](https://github.com/docker/docker/pull/21232))
|
||||
+ The client user agent is now passed to the registry on `pull`, `build`, `push`, `login` and `search` operations ([#21306](https://github.com/docker/docker/pull/21306), [#21373](https://github.com/docker/docker/pull/21373))
|
||||
* Allow setting the Domainname and Hostname separately through the API ([#20200](https://github.com/docker/docker/pull/20200))
|
||||
* Docker info will now warn users if it can not detect the kernel version or the operating system ([#21128](https://github.com/docker/docker/pull/21128))
|
||||
- Fix an issue where `docker stats --no-stream` output could be all 0s ([#20803](https://github.com/docker/docker/pull/20803))
|
||||
- Fix a bug where some newly started container would not appear in a running `docker stats` command ([#20792](https://github.com/docker/docker/pull/20792))
|
||||
* Post processing is no longer enabled for linux-cgo terminals ([#20587](https://github.com/docker/docker/pull/20587))
|
||||
- Values to `--hostname` are now refused if they do not comply with [RFC1123](https://tools.ietf.org/html/rfc1123) ([#20566](https://github.com/docker/docker/pull/20566))
|
||||
+ Docker learned how to use a SOCKS proxy ([#20366](https://github.com/docker/docker/pull/20366), [#18373](https://github.com/docker/docker/pull/18373))
|
||||
+ Docker now supports external credential stores ([#20107](https://github.com/docker/docker/pull/20107))
|
||||
* `docker ps` now supports displaying the list of volumes mounted inside a container ([#20017](https://github.com/docker/docker/pull/20017))
|
||||
* `docker info` now also report Docker's root directory location ([#19986](https://github.com/docker/docker/pull/19986))
|
||||
- Docker now prohibits login in with an empty username (spaces are trimmed) ([#19806](https://github.com/docker/docker/pull/19806))
|
||||
* Docker events attributes are now sorted by key ([#19761](https://github.com/docker/docker/pull/19761))
|
||||
* `docker ps` no longer show exported port for stopped containers ([#19483](https://github.com/docker/docker/pull/19483))
|
||||
- Docker now cleans after itself if a save/export command fails ([#17849](https://github.com/docker/docker/pull/17849))
|
||||
* Docker load learned how to display a progress bar ([#17329](https://github.com/docker/docker/pull/17329), [#120078](https://github.com/docker/docker/pull/20078))
|
||||
|
||||
### Distribution
|
||||
|
||||
- Fix a panic that occurred when pulling an images with 0 layers ([#21222](https://github.com/docker/docker/pull/21222))
|
||||
- Fix a panic that could occur on error while pushing to a registry with a misconfigured token service ([#21212](https://github.com/docker/docker/pull/21212))
|
||||
+ All first-level delegation roles are now signed when doing a trusted push ([#21046](https://github.com/docker/docker/pull/21046))
|
||||
+ OAuth support for registries was added ([#20970](https://github.com/docker/docker/pull/20970))
|
||||
* `docker login` now handles token using the implementation found in [docker/distribution](https://github.com/docker/distribution) ([#20832](https://github.com/docker/docker/pull/20832))
|
||||
* `docker login` will no longer prompt for an email ([#20565](https://github.com/docker/docker/pull/20565))
|
||||
* Docker will now fallback to registry V1 if no basic auth credentials are available ([#20241](https://github.com/docker/docker/pull/20241))
|
||||
* Docker will now try to resume layer download where it left off after a network error/timeout ([#19840](https://github.com/docker/docker/pull/19840))
|
||||
- Fix generated manifest mediaType when pushing cross-repository ([#19509](https://github.com/docker/docker/pull/19509))
|
||||
- Fix docker requesting additional push credentials when pulling an image if Content Trust is enabled ([#20382](https://github.com/docker/docker/pull/20382))
|
||||
|
||||
### Logging
|
||||
|
||||
- Fix a race in the journald log driver ([#21311](https://github.com/docker/docker/pull/21311))
|
||||
* Docker syslog driver now uses the RFC-5424 format when emitting logs ([#20121](https://github.com/docker/docker/pull/20121))
|
||||
* Docker GELF log driver now allows to specify the compression algorithm and level via the `gelf-compression-type` and `gelf-compression-level` options ([#19831](https://github.com/docker/docker/pull/19831))
|
||||
* Docker daemon learned to output uncolorized logs via the `--raw-logs` options ([#19794](https://github.com/docker/docker/pull/19794))
|
||||
+ Docker, on Windows platform, now includes an ETW (Event Tracing in Windows) logging driver named `etwlogs` ([#19689](https://github.com/docker/docker/pull/19689))
|
||||
* Journald log driver learned how to handle tags ([#19564](https://github.com/docker/docker/pull/19564))
|
||||
+ The fluentd log driver learned the following options: `fluentd-address`, `fluentd-buffer-limit`, `fluentd-retry-wait`, `fluentd-max-retries` and `fluentd-async-connect` ([#19439](https://github.com/docker/docker/pull/19439))
|
||||
+ Docker learned to send log to Google Cloud via the new `gcplogs` logging driver. ([#18766](https://github.com/docker/docker/pull/18766))
|
||||
|
||||
|
||||
### Misc
|
||||
|
||||
+ When saving linked images together with `docker save` a subsequent `docker load` will correctly restore their parent/child relationship ([#21385](https://github.com/docker/docker/pull/c))
|
||||
+ Support for building the Docker cli for OpenBSD was added ([#21325](https://github.com/docker/docker/pull/21325))
|
||||
+ Labels can now be applied at network, volume and image creation ([#21270](https://github.com/docker/docker/pull/21270))
|
||||
* The `dockremap` is now created as a system user ([#21266](https://github.com/docker/docker/pull/21266))
|
||||
- Fix a few response body leaks ([#21258](https://github.com/docker/docker/pull/21258))
|
||||
- Docker, when run as a service with systemd, will now properly manage its processes cgroups ([#20633](https://github.com/docker/docker/pull/20633))
|
||||
* Docker info now reports the value of cgroup KernelMemory or emits a warning if it is not supported ([#20863](https://github.com/docker/docker/pull/20863))
|
||||
* Docker info now also reports the cgroup driver in use ([#20388](https://github.com/docker/docker/pull/20388))
|
||||
* Docker completion is now available on PowerShell ([#19894](https://github.com/docker/docker/pull/19894))
|
||||
* `dockerinit` is no more ([#19490](https://github.com/docker/docker/pull/19490),[#19851](https://github.com/docker/docker/pull/19851))
|
||||
+ Support for building Docker on arm64 was added ([#19013](https://github.com/docker/docker/pull/19013))
|
||||
+ Experimental support for building docker.exe in a native Windows Docker installation ([#18348](https://github.com/docker/docker/pull/18348))
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix panic if a node is forcibly removed from the cluster ([#21671](https://github.com/docker/docker/pull/21671))
|
||||
- Fix "error creating vxlan interface" when starting a container in a Swarm cluster ([#21671](https://github.com/docker/docker/pull/21671))
|
||||
* `docker network inspect` will now report all endpoints whether they have an active container or not ([#21160](https://github.com/docker/docker/pull/21160))
|
||||
+ Experimental support for the MacVlan and IPVlan network drivers have been added ([#21122](https://github.com/docker/docker/pull/21122))
|
||||
* Output of `docker network ls` is now sorted by network name ([#20383](https://github.com/docker/docker/pull/20383))
|
||||
- Fix a bug where Docker would allow a network to be created with the reserved `default` name ([#19431](https://github.com/docker/docker/pull/19431))
|
||||
* `docker network inspect` returns whether a network is internal or not ([#19357](https://github.com/docker/docker/pull/19357))
|
||||
+ Control IPv6 via explicit option when creating a network (`docker network create --ipv6`). This shows up as a new `EnableIPv6` field in `docker network inspect` ([#17513](https://github.com/docker/docker/pull/17513))
|
||||
* Support for AAAA Records (aka IPv6 Service Discovery) in embedded DNS Server ([#21396](https://github.com/docker/docker/pull/21396))
|
||||
- Fix to not forward docker domain IPv6 queries to external servers ([#21396](https://github.com/docker/docker/pull/21396))
|
||||
* Multiple A/AAAA records from embedded DNS Server for DNS Round robin ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Fix endpoint count inconsistency after an ungraceful dameon restart ([#21261](https://github.com/docker/docker/pull/21261))
|
||||
- Move the ownership of exposed ports and port-mapping options from Endpoint to Sandbox ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Fixed a bug which prevents docker reload when host is configured with ipv6.disable=1 ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Added inbuilt nil IPAM driver ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Fixed bug in iptables.Exists() logic [#21019](https://github.com/docker/docker/pull/21019)
|
||||
- Fixed a Veth interface leak when using overlay network ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Fixed a bug which prevents docker reload after a network delete during shutdown ([#20214](https://github.com/docker/docker/pull/20214))
|
||||
- Make sure iptables chains are recreated on firewalld reload ([#20419](https://github.com/docker/docker/pull/20419))
|
||||
- Allow to pass global datastore during config reload ([#20419](https://github.com/docker/docker/pull/20419))
|
||||
- For anonymous containers use the alias name for IP to name mapping, ie:DNS PTR record ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Fix a panic when deleting an entry from /etc/hosts file ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Source the forwarded DNS queries from the container net namespace ([#21019](https://github.com/docker/docker/pull/21019))
|
||||
- Fix to retain the network internal mode config for bridge networks on daemon reload ([#21780] (https://github.com/docker/docker/pull/21780))
|
||||
- Fix to retain IPAM driver option configs on daemon reload ([#21914] (https://github.com/docker/docker/pull/21914))
|
||||
|
||||
### Plugins
|
||||
|
||||
- Fix a file descriptor leak that would occur every time plugins were enumerated ([#20686](https://github.com/docker/docker/pull/20686))
|
||||
- Fix an issue where Authz plugin would corrupt the payload body when faced with a large amount of data ([#20602](https://github.com/docker/docker/pull/20602))
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix a panic that could occur when cleanup after a container started with invalid parameters ([#21716](https://github.com/docker/docker/pull/21716))
|
||||
- Fix a race with event timers stopping early ([#21692](https://github.com/docker/docker/pull/21692))
|
||||
- Fix race conditions in the layer store, potentially corrupting the map and crashing the process ([#21677](https://github.com/docker/docker/pull/21677))
|
||||
- Un-deprecate auto-creation of host directories for mounts. This feature was marked deprecated in ([#21666](https://github.com/docker/docker/pull/21666))
|
||||
Docker 1.9, but was decided to be too much of an backward-incompatible change, so it was decided to keep the feature.
|
||||
+ It is now possible for containers to share the NET and IPC namespaces when `userns` is enabled ([#21383](https://github.com/docker/docker/pull/21383))
|
||||
+ `docker inspect <image-id>` will now expose the rootfs layers ([#21370](https://github.com/docker/docker/pull/21370))
|
||||
+ Docker Windows gained a minimal `top` implementation ([#21354](https://github.com/docker/docker/pull/21354))
|
||||
* Docker learned to report the faulty exe when a container cannot be started due to its condition ([#21345](https://github.com/docker/docker/pull/21345))
|
||||
* Docker with device mapper will now refuse to run if `udev sync` is not available ([#21097](https://github.com/docker/docker/pull/21097))
|
||||
- Fix a bug where Docker would not validate the config file upon configuration reload ([#21089](https://github.com/docker/docker/pull/21089))
|
||||
- Fix a hang that would happen on attach if initial start was to fail ([#21048](https://github.com/docker/docker/pull/21048))
|
||||
- Fix an issue where registry service options in the daemon configuration file were not properly taken into account ([#21045](https://github.com/docker/docker/pull/21045))
|
||||
- Fix a race between the exec and resize operations ([#21022](https://github.com/docker/docker/pull/21022))
|
||||
- Fix an issue where nanoseconds were not correctly taken in account when filtering Docker events ([#21013](https://github.com/docker/docker/pull/21013))
|
||||
- Fix the handling of Docker command when passed a 64 bytes id ([#21002](https://github.com/docker/docker/pull/21002))
|
||||
* Docker will now return a `204` (i.e http.StatusNoContent) code when it successfully deleted a network ([#20977](https://github.com/docker/docker/pull/20977))
|
||||
- Fix a bug where the daemon would wait indefinitely in case the process it was about to killed had already exited on its own ([#20967](https://github.com/docker/docker/pull/20967)
|
||||
* The devmapper driver learned the `dm.min_free_space` option. If the mapped device free space reaches the passed value, new device creation will be prohibited. ([#20786](https://github.com/docker/docker/pull/20786))
|
||||
+ Docker can now prevent processes in container to gain new privileges via the `--security-opt=no-new-privileges` flag ([#20727](https://github.com/docker/docker/pull/20727))
|
||||
- Starting a container with the `--device` option will now correctly resolves symlinks ([#20684](https://github.com/docker/docker/pull/20684))
|
||||
+ Docker now relies on [`containerd`](https://github.com/docker/containerd) and [`runc`](https://github.com/opencontainers/runc) to spawn containers. ([#20662](https://github.com/docker/docker/pull/20662))
|
||||
- Fix docker configuration reloading to only alter value present in the given config file ([#20604](https://github.com/docker/docker/pull/20604))
|
||||
+ Docker now allows setting a container hostname via the `--hostname` flag when `--net=host` ([#20177](https://github.com/docker/docker/pull/20177))
|
||||
+ Docker now allows executing privileged container while running with `--userns-remap` if both `--privileged` and the new `--userns=host` flag are specified ([#20111](https://github.com/docker/docker/pull/20111))
|
||||
- Fix Docker not cleaning up correctly old containers upon restarting after a crash ([#19679](https://github.com/docker/docker/pull/19679))
|
||||
* Docker will now error out if it doesn't recognize a configuration key within the config file ([#19517](https://github.com/docker/docker/pull/19517))
|
||||
- Fix container loading, on daemon startup, when they depends on a plugin running within a container ([#19500](https://github.com/docker/docker/pull/19500))
|
||||
* `docker update` learned how to change a container restart policy ([#19116](https://github.com/docker/docker/pull/19116))
|
||||
* `docker inspect` now also returns a new `State` field containing the container state in a human readable way (i.e. one of `created`, `restarting`, `running`, `paused`, `exited` or `dead`)([#18966](https://github.com/docker/docker/pull/18966))
|
||||
+ Docker learned to limit the number of active pids (i.e. processes) within the container via the `pids-limit` flags. NOTE: This requires `CGROUP_PIDS=y` to be in the kernel configuration. ([#18697](https://github.com/docker/docker/pull/18697))
|
||||
- `docker load` now has a `--quiet` option to suppress the load output ([#20078](https://github.com/docker/docker/pull/20078))
|
||||
- Fix a bug in neighbor discovery for IPv6 peers ([#20842](https://github.com/docker/docker/pull/20842))
|
||||
- Fix a panic during cleanup if a container was started with invalid options ([#21802](https://github.com/docker/docker/pull/21802))
|
||||
- Fix a situation where a container cannot be stopped if the terminal is closed ([#21840](https://github.com/docker/docker/pull/21840))
|
||||
|
||||
### Security
|
||||
|
||||
* Object with the `pcp_pmcd_t` selinux type were given management access to `/var/lib/docker(/.*)?` ([#21370](https://github.com/docker/docker/pull/21370))
|
||||
* `restart_syscall`, `copy_file_range`, `mlock2` joined the list of allowed calls in the default seccomp profile ([#21117](https://github.com/docker/docker/pull/21117), [#21262](https://github.com/docker/docker/pull/21262))
|
||||
* `send`, `recv` and `x32` were added to the list of allowed syscalls and arch in the default seccomp profile ([#19432](https://github.com/docker/docker/pull/19432))
|
||||
* Docker Content Trust now requests the server to perform snapshot signing ([#21046](https://github.com/docker/docker/pull/21046))
|
||||
* Support for using YubiKeys for Content Trust signing has been moved out of experimental ([#21591](https://github.com/docker/docker/pull/21591))
|
||||
|
||||
### Volumes
|
||||
|
||||
* Output of `docker volume ls` is now sorted by volume name ([#20389](https://github.com/docker/docker/pull/20389))
|
||||
* Local volumes can now accepts options similar to the unix `mount` tool ([#20262](https://github.com/docker/docker/pull/20262))
|
||||
- Fix an issue where one letter directory name could not be used as source for volumes ([#21106](https://github.com/docker/docker/pull/21106))
|
||||
+ `docker run -v` now accepts a new flag `nocopy`. This tell the runtime not to copy the container path content into the volume (which is the default behavior) ([#21223](https://github.com/docker/docker/pull/21223))
|
||||
|
||||
## 1.10.3 (2016-03-10)
|
||||
|
||||
### Runtime
|
||||
|
||||
- Fix Docker client exiting with an "Unrecognized input header" error [#20706](https://github.com/docker/docker/pull/20706)
|
||||
- Fix Docker exiting if Exec is started with both `AttachStdin` and `Detach` [#20647](https://github.com/docker/docker/pull/20647)
|
||||
|
||||
### Distribution
|
||||
|
||||
- Fix a crash when pushing multiple images sharing the same layers to the same repository in parallel [#20831](https://github.com/docker/docker/pull/20831)
|
||||
- Fix a panic when pushing images to a registry which uses a misconfigured token service [#21030](https://github.com/docker/docker/pull/21030)
|
||||
|
||||
### Plugin system
|
||||
|
||||
- Fix issue preventing volume plugins to start when SELinux is enabled [#20834](https://github.com/docker/docker/pull/20834)
|
||||
- Prevent Docker from exiting if a volume plugin returns a null response for Get requests [#20682](https://github.com/docker/docker/pull/20682)
|
||||
- Fix plugin system leaking file descriptors if a plugin has an error [#20680](https://github.com/docker/docker/pull/20680)
|
||||
|
||||
### Security
|
||||
|
||||
- Fix linux32 emulation to fail during docker build [#20672](https://github.com/docker/docker/pull/20672)
|
||||
It was due to the `personality` syscall being blocked by the default seccomp profile.
|
||||
- Fix Oracle XE 10g failing to start in a container [#20981](https://github.com/docker/docker/pull/20981)
|
||||
It was due to the `ipc` syscall being blocked by the default seccomp profile.
|
||||
- Fix user namespaces not working on Linux From Scratch [#20685](https://github.com/docker/docker/pull/20685)
|
||||
- Fix issue preventing daemon to start if userns is enabled and the `subuid` or `subgid` files contain comments [#20725](https://github.com/docker/docker/pull/20725)
|
||||
|
||||
## 1.10.2 (2016-02-22)
|
||||
|
||||
### Runtime
|
||||
|
||||
- Prevent systemd from deleting containers' cgroups when its configuration is reloaded [#20518](https://github.com/docker/docker/pull/20518)
|
||||
- Fix SELinux issues by disregarding `--read-only` when mounting `/dev/mqueue` [#20333](https://github.com/docker/docker/pull/20333)
|
||||
- Fix chown permissions used during `docker cp` when userns is used [#20446](https://github.com/docker/docker/pull/20446)
|
||||
- Fix configuration loading issue with all booleans defaulting to `true` [#20471](https://github.com/docker/docker/pull/20471)
|
||||
- Fix occasional panic with `docker logs -f` [#20522](https://github.com/docker/docker/pull/20522)
|
||||
|
||||
### Distribution
|
||||
|
||||
- Keep layer reference if deletion failed to avoid a badly inconsistent state [#20513](https://github.com/docker/docker/pull/20513)
|
||||
- Handle gracefully a corner case when canceling migration [#20372](https://github.com/docker/docker/pull/20372)
|
||||
- Fix docker import on compressed data [#20367](https://github.com/docker/docker/pull/20367)
|
||||
- Fix tar-split files corruption during migration that later cause docker push and docker save to fail [#20458](https://github.com/docker/docker/pull/20458)
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix daemon crash if embedded DNS is sent garbage [#20510](https://github.com/docker/docker/pull/20510)
|
||||
|
||||
### Volumes
|
||||
|
||||
- Fix issue with multiple volume references with same name [#20381](https://github.com/docker/docker/pull/20381)
|
||||
|
||||
### Security
|
||||
|
||||
- Fix potential cache corruption and delegation conflict issues [#20523](https://github.com/docker/docker/pull/20523)
|
||||
|
||||
## 1.10.1 (2016-02-11)
|
||||
|
||||
### Runtime
|
||||
|
||||
* Do not stop daemon on migration hard failure [#20156](https://github.com/docker/docker/pull/20156)
|
||||
- Fix various issues with migration to content-addressable images [#20058](https://github.com/docker/docker/pull/20058)
|
||||
- Fix ZFS permission bug with user namespaces [#20045](https://github.com/docker/docker/pull/20045)
|
||||
- Do not leak /dev/mqueue from the host to all containers, keep it container-specific [#19876](https://github.com/docker/docker/pull/19876) [#20133](https://github.com/docker/docker/pull/20133)
|
||||
- Fix `docker ps --filter before=...` to not show stopped containers without providing `-a` flag [#20135](https://github.com/docker/docker/pull/20135)
|
||||
|
||||
### Security
|
||||
|
||||
- Fix issue preventing docker events to work properly with authorization plugin [#20002](https://github.com/docker/docker/pull/20002)
|
||||
|
||||
### Distribution
|
||||
|
||||
* Add additional verifications and prevent from uploading invalid data to registries [#20164](https://github.com/docker/docker/pull/20164)
|
||||
- Fix regression preventing uppercase characters in image reference hostname [#20175](https://github.com/docker/docker/pull/20175)
|
||||
|
||||
### Networking
|
||||
|
||||
- Fix embedded DNS for user-defined networks in the presence of firewalld [#20060](https://github.com/docker/docker/pull/20060)
|
||||
- Fix issue where removing a network during shutdown left Docker inoperable [#20181](https://github.com/docker/docker/issues/20181) [#20235](https://github.com/docker/docker/issues/20235)
|
||||
- Embedded DNS is now able to return compressed results [#20181](https://github.com/docker/docker/issues/20181)
|
||||
- Fix port-mapping issue with `userland-proxy=false` [#20181](https://github.com/docker/docker/issues/20181)
|
||||
|
||||
### Logging
|
||||
|
||||
- Fix bug where tcp+tls protocol would be rejected [#20109](https://github.com/docker/docker/pull/20109)
|
||||
|
||||
### Volumes
|
||||
|
||||
- Fix issue whereby older volume drivers would not receive volume options [#19983](https://github.com/docker/docker/pull/19983)
|
||||
|
||||
### Misc
|
||||
|
||||
- Remove TasksMax from Docker systemd service [#20167](https://github.com/docker/docker/pull/20167)
|
||||
|
||||
## 1.10.0 (2016-02-04)
|
||||
|
||||
**IMPORTANT**: Docker 1.10 uses a new content-addressable storage for images and layers.
|
||||
A migration is performed the first time docker is run, and can take a significant amount of time depending on the number of images present.
|
||||
Refer to this page on the wiki for more information: https://github.com/docker/docker/wiki/Engine-v1.10.0-content-addressability-migration
|
||||
We also released a cool migration utility that enables you to perform the migration before updating to reduce downtime.
|
||||
Engine 1.10 migrator can be found on Docker Hub: https://hub.docker.com/r/docker/v1.10-migrator/
|
||||
|
||||
### Runtime
|
||||
|
||||
+ New `docker update` command that allows updating resource constraints on running containers [#15078](https://github.com/docker/docker/pull/15078)
|
||||
+ Add `--tmpfs` flag to `docker run` to create a tmpfs mount in a container [#13587](https://github.com/docker/docker/pull/13587)
|
||||
+ Add `--format` flag to `docker images` command [#17692](https://github.com/docker/docker/pull/17692)
|
||||
+ Allow to set daemon configuration in a file and hot-reload it with the `SIGHUP` signal [#18587](https://github.com/docker/docker/pull/18587)
|
||||
+ Updated docker events to include more meta-data and event types [#18888](https://github.com/docker/docker/pull/18888)
|
||||
+ Updated docker events to include more meta-data and event types [#18888](https://github.com/docker/docker/pull/18888)
|
||||
This change is backward compatible in the API, but not on the CLI.
|
||||
+ Add `--blkio-weight-device` flag to `docker run` [#13959](https://github.com/docker/docker/pull/13959)
|
||||
+ Add `--device-read-bps` and `--device-write-bps` flags to `docker run` [#14466](https://github.com/docker/docker/pull/14466)
|
||||
+ Add `--device-read-iops` and `--device-write-iops` flags to `docker run` [#15879](https://github.com/docker/docker/pull/15879)
|
||||
+ Add `--oom-score-adj` flag to `docker run` [#16277](https://github.com/docker/docker/pull/16277)
|
||||
+ Add `--detach-keys` flag to `attach`, `run`, `start` and `exec` commands to override the default key sequence that detaches from a container [#15666](https://github.com/docker/docker/pull/15666)
|
||||
* Change `docker run` exit codes to distinguish between runtime and application errors [#14012](https://github.com/docker/docker/pull/14012)
|
||||
+ Add `--detach-keys` flag to `attach` and `exec` commands to override the default key sequence that detaches from a container [#15666](https://github.com/docker/docker/pull/15666)
|
||||
+ Add `--shm-size` flag to `run`, `create` and `build` to set the size of `/dev/shm` [#16168](https://github.com/docker/docker/pull/16168)
|
||||
+ Show the number of running, stopped, and paused containers in `docker info` [#19249](https://github.com/docker/docker/pull/19249)
|
||||
+ Show the `OSType` and `Architecture` in `docker info` [#17478](https://github.com/docker/docker/pull/17478)
|
||||
+ Add `--cgroup-parent` flag on `daemon` to set cgroup parent for all containers [#19062](https://github.com/docker/docker/pull/19062)
|
||||
+ Add `-L` flag to docker cp to follow symlinks [#16613](https://github.com/docker/docker/pull/16613)
|
||||
+ New `status=dead` filter for `docker ps` [#17908](https://github.com/docker/docker/pull/17908)
|
||||
* Change `docker run` exit codes to distinguish between runtime and application errors [#14012](https://github.com/docker/docker/pull/14012)
|
||||
* Enhance `docker events --since` and `--until` to support nanoseconds and timezones [#17495](https://github.com/docker/docker/pull/17495)
|
||||
* Add `--all`/`-a` flag to `stats` to include both running and stopped containers [#16742](https://github.com/docker/docker/pull/16742)
|
||||
* Change the default cgroup-driver to `cgroupfs` [#17704](https://github.com/docker/docker/pull/17704)
|
||||
* Emit a "tag" event when tagging an image with `build -t` [#17115](https://github.com/docker/docker/pull/17115)
|
||||
* Best effort for linked containers' start order when starting the daemon [#18208](https://github.com/docker/docker/pull/18208)
|
||||
* Add ability to add multiple tags on `build` [#15780](https://github.com/docker/docker/pull/15780)
|
||||
* Permit `OPTIONS` request against any url, thus fixing issue with CORS [#19569](https://github.com/docker/docker/pull/19569)
|
||||
- Fix the `--quiet` flag on `docker build` to actually be quiet [#17428](https://github.com/docker/docker/pull/17428)
|
||||
- Fix `docker images --filter dangling=false` to now show all non-dangling images [#19326](https://github.com/docker/docker/pull/19326)
|
||||
- Fix race condition causing autorestart turning off on restart [#17629](https://github.com/docker/docker/pull/17629)
|
||||
- Recognize GPFS filesystems [#19216](https://github.com/docker/docker/pull/19216)
|
||||
- Fix obscure bug preventing to start containers [#19751](https://github.com/docker/docker/pull/19751)
|
||||
- Forbid `exec` during container restart [#19722](https://github.com/docker/docker/pull/19722)
|
||||
- devicemapper: Increasing `--storage-opt dm.basesize` will now increase the base device size on daemon restart [#19123](https://github.com/docker/docker/pull/19123)
|
||||
|
||||
### Security
|
||||
@@ -303,22 +43,20 @@ Engine 1.10 migrator can be found on Docker Hub: https://hub.docker.com/r/docker
|
||||
+ Add support for custom seccomp profiles in `--security-opt` [#17989](https://github.com/docker/docker/pull/17989)
|
||||
+ Add default seccomp profile [#18780](https://github.com/docker/docker/pull/18780)
|
||||
+ Add `--authorization-plugin` flag to `daemon` to customize ACLs [#15365](https://github.com/docker/docker/pull/15365)
|
||||
+ Docker Content Trust now supports the ability to read and write user delegations [#18887](https://github.com/docker/docker/pull/18887)
|
||||
This is an optional, opt-in feature that requires the explicit use of the Notary command-line utility in order to be enabled.
|
||||
Enabling delegation support in a specific repository will break the ability of Docker 1.9 and 1.8 to pull from that repository, if content trust is enabled.
|
||||
* Allow SELinux to run in a container when using the BTRFS storage driver [#16452](https://github.com/docker/docker/pull/16452)
|
||||
|
||||
### Distribution
|
||||
|
||||
* Use content-addressable storage for images and layers [#17924](https://github.com/docker/docker/pull/17924)
|
||||
Note that a migration is performed the first time docker is run; it can take a significant amount of time depending on the number of images and containers present.
|
||||
Images no longer depend on the parent chain but contain a list of layer references.
|
||||
+ New `registrytoken` pass-thru token type for `AuthConfig` [#17481](https://github.com/docker/docker/pull/17481)
|
||||
It allows API clients to retrieve an authentication token from a registry, and then pass that token directly to the remote API
|
||||
* Use content-addressable storage for images and layers [#17924](https://github.com/docker/docker/pull/17924)
|
||||
Note that a migration is performed the first time docker is run; it can take a significant amount of time depending on the number of images and containers present.
|
||||
Images no longer depend on the parent chain but contain a list of layer references.
|
||||
`docker load`/`docker save` tarballs now also contain content-addressable image configurations.
|
||||
For more information: https://github.com/docker/docker/wiki/Engine-v1.10.0-content-addressability-migration
|
||||
* Add support for the new [manifest format ("schema2")](https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-2.md) [#18785](https://github.com/docker/docker/pull/18785)
|
||||
* Lots of improvements for push and pull: performance++, retries on failed downloads, cancelling on client disconnect [#18353](https://github.com/docker/docker/pull/18353), [#18418](https://github.com/docker/docker/pull/18418), [#19109](https://github.com/docker/docker/pull/19109), [#18353](https://github.com/docker/docker/pull/18353)
|
||||
* Limit v1 protocol fallbacks [#18590](https://github.com/docker/docker/pull/18590)
|
||||
- Fix issue where docker could hang indefinitely waiting for a nonexistent process to pull an image [#19743](https://github.com/docker/docker/pull/19743)
|
||||
* Limit v1 protocol fallbacks [#18590](https://github.cAom/docker/docker/pull/18590)
|
||||
* New version of notary with client-side pkcs11 support [#17937](https://github.com/docker/docker/pull/17937)
|
||||
|
||||
### Networking
|
||||
|
||||
@@ -332,7 +70,6 @@ Engine 1.10 migrator can be found on Docker Hub: https://hub.docker.com/r/docker
|
||||
+ Add `--format` flag to `network inspect` [#17481](https://github.com/docker/docker/pull/17481)
|
||||
+ Add `--link` to `network connect` to provide a container-local alias [#19229](https://github.com/docker/docker/pull/19229)
|
||||
+ Support for Capability exchange with remote IPAM plugins [#18775](https://github.com/docker/docker/pull/18775)
|
||||
+ Add `--force` to `network disconnect` to force container to be disconnected from network [#19317](https://github.com/docker/docker/pull/19317)
|
||||
* Support for multi-host networking using built-in overlay driver for all engine supported kernels: 3.10+ [#18775](https://github.com/docker/docker/pull/18775)
|
||||
* `--link` is now supported on `docker run` for containers in user-defined network [#19229](https://github.com/docker/docker/pull/19229)
|
||||
* Enhance `docker network rm` to allow removing multiple networks [#17489](https://github.com/docker/docker/pull/17489)
|
||||
@@ -344,7 +81,6 @@ Engine 1.10 migrator can be found on Docker Hub: https://hub.docker.com/r/docker
|
||||
- Fix MTU issue where Docker would not start with two or more default routes [#18108](https://github.com/docker/docker/pull/18108)
|
||||
- Fix duplicate IP address for containers [#18106](https://github.com/docker/docker/pull/18106)
|
||||
- Fix issue preventing sometimes docker from creating the bridge network [#19338](https://github.com/docker/docker/pull/19338)
|
||||
- Do not substitute 127.0.0.1 name server when using `--net=host` [#19573](https://github.com/docker/docker/pull/19573)
|
||||
|
||||
### Logging
|
||||
|
||||
@@ -356,14 +92,9 @@ Engine 1.10 migrator can be found on Docker Hub: https://hub.docker.com/r/docker
|
||||
### Volumes
|
||||
|
||||
+ Add support to set the mount propagation mode for a volume [#17034](https://github.com/docker/docker/pull/17034)
|
||||
* Add `ls` and `inspect` endpoints to volume plugin API [#16534](https://github.com/docker/docker/pull/16534)
|
||||
* Add `ls` and `inspect` endpoints to volume plugin API [#16534](https://github.com/docker/docker/pull/16534)
|
||||
Existing plugins need to make use of these new APIs to satisfy users' expectation
|
||||
For that, please use the new MIME type `application/vnd.docker.plugins.v1.2+json` [#19549](https://github.com/docker/docker/pull/19549)
|
||||
- Fix data not being copied to named volumes [#19175](https://github.com/docker/docker/pull/19175)
|
||||
- Fix issues preventing volume drivers from being containerized [#19500](https://github.com/docker/docker/pull/19500)
|
||||
- Fix `docker volumes ls --dangling=false` to now show all non-dangling volumes [#19671](https://github.com/docker/docker/pull/19671)
|
||||
- Do not remove named volumes on container removal [#19568](https://github.com/docker/docker/pull/19568)
|
||||
- Allow external volume drivers to host anonymous volumes [#19190](https://github.com/docker/docker/pull/19190)
|
||||
|
||||
### Builder
|
||||
|
||||
@@ -374,11 +105,6 @@ Engine 1.10 migrator can be found on Docker Hub: https://hub.docker.com/r/docker
|
||||
### Client
|
||||
|
||||
+ Add support for overriding the API version to use via an `DOCKER_API_VERSION` environment-variable [#15964](https://github.com/docker/docker/pull/15964)
|
||||
- Fix a bug preventing Windows clients to log in to Docker Hub [#19891](https://github.com/docker/docker/pull/19891)
|
||||
|
||||
### Misc
|
||||
|
||||
* systemd: Set TasksMax in addition to LimitNPROC in systemd service file [#19391](https://github.com/docker/docker/pull/19391)
|
||||
|
||||
### Deprecations
|
||||
|
||||
@@ -1686,7 +1412,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
+ Add -rm to docker run for removing a container on exit
|
||||
- Remove error messages which are not actually errors
|
||||
- Fix `docker rm` with volumes
|
||||
- Fix some error cases where an HTTP body might not be closed
|
||||
- Fix some error cases where a HTTP body might not be closed
|
||||
- Fix panic with wrong dockercfg file
|
||||
- Fix the attach behavior with -i
|
||||
* Record termination time in state.
|
||||
@@ -2020,7 +1746,7 @@ With the ongoing changes to the networking and execution subsystems of docker te
|
||||
+ Containers can expose public UDP ports (eg, '-p 123/udp')
|
||||
+ Optionally specify an exact public port (eg. '-p 80:4500')
|
||||
* 'docker login' supports additional options
|
||||
- Don't save a container`s hostname when committing an image.
|
||||
- Dont save a container`s hostname when committing an image.
|
||||
|
||||
#### Registry
|
||||
|
||||
|
||||
@@ -152,10 +152,8 @@ However, there might be a way to implement that feature *on top of* Docker.
|
||||
<a href="https://groups.google.com/forum/#!forum/docker-user" target="_blank">Docker-user</a>
|
||||
is for people using Docker containers.
|
||||
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 subscribtion.
|
||||
group is for contributors and other people contributing to the Docker
|
||||
project.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
127
Dockerfile
127
Dockerfile
@@ -23,30 +23,22 @@
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM debian:jessie
|
||||
FROM ubuntu:trusty
|
||||
|
||||
# add zfs ppa
|
||||
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys E871F18B51E0147C77796AC81196BA81F6B0FC61 \
|
||||
|| apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys E871F18B51E0147C77796AC81196BA81F6B0FC61
|
||||
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys E871F18B51E0147C77796AC81196BA81F6B0FC61
|
||||
RUN echo deb http://ppa.launchpad.net/zfs-native/stable/ubuntu trusty main > /etc/apt/sources.list.d/zfs.list
|
||||
|
||||
# add llvm repo
|
||||
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 6084F3CF814B57C1CF12EFD515CF4D18AF4F7421 \
|
||||
|| apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 6084F3CF814B57C1CF12EFD515CF4D18AF4F7421
|
||||
RUN echo deb http://llvm.org/apt/jessie/ llvm-toolchain-jessie-3.8 main > /etc/apt/sources.list.d/llvm.list
|
||||
|
||||
# allow replacing httpredir mirror
|
||||
ARG APT_MIRROR=httpredir.debian.org
|
||||
RUN sed -i s/httpredir.debian.org/$APT_MIRROR/g /etc/apt/sources.list
|
||||
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 6084F3CF814B57C1CF12EFD515CF4D18AF4F7421
|
||||
RUN echo deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty main > /etc/apt/sources.list.d/llvm.list
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apparmor \
|
||||
apt-utils \
|
||||
aufs-tools \
|
||||
automake \
|
||||
bash-completion \
|
||||
bsdmainutils \
|
||||
btrfs-tools \
|
||||
build-essential \
|
||||
clang-3.8 \
|
||||
@@ -64,19 +56,17 @@ RUN apt-get update && apt-get install -y \
|
||||
libsystemd-journal-dev \
|
||||
libtool \
|
||||
mercurial \
|
||||
net-tools \
|
||||
pkg-config \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-websocket \
|
||||
s3cmd=1.1.0* \
|
||||
ubuntu-zfs \
|
||||
xfsprogs \
|
||||
libzfs-dev \
|
||||
tar \
|
||||
zip \
|
||||
--no-install-recommends \
|
||||
&& pip install awscli==1.10.15 \
|
||||
&& ln -snf /usr/bin/clang-3.8 /usr/local/bin/clang \
|
||||
&& ln -snf /usr/bin/clang++-3.8 /usr/local/bin/clang++
|
||||
|
||||
@@ -96,37 +86,11 @@ RUN cd /usr/local/lvm2 \
|
||||
&& make install_device-mapper
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# Configure the container for OSX cross compilation
|
||||
ENV OSX_SDK MacOSX10.11.sdk
|
||||
ENV OSX_CROSS_COMMIT 8aa9b71a394905e6c5f4b59e2b97b87a004658a4
|
||||
RUN set -x \
|
||||
&& export OSXCROSS_PATH="/osxcross" \
|
||||
&& git clone https://github.com/tpoechtrager/osxcross.git $OSXCROSS_PATH \
|
||||
&& ( cd $OSXCROSS_PATH && git checkout -q $OSX_CROSS_COMMIT) \
|
||||
&& curl -sSL https://s3.dockerproject.org/darwin/v2/${OSX_SDK}.tar.xz -o "${OSXCROSS_PATH}/tarballs/${OSX_SDK}.tar.xz" \
|
||||
&& UNATTENDED=yes OSX_VERSION_MIN=10.6 ${OSXCROSS_PATH}/build.sh
|
||||
ENV PATH /osxcross/target/bin:$PATH
|
||||
|
||||
# install seccomp: the version shipped in trusty is too old
|
||||
ENV SECCOMP_VERSION 2.3.0
|
||||
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
|
||||
# will need updating, to avoid errors. Ping #docker-maintainers on IRC
|
||||
# with a heads-up.
|
||||
ENV GO_VERSION 1.5.4
|
||||
ENV GO_VERSION 1.5.3
|
||||
RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
@@ -139,6 +103,9 @@ ENV DOCKER_CROSSPLATFORMS \
|
||||
freebsd/amd64 freebsd/386 freebsd/arm \
|
||||
windows/amd64 windows/386
|
||||
|
||||
# (set an explicit GOARM of 5 for maximum compatibility)
|
||||
ENV GOARM 5
|
||||
|
||||
# This has been commented out and kept as reference because we don't support compiling with older Go anymore.
|
||||
# ENV GOFMT_VERSION 1.3.3
|
||||
# RUN curl -sSL https://storage.googleapis.com/golang/go${GOFMT_VERSION}.$(go env GOOS)-$(go env GOARCH).tar.gz | tar -C /go/bin -xz --strip-components=2 go/bin/gofmt
|
||||
@@ -157,12 +124,38 @@ RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint
|
||||
&& (cd /go/src/github.com/golang/lint && git checkout -q $GO_LINT_COMMIT) \
|
||||
&& go install -v github.com/golang/lint/golint
|
||||
|
||||
# Configure the container for OSX cross compilation
|
||||
ENV OSX_SDK MacOSX10.11.sdk
|
||||
RUN set -x \
|
||||
&& export OSXCROSS_PATH="/osxcross" \
|
||||
&& git clone --depth 1 https://github.com/tpoechtrager/osxcross.git $OSXCROSS_PATH \
|
||||
&& curl -sSL https://s3.dockerproject.org/darwin/${OSX_SDK}.tar.xz -o "${OSXCROSS_PATH}/tarballs/${OSX_SDK}.tar.xz" \
|
||||
&& UNATTENDED=yes OSX_VERSION_MIN=10.6 ${OSXCROSS_PATH}/build.sh
|
||||
ENV PATH /osxcross/target/bin:$PATH
|
||||
|
||||
# install seccomp
|
||||
# this can be changed to the ubuntu package libseccomp-dev if dockerinit is removed,
|
||||
# we need libseccomp.a (which the package does not provide) for dockerinit
|
||||
ENV SECCOMP_VERSION 2.2.3
|
||||
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
|
||||
# and schema2 manifests.
|
||||
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||
ENV REGISTRY_COMMIT cb08de17d74bef86ce6c5abe8b240e282f5750be
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||
@@ -175,15 +168,14 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
ENV NOTARY_VERSION docker-v1.10-3
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
@@ -194,6 +186,13 @@ RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
# Setup s3cmd config
|
||||
RUN { \
|
||||
echo '[default]'; \
|
||||
echo 'access_key=$AWS_ACCESS_KEY'; \
|
||||
echo 'secret_key=$AWS_SECRET_KEY'; \
|
||||
} > ~/.s3cfg
|
||||
|
||||
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||
RUN git config --global user.email 'docker-dummy@example.com'
|
||||
|
||||
@@ -203,7 +202,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||
ENV DOCKER_BUILDTAGS apparmor seccomp selinux
|
||||
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||
@@ -214,9 +213,8 @@ RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
buildpack-deps:jessie@sha256:25785f89240fbcdd8a74bdaf30dd5599a9523882c6dfc567f2e9ef7cf6f79db6 \
|
||||
busybox:latest@sha256:e4f93f6ed15a0cdd342f5aae387886fba0ab98af0a102da6276eaf24d6e6ade0 \
|
||||
debian:jessie@sha256:f968f10b4b523737e253a97eac59b0d1420b5c19b69928d35801a6373ffe330e \
|
||||
debian:jessie@sha256:24a900d1671b269d6640b4224e7b63801880d8e3cb2bcbfaa10a5dddcf4469ed \
|
||||
hello-world:latest@sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
|
||||
# see also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
@@ -239,36 +237,13 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Build/install the tool for embedding resources in Windows binaries
|
||||
ENV RSRC_COMMIT ba14da1f827188454a4591717fff29999010887f
|
||||
ENV RSRC_VERSION v2
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/akavel/rsrc.git "$GOPATH/src/github.com/akavel/rsrc" \
|
||||
&& (cd "$GOPATH/src/github.com/akavel/rsrc" && git checkout -q "$RSRC_COMMIT") \
|
||||
&& git clone --depth 1 -b "$RSRC_VERSION" https://github.com/akavel/rsrc.git "$GOPATH/src/github.com/akavel/rsrc" \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git checkout -q "$RUNC_COMMIT" \
|
||||
&& make static BUILDTAGS="seccomp apparmor selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
# This file describes the standard way to build Docker on aarch64, using docker
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # Assemble the full dev environment. This is slow the first time.
|
||||
# docker build -t docker -f Dockerfile.aarch64 .
|
||||
#
|
||||
# # Mount your source in an interactive container for quick testing:
|
||||
# docker run -v `pwd`:/go/src/github.com/docker/docker --privileged -i -t docker bash
|
||||
#
|
||||
# # Run the test suite:
|
||||
# docker run --privileged docker hack/make.sh test
|
||||
#
|
||||
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM aarch64/ubuntu:wily
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apparmor \
|
||||
aufs-tools \
|
||||
automake \
|
||||
bash-completion \
|
||||
btrfs-tools \
|
||||
build-essential \
|
||||
createrepo \
|
||||
curl \
|
||||
dpkg-sig \
|
||||
g++ \
|
||||
gcc \
|
||||
git \
|
||||
iptables \
|
||||
jq \
|
||||
libapparmor-dev \
|
||||
libc6-dev \
|
||||
libcap-dev \
|
||||
libsqlite3-dev \
|
||||
libsystemd-dev \
|
||||
mercurial \
|
||||
net-tools \
|
||||
parallel \
|
||||
pkg-config \
|
||||
python-dev \
|
||||
python-mock \
|
||||
python-pip \
|
||||
python-websocket \
|
||||
gccgo \
|
||||
--no-install-recommends
|
||||
|
||||
# Install armhf loader to use armv6 binaries on armv8
|
||||
RUN dpkg --add-architecture armhf \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y libc6:armhf
|
||||
|
||||
# Get lvm2 source for compiling statically
|
||||
ENV LVM2_VERSION 2.02.103
|
||||
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
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
|
||||
|
||||
# fix platform enablement in lvm2 to support aarch64 properly
|
||||
RUN set -e \
|
||||
&& for f in config.guess config.sub; do \
|
||||
curl -fsSL -o "/usr/local/lvm2/autoconf/$f" "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=$f;hb=HEAD"; \
|
||||
done
|
||||
# "arch.c:78:2: error: #error the arch code needs to know about your machine type"
|
||||
|
||||
# Compile and install lvm2
|
||||
RUN cd /usr/local/lvm2 \
|
||||
&& ./configure \
|
||||
--build="$(gcc -print-multiarch)" \
|
||||
--enable-static_link \
|
||||
&& make device-mapper \
|
||||
&& make install_device-mapper
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# install seccomp: the version shipped in trusty is too old
|
||||
ENV SECCOMP_VERSION 2.3.0
|
||||
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 tarballs for ARM64, eigher for Go or bootstrap,
|
||||
# so we use the official armv6 released binaries as a GOROOT_BOOTSTRAP, and
|
||||
# build Go from source code.
|
||||
ENV GO_VERSION 1.5.4
|
||||
RUN mkdir /usr/src/go && curl -fsSL https://storage.googleapis.com/golang/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 PATH /usr/src/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
|
||||
# Only install one version of the registry, because old version which support
|
||||
# schema1 manifests is not working on ARM64, we should skip integration-cli
|
||||
# tests for schema1 manifests on ARM64.
|
||||
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
ENV DOCKER_PY_COMMIT e2878cbcc3a7eef99917adc1be252800b0e41ece
|
||||
RUN git clone https://github.com/docker/docker-py.git /docker-py \
|
||||
&& cd /docker-py \
|
||||
&& git checkout -q $DOCKER_PY_COMMIT \
|
||||
&& pip install -r test-requirements.txt
|
||||
|
||||
# Set user.email so crosbymichael's in-container merge commits go smoothly
|
||||
RUN git config --global user.email 'docker-dummy@example.com'
|
||||
|
||||
# Add an unprivileged user to be used for tests which need it
|
||||
RUN groupadd -r docker
|
||||
RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||
|
||||
# Register Docker's bash completion.
|
||||
RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||
|
||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
aarch64/buildpack-deps:jessie@sha256:6aa1d6910791b7ac78265fd0798e5abd6cb3f27ae992f6f960f6c303ec9535f2 \
|
||||
aarch64/busybox:latest@sha256:b23a6a37cf269dff6e46d2473b6e227afa42b037e6d23435f1d2bc40fc8c2828 \
|
||||
aarch64/debian:jessie@sha256:4be74a41a7c70ebe887b634b11ffe516cf4fcd56864a54941e56bb49883c3170 \
|
||||
aarch64/hello-world:latest@sha256:65a4a158587b307bb02db4de41b836addb0c35175bdc801367b1ac1ddeb9afda
|
||||
# see also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
# Download man page generator
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone --depth 1 -b v1.0.4 https://github.com/cpuguy83/go-md2man.git "$GOPATH/src/github.com/cpuguy83/go-md2man" \
|
||||
&& git clone --depth 1 -b v1.4 https://github.com/russross/blackfriday.git "$GOPATH/src/github.com/russross/blackfriday" \
|
||||
&& go get -v -d github.com/cpuguy83/go-md2man \
|
||||
&& go build -v -o /usr/local/bin/go-md2man github.com/cpuguy83/go-md2man \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Download toml validator
|
||||
ENV TOMLV_COMMIT 9baf8a8a9f2ed20a8e54160840c492f937eeaf9a
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/BurntSushi/toml.git "$GOPATH/src/github.com/BurntSushi/toml" \
|
||||
&& (cd "$GOPATH/src/github.com/BurntSushi/toml" && git checkout -q "$TOMLV_COMMIT") \
|
||||
&& go build -v -o /usr/local/bin/tomlv github.com/BurntSushi/toml/cmd/tomlv \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git checkout -q "$RUNC_COMMIT" \
|
||||
&& make static BUILDTAGS="seccomp apparmor selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
# Upload docker source
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
@@ -11,11 +11,19 @@
|
||||
# # Run the test suite:
|
||||
# docker run --privileged docker hack/make.sh test
|
||||
#
|
||||
# # Publish a release:
|
||||
# docker run --privileged \
|
||||
# -e AWS_S3_BUCKET=baz \
|
||||
# -e AWS_ACCESS_KEY=foo \
|
||||
# -e AWS_SECRET_KEY=bar \
|
||||
# -e GPG_PASSPHRASE=gloubiboulga \
|
||||
# docker hack/release.sh
|
||||
#
|
||||
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
|
||||
FROM armhf/debian:jessie
|
||||
FROM armhf/ubuntu:trusty
|
||||
|
||||
# Packaged dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -65,11 +73,14 @@ RUN cd /usr/local/lvm2 \
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# Install Go
|
||||
# TODO Update to 1.5.4 once available, or build from source, as these builds
|
||||
# are marked "end of life", see http://dave.cheney.net/unofficial-arm-tarballs
|
||||
ENV GO_VERSION 1.5.3
|
||||
RUN curl -fsSL "http://dave.cheney.net/paste/go${GO_VERSION}.linux-arm.tar.gz" \
|
||||
#ENV GO_VERSION 1.5.3
|
||||
# TODO update GO_TOOLS_COMMIT below when this updates to 1.5+
|
||||
ENV GO_VERSION 1.4.3
|
||||
RUN curl -fsSL "https://github.com/hypriot/golang-armbuilds/releases/download/v${GO_VERSION}/go${GO_VERSION}.linux-armv7.tar.gz" \
|
||||
| tar -xzC /usr/local
|
||||
# temporarily using Hypriot's tarballs while we wait for official 1.6+
|
||||
#RUN curl -fsSL https://golang.org/dl/go${GO_VERSION}.linux-arm6.tar.gz \
|
||||
# | tar -xzC /usr/local
|
||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
|
||||
@@ -81,7 +92,9 @@ ENV GOARM 7
|
||||
# ENV GOFMT_VERSION 1.3.3
|
||||
# RUN curl -sSL https://storage.googleapis.com/golang/go${GOFMT_VERSION}.$(go env GOOS)-$(go env GOARCH).tar.gz | tar -C /go/bin -xz --strip-components=2 go/bin/gofmt
|
||||
|
||||
ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3
|
||||
#ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3
|
||||
# TODO update this sha when we upgrade to Go 1.5+
|
||||
ENV GO_TOOLS_COMMIT 069d2f3bcb68257b627205f0486d6cc69a231ff9
|
||||
# Grab Go's cover tool for dead-simple code coverage testing
|
||||
# Grab Go's vet tool for examining go code to find suspicious constructs
|
||||
# and help prevent errors that the compiler might not catch
|
||||
@@ -90,13 +103,17 @@ RUN git clone https://github.com/golang/tools.git /go/src/golang.org/x/tools \
|
||||
&& go install -v golang.org/x/tools/cmd/cover \
|
||||
&& go install -v golang.org/x/tools/cmd/vet
|
||||
# Grab Go's lint tool
|
||||
ENV GO_LINT_COMMIT 32a87160691b3c96046c0c678fe57c5bef761456
|
||||
#ENV GO_LINT_COMMIT 32a87160691b3c96046c0c678fe57c5bef761456
|
||||
# TODO update this sha when we upgrade to Go 1.5+
|
||||
ENV GO_LINT_COMMIT f42f5c1c440621302702cb0741e9d2ca547ae80f
|
||||
RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint \
|
||||
&& (cd /go/src/github.com/golang/lint && git checkout -q $GO_LINT_COMMIT) \
|
||||
&& go install -v github.com/golang/lint/golint
|
||||
|
||||
# install seccomp: the version shipped in trusty is too old
|
||||
ENV SECCOMP_VERSION 2.3.0
|
||||
# install seccomp
|
||||
# this can be changed to the ubuntu package libseccomp-dev if dockerinit is removed,
|
||||
# we need libseccomp.a (which the package does not provide) for dockerinit
|
||||
ENV SECCOMP_VERSION 2.2.3
|
||||
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" \
|
||||
@@ -115,7 +132,7 @@ RUN set -x \
|
||||
# both. This allows integration-cli tests to cover push/pull with both schema1
|
||||
# and schema2 manifests.
|
||||
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
ENV REGISTRY_COMMIT cb08de17d74bef86ce6c5abe8b240e282f5750be
|
||||
ENV REGISTRY_COMMIT a7ae88da459b98b481a245e5b1750134724ac67d
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||
@@ -128,15 +145,14 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
ENV NOTARY_VERSION docker-v1.10-2
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
@@ -156,7 +172,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
|
||||
ENV DOCKER_BUILDTAGS apparmor seccomp selinux
|
||||
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||
@@ -167,9 +183,8 @@ RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
armhf/buildpack-deps:jessie@sha256:ca6cce8e5bf5c952129889b5cc15cd6aa8d995d77e55e3749bbaadae50e476cb \
|
||||
armhf/busybox:latest@sha256:d98a7343ac750ffe387e3d514f8521ba69846c216778919b01414b8617cfb3d4 \
|
||||
armhf/debian:jessie@sha256:4a2187483f04a84f9830910fe3581d69b3c985cc045d9f01d8e2f3795b28107b \
|
||||
armhf/debian:jessie@sha256:094687129906d2a43cb4e5946ea379b5619c9ca8e4e27b3ba28b40f237a4150c \
|
||||
armhf/hello-world:latest@sha256:161dcecea0225975b2ad5f768058212c1e0d39e8211098666ffa1ac74cfb7791
|
||||
# see also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
@@ -199,28 +214,7 @@ RUN set -x \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git checkout -q "$RUNC_COMMIT" \
|
||||
&& make static BUILDTAGS="seccomp apparmor selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
# Upload docker source
|
||||
|
||||
@@ -23,7 +23,6 @@ RUN apt-get update && apt-get install -y \
|
||||
libcap-dev \
|
||||
libsqlite3-dev \
|
||||
mercurial \
|
||||
net-tools \
|
||||
parallel \
|
||||
python-dev \
|
||||
python-mock \
|
||||
@@ -42,8 +41,10 @@ RUN cd /usr/local/lvm2 \
|
||||
&& make install_device-mapper
|
||||
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
|
||||
|
||||
# install seccomp: the version shipped in jessie is too old
|
||||
ENV SECCOMP_VERSION v2.3.0
|
||||
# install seccomp
|
||||
# this can be changed to the ubuntu package libseccomp-dev if dockerinit is removed,
|
||||
# we need libseccomp.a (which the package does not provide) for dockerinit
|
||||
ENV SECCOMP_VERSION v2.2.3
|
||||
RUN set -x \
|
||||
&& export SECCOMP_PATH=$(mktemp -d) \
|
||||
&& git clone https://github.com/seccomp/libseccomp.git "$SECCOMP_PATH" \
|
||||
@@ -73,28 +74,6 @@ VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor seccomp selinux
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git checkout -q "$RUNC_COMMIT" \
|
||||
&& make static BUILDTAGS="seccomp apparmor selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
# # Run the test suite:
|
||||
# docker run --privileged docker hack/make.sh test
|
||||
#
|
||||
# # Publish a release:
|
||||
# docker run --privileged \
|
||||
# -e AWS_S3_BUCKET=baz \
|
||||
# -e AWS_ACCESS_KEY=foo \
|
||||
# -e AWS_SECRET_KEY=bar \
|
||||
# -e GPG_PASSPHRASE=gloubiboulga \
|
||||
# docker hack/release.sh
|
||||
#
|
||||
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
@@ -74,28 +82,15 @@ RUN cd /usr/local/lvm2 \
|
||||
# TODO install Go, using gccgo as GOROOT_BOOTSTRAP (Go 1.5+ supports ppc64le properly)
|
||||
# possibly a ppc64le/golang image?
|
||||
|
||||
## BUILD GOLANG
|
||||
ENV GO_VERSION 1.5.4
|
||||
ENV GO_DOWNLOAD_URL https://golang.org/dl/go${GO_VERSION}.src.tar.gz
|
||||
ENV GO_DOWNLOAD_SHA256 002acabce7ddc140d0d55891f9d4fcfbdd806b9332fb8b110c91bc91afb0bc93
|
||||
ENV GOROOT_BOOTSTRAP /usr/local
|
||||
|
||||
RUN curl -fsSL "$GO_DOWNLOAD_URL" -o golang.tar.gz \
|
||||
&& echo "$GO_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
|
||||
&& tar -C /usr/src -xzf golang.tar.gz \
|
||||
&& rm golang.tar.gz \
|
||||
&& cd /usr/src/go/src && ./make.bash 2>&1
|
||||
|
||||
ENV GOROOT_BOOTSTRAP /usr/src/
|
||||
|
||||
ENV PATH /usr/src/go/bin/:/go/bin:$PATH
|
||||
ENV PATH /go/bin:$PATH
|
||||
ENV GOPATH /go:/go/src/github.com/docker/docker/vendor
|
||||
|
||||
# This has been commented out and kept as reference because we don't support compiling with older Go anymore.
|
||||
# ENV GOFMT_VERSION 1.3.3
|
||||
# RUN curl -sSL https://storage.googleapis.com/golang/go${GOFMT_VERSION}.$(go env GOOS)-$(go env GOARCH).tar.gz | tar -C /go/bin -xz --strip-components=2 go/bin/gofmt
|
||||
|
||||
ENV GO_TOOLS_COMMIT 823804e1ae08dbb14eb807afc7db9993bc9e3cc3
|
||||
# TODO update this sha when we upgrade to Go 1.5+
|
||||
ENV GO_TOOLS_COMMIT 069d2f3bcb68257b627205f0486d6cc69a231ff9
|
||||
# Grab Go's cover tool for dead-simple code coverage testing
|
||||
# Grab Go's vet tool for examining go code to find suspicious constructs
|
||||
# and help prevent errors that the compiler might not catch
|
||||
@@ -104,39 +99,30 @@ RUN git clone https://github.com/golang/tools.git /go/src/golang.org/x/tools \
|
||||
&& go install -v golang.org/x/tools/cmd/cover \
|
||||
&& go install -v golang.org/x/tools/cmd/vet
|
||||
# Grab Go's lint tool
|
||||
ENV GO_LINT_COMMIT 32a87160691b3c96046c0c678fe57c5bef761456
|
||||
ENV GO_LINT_COMMIT f42f5c1c440621302702cb0741e9d2ca547ae80f
|
||||
RUN git clone https://github.com/golang/lint.git /go/src/github.com/golang/lint \
|
||||
&& (cd /go/src/github.com/golang/lint && git checkout -q $GO_LINT_COMMIT) \
|
||||
&& go install -v github.com/golang/lint/golint
|
||||
|
||||
# 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
|
||||
# and schema2 manifests.
|
||||
ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
|
||||
|
||||
# Install registry
|
||||
ENV REGISTRY_COMMIT ec87e9b6971d831f0eff752ddb54fb64693e51cd
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
|
||||
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary and notary-server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
# Install notary server
|
||||
ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_COMMIT") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary github.com/docker/notary/cmd/notary \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Get the "docker-py" source so we can run their integration tests
|
||||
@@ -155,7 +141,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 selinux
|
||||
ENV DOCKER_BUILDTAGS apparmor selinux
|
||||
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||
@@ -166,9 +152,8 @@ RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
ppc64le/buildpack-deps:jessie@sha256:902bfe4ef1389f94d143d64516dd50a2de75bca2e66d4a44b1d73f63ddf05dda \
|
||||
ppc64le/busybox:latest@sha256:38bb82085248d5a3c24bd7a5dc146f2f2c191e189da0441f1c2ca560e3fc6f1b \
|
||||
ppc64le/debian:jessie@sha256:412845f51b6ab662afba71bc7a716e20fdb9b84f185d180d4c7504f8a75c4f91 \
|
||||
ppc64le/debian:jessie@sha256:74e06e6506b23cf8abd00250782838b2d19910824d8e7eab3d14dc1845ea10c6 \
|
||||
ppc64le/hello-world:latest@sha256:186a40a9a02ca26df0b6c8acdfb8ac2f3ae6678996a838f977e57fac9d963974
|
||||
# see also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
@@ -198,28 +183,6 @@ RUN set -x \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git checkout -q "$RUNC_COMMIT" \
|
||||
&& make static BUILDTAGS="apparmor selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
# # Run the test suite:
|
||||
# docker run --privileged docker hack/make.sh test
|
||||
#
|
||||
# # Publish a release:
|
||||
# docker run --privileged \
|
||||
# -e AWS_S3_BUCKET=baz \
|
||||
# -e AWS_ACCESS_KEY=foo \
|
||||
# -e AWS_SECRET_KEY=bar \
|
||||
# -e GPG_PASSPHRASE=gloubiboulga \
|
||||
# docker hack/release.sh
|
||||
#
|
||||
# Note: AppArmor used to mess with privileged mode, but this is no longer
|
||||
# the case. Therefore, you don't have to disable it anymore.
|
||||
#
|
||||
@@ -108,13 +116,12 @@ RUN set -x \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.11-3
|
||||
ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
||||
RUN set -x \
|
||||
&& export GO15VENDOREXPERIMENT=1 \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/vendor:$GOPATH" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_COMMIT") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
@@ -134,7 +141,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
|
||||
|
||||
VOLUME /var/lib/docker
|
||||
WORKDIR /go/src/github.com/docker/docker
|
||||
ENV DOCKER_BUILDTAGS apparmor pkcs11 selinux
|
||||
ENV DOCKER_BUILDTAGS apparmor selinux
|
||||
|
||||
# Let us use a .bashrc file
|
||||
RUN ln -sfv $PWD/.bashrc ~/.bashrc
|
||||
@@ -145,9 +152,8 @@ RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker
|
||||
# Get useful and necessary Hub images so we can "docker load" locally instead of pulling
|
||||
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/
|
||||
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \
|
||||
s390x/buildpack-deps:jessie@sha256:4d1381224acaca6c4bfe3604de3af6972083a8558a99672cb6989c7541780099 \
|
||||
s390x/busybox:latest@sha256:dd61522c983884a66ed72d60301925889028c6d2d5e0220a8fe1d9b4c6a4f01b \
|
||||
s390x/debian:jessie@sha256:b74c863400909eff3c5e196cac9bfd1f6333ce47aae6a38398d87d5875da170a \
|
||||
s390x/debian:jessie@sha256:3c478e199f60c877c00306356267798d32727dc3cd38512cdb4b060659ea9d20 \
|
||||
s390x/hello-world:latest@sha256:780d80b3a7677c3788c0d5cd9168281320c8d4a6d9183892d8ee5cdd610f5699
|
||||
# see also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is)
|
||||
|
||||
@@ -177,28 +183,6 @@ RUN set -x \
|
||||
&& go build -v -o /usr/local/bin/rsrc github.com/akavel/rsrc \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git checkout -q "$RUNC_COMMIT" \
|
||||
&& make static BUILDTAGS="seccomp apparmor selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
# Wrap all commands in the "docker-in-docker" script to allow nested containers
|
||||
ENTRYPOINT ["hack/dind"]
|
||||
|
||||
|
||||
@@ -29,28 +29,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
aufs-tools \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT e87436998478d222be209707503c27f6f91be0c5
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
&& git checkout -q "$RUNC_COMMIT" \
|
||||
&& make static BUILDTAGS="seccomp apparmor selinux" \
|
||||
&& cp runc /usr/local/bin/docker-runc
|
||||
|
||||
# Install containerd
|
||||
ENV CONTAINERD_COMMIT d2f03861c91edaafdcb3961461bf82ae83785ed7
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone git://github.com/docker/containerd.git "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& cd "$GOPATH/src/github.com/docker/containerd" \
|
||||
&& git checkout -q "$CONTAINERD_COMMIT" \
|
||||
&& make static \
|
||||
&& cp bin/containerd /usr/local/bin/docker-containerd \
|
||||
&& cp bin/containerd-shim /usr/local/bin/docker-containerd-shim \
|
||||
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr
|
||||
|
||||
ENV AUTO_GOPATH 1
|
||||
WORKDIR /usr/src/docker
|
||||
COPY . /usr/src/docker
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
# This file describes the standard way to build Docker, using a docker container on Windows
|
||||
# Server 2016
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # Assemble the full dev environment. This is slow the first time. Run this from
|
||||
# # a directory containing the sources you are validating. For example from
|
||||
# # c:\go\src\github.com\docker\docker
|
||||
#
|
||||
# docker build -t docker -f Dockerfile.windows .
|
||||
#
|
||||
#
|
||||
# # Build docker in a container. Run the following from a Windows cmd command prommpt,
|
||||
# # replacing c:\built with the directory you want the binaries to be placed on the
|
||||
# # host system.
|
||||
#
|
||||
# docker run --rm -v "c:\built:c:\target" docker sh -c 'cd /c/go/src/github.com/docker/docker; hack/make.sh binary; ec=$?; if [ $ec -eq 0 ]; then robocopy /c/go/src/github.com/docker/docker/bundles/$(cat VERSION)/binary /c/target/binary; fi; exit $ec'
|
||||
#
|
||||
# Important notes:
|
||||
# ---------------
|
||||
#
|
||||
# 'Start-Sleep' is a deliberate workaround for a current problem on containers in Windows
|
||||
# Server 2016. It ensures that the network is up and available for when the command is
|
||||
# network related. This bug is being tracked internally at Microsoft and exists in TP4.
|
||||
# Generally sleep 1 or 2 is probably enough, but making it 5 to make the build file
|
||||
# as bullet proof as possible. This isn't a big deal as this only runs the first time.
|
||||
#
|
||||
# The cygwin posix utilities from GIT aren't usable interactively as at January 2016. This
|
||||
# is because they require a console window which isn't present in a container in Windows.
|
||||
# See the example at the top of this file. Do NOT use -it in that docker run!!!
|
||||
#
|
||||
# Don't try to use a volume for passing the source through. The cygwin posix utilities will
|
||||
# balk at reparse points. Again, see the example at the top of this file on how use a volume
|
||||
# to get the built binary out of the container.
|
||||
#
|
||||
# The steps are minimised dramatically to improve performance (TP4 is slow on commit)
|
||||
|
||||
FROM windowsservercore
|
||||
|
||||
# Environment variable notes:
|
||||
# - GO_VERSION must consistent with 'Dockerfile' used by Linux'.
|
||||
# - FROM_DOCKERFILE is used for detection of building within a container.
|
||||
ENV GO_VERSION=1.5.4 \
|
||||
GIT_LOCATION=https://github.com/git-for-windows/git/releases/download/v2.7.2.windows.1/Git-2.7.2-64-bit.exe \
|
||||
RSRC_COMMIT=ba14da1f827188454a4591717fff29999010887f \
|
||||
GOPATH=C:/go;C:/go/src/github.com/docker/docker/vendor \
|
||||
FROM_DOCKERFILE=1
|
||||
|
||||
WORKDIR c:/
|
||||
|
||||
# Everything downloaded/installed in one go (better performance, esp on TP4)
|
||||
RUN \
|
||||
setx /M Path "c:\git\cmd;c:\git\bin;c:\git\usr\bin;%Path%;c:\gcc\bin;c:\go\bin" && \
|
||||
setx GOROOT "c:\go" && \
|
||||
powershell -command \
|
||||
$ErrorActionPreference = 'Stop'; \
|
||||
Start-Sleep -Seconds 5; \
|
||||
Function Download-File([string] $source, [string] $target) { \
|
||||
$wc = New-Object net.webclient; $wc.Downloadfile($source, $target) \
|
||||
} \
|
||||
\
|
||||
Write-Host INFO: Downloading git...; \
|
||||
Download-File %GIT_LOCATION% gitsetup.exe; \
|
||||
\
|
||||
Write-Host INFO: Downloading go...; \
|
||||
Download-File https://storage.googleapis.com/golang/go%GO_VERSION%.windows-amd64.msi go.msi; \
|
||||
\
|
||||
Write-Host INFO: Downloading compiler 1 of 3...; \
|
||||
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip gcc.zip; \
|
||||
\
|
||||
Write-Host INFO: Downloading compiler 2 of 3...; \
|
||||
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip runtime.zip; \
|
||||
\
|
||||
Write-Host INFO: Downloading compiler 3 of 3...; \
|
||||
Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip binutils.zip; \
|
||||
\
|
||||
Write-Host INFO: Installing git...; \
|
||||
Start-Process gitsetup.exe -ArgumentList '/VERYSILENT /SUPPRESSMSGBOXES /CLOSEAPPLICATIONS /DIR=c:\git\' -Wait; \
|
||||
\
|
||||
Write-Host INFO: Installing go..."; \
|
||||
Start-Process msiexec -ArgumentList '-i go.msi -quiet' -Wait; \
|
||||
\
|
||||
Write-Host INFO: Unzipping compiler...; \
|
||||
c:\git\usr\bin\unzip.exe -q -o gcc.zip -d /c/gcc; \
|
||||
c:\git\usr\bin\unzip.exe -q -o runtime.zip -d /c/gcc; \
|
||||
c:\git\usr\bin\unzip.exe -q -o binutils.zip -d /c/gcc"; \
|
||||
\
|
||||
Write-Host INFO: Removing interim files; \
|
||||
Remove-Item *.zip; \
|
||||
Remove-Item go.msi; \
|
||||
Remove-Item gitsetup.exe; \
|
||||
\
|
||||
Write-Host INFO: Cloning and installing RSRC; \
|
||||
c:\git\bin\git.exe clone https://github.com/akavel/rsrc.git c:\go\src\github.com\akavel\rsrc; \
|
||||
cd \go\src\github.com\akavel\rsrc; c:\git\bin\git.exe checkout -q %RSRC_COMMIT%; c:\go\bin\go.exe install -v; \
|
||||
\
|
||||
Write-Host INFO: Completed
|
||||
|
||||
# Prepare for building
|
||||
COPY . /go/src/github.com/docker/docker
|
||||
|
||||
27
MAINTAINERS
27
MAINTAINERS
@@ -26,7 +26,6 @@
|
||||
# the release process is clear and up-to-date.
|
||||
|
||||
people = [
|
||||
"aaronlehmann",
|
||||
"calavera",
|
||||
"coolljt0725",
|
||||
"cpuguy83",
|
||||
@@ -34,7 +33,6 @@
|
||||
"duglin",
|
||||
"estesp",
|
||||
"icecrime",
|
||||
"jhowardmsft",
|
||||
"jfrazelle",
|
||||
"lk4d4",
|
||||
"mhbauer",
|
||||
@@ -71,7 +69,6 @@
|
||||
# - close an issue or pull request when it's inappropriate or off-topic
|
||||
|
||||
people = [
|
||||
"programmerq",
|
||||
"thajeztah"
|
||||
]
|
||||
|
||||
@@ -82,7 +79,7 @@
|
||||
# Thank you!
|
||||
|
||||
people = [
|
||||
# As a maintainer, Erik was responsible for the "builder", and
|
||||
# As a maintainer, Erik was responsible for the "builder", and
|
||||
# started the first designs for the new networking model in
|
||||
# Docker. Erik is now working on all kinds of plugins for Docker
|
||||
# (https://github.com/contiv) and various open source projects
|
||||
@@ -114,11 +111,6 @@
|
||||
|
||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||
|
||||
[people.aaronlehmann]
|
||||
Name = "Aaron Lehmann"
|
||||
Email = "aaron.lehmann@docker.com"
|
||||
GitHub = "aaronlehmann"
|
||||
|
||||
[people.calavera]
|
||||
Name = "David Calavera"
|
||||
Email = "david.calavera@gmail.com"
|
||||
@@ -164,14 +156,9 @@
|
||||
Email = "james@lovedthanlost.net"
|
||||
GitHub = "jamtur01"
|
||||
|
||||
[people.jhowardmsft]
|
||||
Name = "John Howard"
|
||||
Email = "jhoward@microsoft.com"
|
||||
GitHub = "jhowardmsft"
|
||||
|
||||
[people.jfrazelle]
|
||||
Name = "Jessie Frazelle"
|
||||
Email = "jess@linux.com"
|
||||
Email = "j@docker.com"
|
||||
GitHub = "jfrazelle"
|
||||
|
||||
[people.lk4d4]
|
||||
@@ -189,11 +176,6 @@
|
||||
Email = "mary.anthony@docker.com"
|
||||
GitHub = "moxiegirl"
|
||||
|
||||
[people.programmerq]
|
||||
Name = "Jeff Anderson"
|
||||
Email = "jeff@docker.com"
|
||||
GitHub = "programmerq"
|
||||
|
||||
[people.runcom]
|
||||
Name = "Antonio Murdaca"
|
||||
Email = "runcom@redhat.com"
|
||||
@@ -214,6 +196,11 @@
|
||||
Email = "github@gone.nl"
|
||||
GitHub = "thaJeztah"
|
||||
|
||||
[people.theadactyl]
|
||||
Name = "Thea Lamkin"
|
||||
Email = "thea@docker.com"
|
||||
GitHub = "theadactyl"
|
||||
|
||||
[people.tianon]
|
||||
Name = "Tianon Gravi"
|
||||
Email = "admwiggin@gmail.com"
|
||||
|
||||
45
Makefile
45
Makefile
@@ -1,22 +1,38 @@
|
||||
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-docker-py test-integration-cli test-unit validate
|
||||
|
||||
# get OS/Arch of docker engine
|
||||
DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKER_ENGINE_OSARCH:-$$DOCKER_CLIENT_OSARCH}')
|
||||
DOCKERFILE := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKERFILE}')
|
||||
DOCKER_OSARCH := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $${DOCKER_ENGINE_OSARCH:+$$DOCKER_CLIENT_OSARCH}')
|
||||
# default for linux/amd64 and others
|
||||
DOCKERFILE := Dockerfile
|
||||
# switch to different Dockerfile for linux/arm
|
||||
ifeq ($(DOCKER_OSARCH), linux/arm)
|
||||
DOCKERFILE := Dockerfile.armhf
|
||||
else
|
||||
ifeq ($(DOCKER_OSARCH), linux/arm64)
|
||||
# TODO .arm64
|
||||
DOCKERFILE := Dockerfile.armhf
|
||||
else
|
||||
ifeq ($(DOCKER_OSARCH), linux/ppc64le)
|
||||
DOCKERFILE := Dockerfile.ppc64le
|
||||
else
|
||||
ifeq ($(DOCKER_OSARCH), linux/s390x)
|
||||
DOCKERFILE := Dockerfile.s390x
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
export DOCKERFILE
|
||||
|
||||
# env vars passed through directly to Docker's build scripts
|
||||
# to allow things like `make DOCKER_CLIENTONLY=1 binary` easily
|
||||
# `docs/sources/contributing/devenvironment.md ` and `project/PACKAGERS.md` have some limited documentation of some of these
|
||||
DOCKER_ENVS := \
|
||||
-e BUILDFLAGS \
|
||||
-e KEEPBUNDLE \
|
||||
-e DOCKER_BUILD_GOGC \
|
||||
-e DOCKER_BUILD_PKGS \
|
||||
-e DOCKER_CLIENTONLY \
|
||||
-e DOCKER_DEBUG \
|
||||
-e DOCKER_EXPERIMENTAL \
|
||||
-e DOCKERFILE \
|
||||
-e DOCKER_GRAPHDRIVER \
|
||||
-e DOCKER_INCREMENTAL_BINARY \
|
||||
-e DOCKER_REMAP_ROOT \
|
||||
-e DOCKER_STORAGE_OPTS \
|
||||
-e DOCKER_USERLANDPROXY \
|
||||
@@ -31,10 +47,6 @@ DOCKER_ENVS := \
|
||||
BIND_DIR := $(if $(BINDDIR),$(BINDDIR),$(if $(DOCKER_HOST),,bundles))
|
||||
DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)")
|
||||
|
||||
# This allows the test suite to be able to run without worrying about the underlying fs used by the container running the daemon (e.g. aufs-on-aufs), so long as the host running the container is running a supported fs.
|
||||
# The volume will be cleaned up when the container is removed due to `--rm`.
|
||||
# Note that `BIND_DIR` will already be set to `bundles` if `DOCKER_HOST` is not set (see above BIND_DIR line), in such case this will do nothing since `DOCKER_MOUNT` will already be set.
|
||||
DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v "/go/src/github.com/docker/docker/bundles")
|
||||
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||
DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||
@@ -61,7 +73,7 @@ binary: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary
|
||||
|
||||
build: bundles
|
||||
docker build ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
|
||||
docker build -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
|
||||
|
||||
bundles:
|
||||
mkdir bundles
|
||||
@@ -69,21 +81,12 @@ bundles:
|
||||
cross: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross
|
||||
|
||||
win: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh win
|
||||
|
||||
tgz: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross tgz
|
||||
|
||||
deb: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary build-deb
|
||||
|
||||
docs:
|
||||
$(MAKE) -C docs docs
|
||||
|
||||
gccgo: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh gccgo
|
||||
|
||||
rpm: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary build-rpm
|
||||
|
||||
@@ -103,4 +106,4 @@ test-unit: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
||||
|
||||
validate: build
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh validate-dco validate-default-seccomp validate-gofmt validate-pkg validate-lint validate-test validate-toml validate-vet validate-vendor
|
||||
$(DOCKER_RUN_DOCKER) hack/make.sh validate-dco validate-gofmt validate-pkg validate-lint validate-test validate-toml validate-vet validate-vendor
|
||||
|
||||
@@ -167,7 +167,7 @@ Under the hood
|
||||
Under the hood, Docker is built on the following components:
|
||||
|
||||
* The
|
||||
[cgroups](https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt)
|
||||
[cgroups](https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt)
|
||||
and
|
||||
[namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html)
|
||||
capabilities of the Linux kernel
|
||||
@@ -216,7 +216,7 @@ We are always open to suggestions on process improvements, and are always lookin
|
||||
<td>Internet Relay Chat (IRC)</td>
|
||||
<td>
|
||||
<p>
|
||||
IRC is a direct line to our most knowledgeable Docker users; we have
|
||||
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
|
||||
@@ -234,8 +234,6 @@ We are always open to suggestions on process improvements, and are always lookin
|
||||
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 an google account by sending an email to e.g. "docker-user+subscribe@googlegroups.com".
|
||||
After receiving the join-request message, you can simply reply to that to confirm the subscribtion.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
179
ROADMAP.md
179
ROADMAP.md
@@ -33,58 +33,97 @@ won't be accepting pull requests adding or removing items from this file.
|
||||
|
||||
# 1. Features and refactoring
|
||||
|
||||
## 1.1 Runtime improvements
|
||||
## 1.1 Security
|
||||
|
||||
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.
|
||||
Security is a top objective for the Docker Engine. The most notable items we intend to provide in
|
||||
the near future are:
|
||||
|
||||
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.
|
||||
- Trusted distribution of images: the effort is driven by the [distribution](https://github.com/docker/distribution)
|
||||
group but will have significant impact on the Engine
|
||||
- [User namespaces](https://github.com/docker/docker/pull/12648)
|
||||
- [Seccomp support](https://github.com/docker/libcontainer/pull/613)
|
||||
|
||||
Docker Engine 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.
|
||||
## 1.2 Plumbing project
|
||||
|
||||
## 1.2 Plugins improvements
|
||||
We define a plumbing tool as a standalone piece of software usable and meaningful on its own. In
|
||||
the current state of the Docker Engine, most subsystems provide independent functionalities (such
|
||||
the builder, pushing and pulling images, running applications in a containerized environment, etc)
|
||||
but all are coupled in a single binary. We want to offer the users to flexibility to use only the
|
||||
pieces they need, and we will also gain in maintainability by splitting the project among multiple
|
||||
repositories.
|
||||
|
||||
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.
|
||||
As it currently stands, the rough design outlines is to have:
|
||||
- Low level plumbing tools, each dealing with one responsibility (e.g., [runC](https://runc.io))
|
||||
- Docker subsystems services, each exposing an elementary concept over an API, and relying on one or
|
||||
multiple lower level plumbing tools for their implementation (e.g., network management)
|
||||
- Docker Engine to expose higher level actions (e.g., create a container with volume `V` and network
|
||||
`N`), while still providing pass-through access to the individual subsystems.
|
||||
|
||||
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).
|
||||
The architectural details are still being worked on, but one thing we know for sure is that we need
|
||||
to technically decouple the pieces.
|
||||
|
||||
## 1.3 Internal decoupling
|
||||
### 1.2.1 Runtime
|
||||
|
||||
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 ongoing work is happening to move the code to a separate
|
||||
repository ([`docker/engine-api`](https://github.com/docker/engine-api)), 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.
|
||||
A Runtime tool already exists today in the form of [runC](https://github.com/opencontainers/runc).
|
||||
We intend to modify the Engine to directly call out to a binary implementing the Open Containers
|
||||
Specification such as runC rather than relying on libcontainer to set the container runtime up.
|
||||
|
||||
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.
|
||||
This plan will deprecate the existing [`execdriver`](https://github.com/docker/docker/tree/master/daemon/execdriver)
|
||||
as different runtime backends will be implemented as separated binaries instead of being compiled
|
||||
into the Engine.
|
||||
|
||||
## 1.4 Cluster capable Engine
|
||||
### 1.2.2 Builder
|
||||
|
||||
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.
|
||||
The Builder (i.e., the ability to build an image from a Dockerfile) is already nicely decoupled,
|
||||
but would benefit from being entirely separated from the Engine, and rely on the standard Engine
|
||||
API for its operations.
|
||||
|
||||
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.
|
||||
### 1.2.3 Distribution
|
||||
|
||||
Distribution already has a [dedicated repository](https://github.com/docker/distribution) which
|
||||
holds the implementation for Registry v2 and client libraries. We could imagine going further by
|
||||
having the Engine call out to a binary providing image distribution related functionalities.
|
||||
|
||||
There are two short term goals related to image distribution. The first is stabilize and simplify
|
||||
the push/pull code. Following that is the conversion to the more secure Registry V2 protocol.
|
||||
|
||||
### 1.2.4 Networking
|
||||
|
||||
Most of networking related code was already decoupled today in [libnetwork](https://github.com/docker/libnetwork).
|
||||
As with other ingredients, we might want to take it a step further and make it a meaningful utility
|
||||
that the Engine would call out to instead of a library.
|
||||
|
||||
## 1.3 Plugins
|
||||
|
||||
An initiative around plugins started with Docker 1.7.0, with the goal of allowing for out of
|
||||
process extensibility of some Docker functionalities, starting with volumes and networking. The
|
||||
approach is to provide specific extension points rather than generic hooking facilities. We also
|
||||
deliberately keep the extensions API the simplest possible, expanding as we discover valid use
|
||||
cases that cannot be implemented.
|
||||
|
||||
At the time of writing:
|
||||
|
||||
- Plugin support is merged as an experimental feature: real world use cases and user feedback will
|
||||
help us refine the UX to make the feature more user friendly.
|
||||
- There are no immediate plans to expand on the number of pluggable subsystems.
|
||||
- Golang 1.5 might add language support for [plugins](https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ)
|
||||
which we consider supporting as an alternative to JSON/HTTP.
|
||||
|
||||
## 1.4 Volume management
|
||||
|
||||
Volumes are not a first class citizen in the Engine today: we would like better volume management,
|
||||
similar to the way network are managed in the new [CNM](https://github.com/docker/docker/issues/9983).
|
||||
|
||||
## 1.5 Better API implementation
|
||||
|
||||
The current Engine API is insufficiently typed, versioned, and ultimately hard to maintain. We
|
||||
also suffer from the lack of a common implementation with [Swarm](https://github.com/docker/swarm).
|
||||
|
||||
## 1.6 Checkpoint/restore
|
||||
|
||||
Support for checkpoint/restore was [merged](https://github.com/docker/libcontainer/pull/479) in
|
||||
[libcontainer](https://github.com/docker/libcontainer) and made available through [runC](https://runc.io):
|
||||
we intend to take advantage of it in the Engine.
|
||||
|
||||
# 2 Frozen features
|
||||
|
||||
@@ -100,41 +139,45 @@ The Dockerfile syntax as we know it is simple, and has proven successful in supp
|
||||
definitive move, we temporarily won't accept more patches to the Dockerfile syntax for several
|
||||
reasons:
|
||||
|
||||
- Long term impact of syntax changes is a sensitive matter that require an amount of attention the
|
||||
volume of Engine codebase and activity today doesn't allow us to provide.
|
||||
- Allowing the Builder to be implemented as a separate utility consuming the Engine's API will
|
||||
open the door for many possibilities, such as offering alternate syntaxes or DSL for existing
|
||||
languages without cluttering the Engine's codebase.
|
||||
- A standalone Builder will also offer the opportunity for a better dedicated group of maintainers
|
||||
to own the Dockerfile syntax and decide collectively on the direction to give it.
|
||||
- Our experience with official images tend to show that no new instruction or syntax expansion is
|
||||
*strictly* necessary for the majority of use cases, and although we are aware many things are
|
||||
still lacking for many, we cannot make it a priority yet for the above reasons.
|
||||
- Long term impact of syntax changes is a sensitive matter that require an amount of attention
|
||||
the volume of Engine codebase and activity today doesn't allow us to provide.
|
||||
- Allowing the Builder to be implemented as a separate utility consuming the Engine's API will
|
||||
open the door for many possibilities, such as offering alternate syntaxes or DSL for existing
|
||||
languages without cluttering the Engine's codebase.
|
||||
- A standalone Builder will also offer the opportunity for a better dedicated group of maintainers
|
||||
to own the Dockerfile syntax and decide collectively on the direction to give it.
|
||||
- Our experience with official images tend to show that no new instruction or syntax expansion is
|
||||
*strictly* necessary for the majority of use cases, and although we are aware many things are still
|
||||
lacking for many, we cannot make it a priority yet for the above reasons.
|
||||
|
||||
Again, this is not about saying that the Dockerfile syntax is done, it's about making choices about
|
||||
what we want to do first!
|
||||
|
||||
## 2.3 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.
|
||||
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.
|
||||
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:
|
||||
Currently, only the following commands cause interaction with a remote
|
||||
registry:
|
||||
|
||||
- push
|
||||
- pull
|
||||
- run
|
||||
- build
|
||||
- search
|
||||
- login
|
||||
- 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.
|
||||
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.
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -26,7 +24,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
c, err := cli.client.ContainerInspect(context.Background(), cmd.Arg(0))
|
||||
c, err := cli.client.ContainerInspect(cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -43,6 +41,12 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Config.Tty && cli.isTerminalOut {
|
||||
if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
|
||||
logrus.Debugf("Error monitoring TTY size: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *detachKeys != "" {
|
||||
cli.configFile.DetachKeys = *detachKeys
|
||||
}
|
||||
@@ -66,32 +70,11 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
resp, err := cli.client.ContainerAttach(context.Background(), options)
|
||||
resp, err := cli.client.ContainerAttach(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
if in != nil && c.Config.Tty {
|
||||
if err := cli.setRawTerminal(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.restoreTerminal(in)
|
||||
}
|
||||
|
||||
if c.Config.Tty && cli.isTerminalOut {
|
||||
height, width := cli.getTtySize()
|
||||
// To handle the case where a user repeatedly attaches/detaches without resizing their
|
||||
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
|
||||
// resize it, then go back to normal. Without this, every attach after the first will
|
||||
// require the user to manually resize or hit enter.
|
||||
cli.resizeTtyTo(cmd.Arg(0), height+1, width+1, false)
|
||||
|
||||
// After the above resizing occurs, the call to monitorTtySize below will handle resetting back
|
||||
// to the actual size.
|
||||
if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
|
||||
logrus.Debugf("Error monitoring TTY size: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
||||
return err
|
||||
|
||||
@@ -6,20 +6,23 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/dockerignore"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/gitutils"
|
||||
"github.com/docker/docker/pkg/httputils"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
@@ -60,10 +63,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
|
||||
flBuildArg := opts.NewListOpts(runconfigopts.ValidateEnv)
|
||||
cmd.Var(&flBuildArg, []string{"-build-arg"}, "Set build-time variables")
|
||||
isolation := cmd.String([]string{"-isolation"}, "", "Container isolation technology")
|
||||
|
||||
flLabels := opts.NewListOpts(nil)
|
||||
cmd.Var(&flLabels, []string{"-label"}, "Set metadata for an image")
|
||||
isolation := cmd.String([]string{"-isolation"}, "", "Container isolation level")
|
||||
|
||||
ulimits := make(map[string]*units.Ulimit)
|
||||
flUlimits := runconfigopts.NewUlimitOpt(&ulimits)
|
||||
@@ -77,10 +77,14 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var (
|
||||
ctx io.ReadCloser
|
||||
err error
|
||||
context io.ReadCloser
|
||||
isRemote bool
|
||||
err error
|
||||
)
|
||||
|
||||
_, err = exec.LookPath("git")
|
||||
hasGit := err == nil
|
||||
|
||||
specifiedContext := cmd.Arg(0)
|
||||
|
||||
var (
|
||||
@@ -100,13 +104,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
|
||||
switch {
|
||||
case specifiedContext == "-":
|
||||
ctx, relDockerfile, err = builder.GetContextFromReader(cli.in, *dockerfileName)
|
||||
case urlutil.IsGitURL(specifiedContext):
|
||||
tempDir, relDockerfile, err = builder.GetContextFromGitURL(specifiedContext, *dockerfileName)
|
||||
context, relDockerfile, err = getContextFromReader(cli.in, *dockerfileName)
|
||||
case urlutil.IsGitURL(specifiedContext) && hasGit:
|
||||
tempDir, relDockerfile, err = getContextFromGitURL(specifiedContext, *dockerfileName)
|
||||
case urlutil.IsURL(specifiedContext):
|
||||
ctx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, *dockerfileName)
|
||||
context, relDockerfile, err = getContextFromURL(progBuff, specifiedContext, *dockerfileName)
|
||||
default:
|
||||
contextDir, relDockerfile, err = builder.GetContextFromLocalDir(specifiedContext, *dockerfileName)
|
||||
contextDir, relDockerfile, err = getContextFromLocalDir(specifiedContext, *dockerfileName)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -121,7 +125,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
contextDir = tempDir
|
||||
}
|
||||
|
||||
if ctx == nil {
|
||||
if context == nil {
|
||||
// And canonicalize dockerfile name to a platform-independent one
|
||||
relDockerfile, err = archive.CanonicalTarNameForPath(relDockerfile)
|
||||
if err != nil {
|
||||
@@ -141,7 +145,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := builder.ValidateContextDirectory(contextDir, excludes); err != nil {
|
||||
if err := validateContextDirectory(contextDir, excludes); err != nil {
|
||||
return fmt.Errorf("Error checking context: '%s'.", err)
|
||||
}
|
||||
|
||||
@@ -159,7 +163,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
includes = append(includes, ".dockerignore", relDockerfile)
|
||||
}
|
||||
|
||||
ctx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||
context, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||
Compression: archive.Uncompressed,
|
||||
ExcludePatterns: excludes,
|
||||
IncludeFiles: includes,
|
||||
@@ -173,13 +177,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
if isTrusted() {
|
||||
// Wrap the tar archive to replace the Dockerfile entry with the rewritten
|
||||
// Dockerfile which uses trusted pulls.
|
||||
ctx = replaceDockerfileTarWrapper(ctx, relDockerfile, cli.trustedReference, &resolvedTags)
|
||||
context = replaceDockerfileTarWrapper(context, relDockerfile, cli.trustedReference, &resolvedTags)
|
||||
}
|
||||
|
||||
// Setup an upload progress bar
|
||||
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)
|
||||
|
||||
var body io.Reader = progress.NewProgressReader(ctx, progressOutput, 0, "", "Sending build context to Docker daemon")
|
||||
var body io.Reader = progress.NewProgressReader(context, progressOutput, 0, "", "Sending build context to Docker daemon")
|
||||
|
||||
var memory int64
|
||||
if *flMemoryString != "" {
|
||||
@@ -211,17 +215,23 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
var remoteContext string
|
||||
if isRemote {
|
||||
remoteContext = cmd.Arg(0)
|
||||
}
|
||||
|
||||
options := types.ImageBuildOptions{
|
||||
Context: body,
|
||||
Memory: memory,
|
||||
MemorySwap: memorySwap,
|
||||
Tags: flTags.GetAll(),
|
||||
SuppressOutput: *suppressOutput,
|
||||
RemoteContext: remoteContext,
|
||||
NoCache: *noCache,
|
||||
Remove: *rm,
|
||||
ForceRemove: *forceRm,
|
||||
PullParent: *pull,
|
||||
Isolation: container.Isolation(*isolation),
|
||||
IsolationLevel: container.IsolationLevel(*isolation),
|
||||
CPUSetCPUs: *flCPUSetCpus,
|
||||
CPUSetMems: *flCPUSetMems,
|
||||
CPUShares: *flCPUShares,
|
||||
@@ -232,15 +242,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
ShmSize: shmSize,
|
||||
Ulimits: flUlimits.GetList(),
|
||||
BuildArgs: runconfigopts.ConvertKVStringsToMap(flBuildArg.GetAll()),
|
||||
AuthConfigs: cli.retrieveAuthConfigs(),
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
|
||||
AuthConfigs: cli.configFile.AuthConfigs,
|
||||
}
|
||||
|
||||
response, err := cli.client.ImageBuild(context.Background(), options)
|
||||
response, err := cli.client.ImageBuild(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, cli.outFd, cli.isTerminalOut, nil)
|
||||
if err != nil {
|
||||
@@ -281,6 +289,54 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateContextDirectory checks if all the contents of the directory
|
||||
// can be read and returns an error if some files can't be read
|
||||
// symlinks which point to non-existing files don't trigger an error
|
||||
func validateContextDirectory(srcPath string, excludes []string) error {
|
||||
contextRoot, err := getContextRoot(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return filepath.Walk(contextRoot, func(filePath string, f os.FileInfo, err error) error {
|
||||
// skip this directory/file if it's not in the path, it won't get added to the context
|
||||
if relFilePath, err := filepath.Rel(contextRoot, filePath); err != nil {
|
||||
return err
|
||||
} else if skip, err := fileutils.Matches(relFilePath, excludes); err != nil {
|
||||
return err
|
||||
} else if skip {
|
||||
if f.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return fmt.Errorf("can't stat '%s'", filePath)
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// skip checking if symlinks point to non-existing files, such symlinks can be useful
|
||||
// also skip named pipes, because they hanging on open
|
||||
if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !f.IsDir() {
|
||||
currentFile, err := os.Open(filePath)
|
||||
if err != nil && os.IsPermission(err) {
|
||||
return fmt.Errorf("no permission to read from '%s'", filePath)
|
||||
}
|
||||
currentFile.Close()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// validateTag checks if the given image name can be resolved.
|
||||
func validateTag(rawRepo string) (string, error) {
|
||||
_, err := reference.ParseNamed(rawRepo)
|
||||
@@ -291,6 +347,210 @@ func validateTag(rawRepo string) (string, error) {
|
||||
return rawRepo, nil
|
||||
}
|
||||
|
||||
// isUNC returns true if the path is UNC (one starting \\). It always returns
|
||||
// false on Linux.
|
||||
func isUNC(path string) bool {
|
||||
return runtime.GOOS == "windows" && strings.HasPrefix(path, `\\`)
|
||||
}
|
||||
|
||||
// getDockerfileRelPath uses the given context directory for a `docker build`
|
||||
// and returns the absolute path to the context directory, the relative path of
|
||||
// the dockerfile in that context directory, and a non-nil error on success.
|
||||
func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDir, relDockerfile string, err error) {
|
||||
if absContextDir, err = filepath.Abs(givenContextDir); err != nil {
|
||||
return "", "", fmt.Errorf("unable to get absolute context directory: %v", err)
|
||||
}
|
||||
|
||||
// The context dir might be a symbolic link, so follow it to the actual
|
||||
// target directory.
|
||||
//
|
||||
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||
// paths (those starting with \\). This hack means that when using links
|
||||
// on UNC paths, they will not be followed.
|
||||
if !isUNC(absContextDir) {
|
||||
absContextDir, err = filepath.EvalSymlinks(absContextDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("unable to evaluate symlinks in context path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
stat, err := os.Lstat(absContextDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("unable to stat context directory %q: %v", absContextDir, err)
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
return "", "", fmt.Errorf("context must be a directory: %s", absContextDir)
|
||||
}
|
||||
|
||||
absDockerfile := givenDockerfile
|
||||
if absDockerfile == "" {
|
||||
// No -f/--file was specified so use the default relative to the
|
||||
// context directory.
|
||||
absDockerfile = filepath.Join(absContextDir, api.DefaultDockerfileName)
|
||||
|
||||
// Just to be nice ;-) look for 'dockerfile' too but only
|
||||
// use it if we found it, otherwise ignore this check
|
||||
if _, err = os.Lstat(absDockerfile); os.IsNotExist(err) {
|
||||
altPath := filepath.Join(absContextDir, strings.ToLower(api.DefaultDockerfileName))
|
||||
if _, err = os.Lstat(altPath); err == nil {
|
||||
absDockerfile = altPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not already an absolute path, the Dockerfile path should be joined to
|
||||
// the base directory.
|
||||
if !filepath.IsAbs(absDockerfile) {
|
||||
absDockerfile = filepath.Join(absContextDir, absDockerfile)
|
||||
}
|
||||
|
||||
// Evaluate symlinks in the path to the Dockerfile too.
|
||||
//
|
||||
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||
// paths (those starting with \\). This hack means that when using links
|
||||
// on UNC paths, they will not be followed.
|
||||
if !isUNC(absDockerfile) {
|
||||
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Lstat(absDockerfile); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", "", fmt.Errorf("Cannot locate Dockerfile: %q", absDockerfile)
|
||||
}
|
||||
return "", "", fmt.Errorf("unable to stat Dockerfile: %v", err)
|
||||
}
|
||||
|
||||
if relDockerfile, err = filepath.Rel(absContextDir, absDockerfile); err != nil {
|
||||
return "", "", fmt.Errorf("unable to get relative Dockerfile path: %v", err)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||
return "", "", fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", givenDockerfile, givenContextDir)
|
||||
}
|
||||
|
||||
return absContextDir, relDockerfile, nil
|
||||
}
|
||||
|
||||
// writeToFile copies from the given reader and writes it to a file with the
|
||||
// given filename.
|
||||
func writeToFile(r io.Reader, filename string) error {
|
||||
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err := io.Copy(file, r); err != nil {
|
||||
return fmt.Errorf("unable to write file: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getContextFromReader will read the contents of the given reader as either a
|
||||
// Dockerfile or tar archive. Returns a tar archive used as a context and a
|
||||
// path to the Dockerfile inside the tar.
|
||||
func getContextFromReader(r io.ReadCloser, dockerfileName string) (out io.ReadCloser, relDockerfile string, err error) {
|
||||
buf := bufio.NewReader(r)
|
||||
|
||||
magic, err := buf.Peek(archive.HeaderSize)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, "", fmt.Errorf("failed to peek context header from STDIN: %v", err)
|
||||
}
|
||||
|
||||
if archive.IsArchive(magic) {
|
||||
return ioutils.NewReadCloserWrapper(buf, func() error { return r.Close() }), dockerfileName, nil
|
||||
}
|
||||
|
||||
// Input should be read as a Dockerfile.
|
||||
tmpDir, err := ioutil.TempDir("", "docker-build-context-")
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("unbale to create temporary context directory: %v", err)
|
||||
}
|
||||
|
||||
f, err := os.Create(filepath.Join(tmpDir, api.DefaultDockerfileName))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
_, err = io.Copy(f, buf)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := r.Close(); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
tar, err := archive.Tar(tmpDir, archive.Uncompressed)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return ioutils.NewReadCloserWrapper(tar, func() error {
|
||||
err := tar.Close()
|
||||
os.RemoveAll(tmpDir)
|
||||
return err
|
||||
}), api.DefaultDockerfileName, nil
|
||||
|
||||
}
|
||||
|
||||
// getContextFromGitURL uses a Git URL as context for a `docker build`. The
|
||||
// git repo is cloned into a temporary directory used as the context directory.
|
||||
// Returns the absolute path to the temporary context directory, the relative
|
||||
// path of the dockerfile in that context directory, and a non-nil error on
|
||||
// success.
|
||||
func getContextFromGitURL(gitURL, dockerfileName string) (absContextDir, relDockerfile string, err error) {
|
||||
if absContextDir, err = gitutils.Clone(gitURL); err != nil {
|
||||
return "", "", fmt.Errorf("unable to 'git clone' to temporary context directory: %v", err)
|
||||
}
|
||||
|
||||
return getDockerfileRelPath(absContextDir, dockerfileName)
|
||||
}
|
||||
|
||||
// getContextFromURL uses a remote URL as context for a `docker build`. The
|
||||
// remote resource is downloaded as either a Dockerfile or a tar archive.
|
||||
// Returns the tar archive used for the context and a path of the
|
||||
// dockerfile inside the tar.
|
||||
func getContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.ReadCloser, string, error) {
|
||||
response, err := httputils.Download(remoteURL)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("unable to download remote context %s: %v", remoteURL, err)
|
||||
}
|
||||
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(out, true)
|
||||
|
||||
// Pass the response body through a progress reader.
|
||||
progReader := progress.NewProgressReader(response.Body, progressOutput, response.ContentLength, "", fmt.Sprintf("Downloading build context from remote url: %s", remoteURL))
|
||||
|
||||
return getContextFromReader(ioutils.NewReadCloserWrapper(progReader, func() error { return response.Body.Close() }), dockerfileName)
|
||||
}
|
||||
|
||||
// getContextFromLocalDir uses the given local directory as context for a
|
||||
// `docker build`. Returns the absolute path to the local context directory,
|
||||
// the relative path of the dockerfile in that context directory, and a non-nil
|
||||
// error on success.
|
||||
func getContextFromLocalDir(localDir, dockerfileName string) (absContextDir, relDockerfile string, err error) {
|
||||
// When using a local context directory, when the Dockerfile is specified
|
||||
// with the `-f/--file` option then it is considered relative to the
|
||||
// current directory and not the context directory.
|
||||
if dockerfileName != "" {
|
||||
if dockerfileName, err = filepath.Abs(dockerfileName); err != nil {
|
||||
return "", "", fmt.Errorf("unable to get absolute path to Dockerfile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return getDockerfileRelPath(localDir, dockerfileName)
|
||||
}
|
||||
|
||||
var dockerfileFromLinePattern = regexp.MustCompile(`(?i)^[\s]*FROM[ \f\r\t\v]+(?P<image>[^ \f\r\t\v\n#]+)`)
|
||||
|
||||
// resolvedTag records the repository, tag, and resolved digest reference
|
||||
|
||||
@@ -11,12 +11,10 @@ import (
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/cliconfig/credentials"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
)
|
||||
|
||||
@@ -46,8 +44,6 @@ type DockerCli struct {
|
||||
isTerminalOut bool
|
||||
// client is the http client that performs all API operations
|
||||
client client.APIClient
|
||||
// state holds the terminal state
|
||||
state *term.State
|
||||
}
|
||||
|
||||
// Initialize calls the init function that will setup the configuration for the client
|
||||
@@ -83,31 +79,6 @@ func (cli *DockerCli) ImagesFormat() string {
|
||||
return cli.configFile.ImagesFormat
|
||||
}
|
||||
|
||||
func (cli *DockerCli) setRawTerminal() error {
|
||||
if cli.isTerminalIn && os.Getenv("NORAW") == "" {
|
||||
state, err := term.SetRawTerminal(cli.inFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli.state = state
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) restoreTerminal(in io.Closer) error {
|
||||
if cli.state != nil {
|
||||
term.RestoreTerminal(cli.inFd, cli.state)
|
||||
}
|
||||
// WARNING: DO NOT REMOVE THE OS CHECK !!!
|
||||
// For some reason this Close call blocks on darwin..
|
||||
// As the client exists right after, simply discard the close
|
||||
// until we find a better solution.
|
||||
if in != nil && runtime.GOOS != "darwin" {
|
||||
return in.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
|
||||
// The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
|
||||
// is set the client scheme will be set to https.
|
||||
@@ -126,9 +97,6 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
|
||||
if e != nil {
|
||||
fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
|
||||
}
|
||||
if !configFile.ContainsAuth() {
|
||||
credentials.DetectDefaultStore(configFile)
|
||||
}
|
||||
cli.configFile = configFile
|
||||
|
||||
host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions)
|
||||
@@ -140,19 +108,19 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
|
||||
if customHeaders == nil {
|
||||
customHeaders = map[string]string{}
|
||||
}
|
||||
customHeaders["User-Agent"] = clientUserAgent()
|
||||
customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")"
|
||||
|
||||
verStr := api.DefaultVersion.String()
|
||||
if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" {
|
||||
verStr = tmpStr
|
||||
}
|
||||
|
||||
httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
|
||||
clientTransport, err := newClientTransport(clientFlags.Common.TLSOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := client.NewClient(host, verStr, httpClient, customHeaders)
|
||||
client, err := client.NewClient(host, verStr, clientTransport, customHeaders)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -181,35 +149,25 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string,
|
||||
return "", errors.New("Please specify only one -H")
|
||||
}
|
||||
|
||||
host, err = opts.ParseHost(tlsOptions != nil, host)
|
||||
defaultHost := opts.DefaultTCPHost
|
||||
if tlsOptions != nil {
|
||||
defaultHost = opts.DefaultTLSHost
|
||||
}
|
||||
|
||||
host, err = opts.ParseHost(defaultHost, host)
|
||||
return
|
||||
}
|
||||
|
||||
func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) {
|
||||
func newClientTransport(tlsOptions *tlsconfig.Options) (*http.Transport, error) {
|
||||
if tlsOptions == nil {
|
||||
// let the api client configure the default transport.
|
||||
return nil, nil
|
||||
return &http.Transport{}, nil
|
||||
}
|
||||
|
||||
config, err := tlsconfig.Client(*tlsOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr := &http.Transport{
|
||||
return &http.Transport{
|
||||
TLSClientConfig: config,
|
||||
}
|
||||
proto, addr, _, err := client.ParseHost(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sockets.ConfigureTransport(tr, proto, addr)
|
||||
|
||||
return &http.Client{
|
||||
Transport: tr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func clientUserAgent() string {
|
||||
return "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")"
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -75,7 +73,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
|
||||
Config: config,
|
||||
}
|
||||
|
||||
response, err := cli.client.ContainerCommit(context.Background(), options)
|
||||
response, err := cli.client.ContainerCommit(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -127,7 +125,7 @@ func splitCpArg(arg string) (container, path string) {
|
||||
}
|
||||
|
||||
func (cli *DockerCli) statContainerPath(containerName, path string) (types.ContainerPathStat, error) {
|
||||
return cli.client.ContainerStatPath(context.Background(), containerName, path)
|
||||
return cli.client.ContainerStatPath(containerName, path)
|
||||
}
|
||||
|
||||
func resolveLocalPath(localPath string) (absPath string, err error) {
|
||||
@@ -167,7 +165,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
|
||||
|
||||
}
|
||||
|
||||
content, stat, err := cli.client.CopyFromContainer(context.Background(), srcContainer, srcPath)
|
||||
content, stat, err := cli.client.CopyFromContainer(srcContainer, srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -294,5 +292,5 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
|
||||
AllowOverwriteDirWithFile: false,
|
||||
}
|
||||
|
||||
return cli.client.CopyToContainer(context.Background(), options)
|
||||
return cli.client.CopyToContainer(options)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/reference"
|
||||
@@ -42,8 +40,8 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
authConfig := cli.resolveAuthConfig(repoInfo.Index)
|
||||
encodedAuth, err := encodeAuthToBase64(authConfig)
|
||||
// Resolve the Auth config relevant for this server
|
||||
encodedAuth, err := cli.encodeRegistryAuth(repoInfo.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -54,7 +52,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
|
||||
RegistryAuth: encodedAuth,
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ImageCreate(context.Background(), options)
|
||||
responseBody, err := cli.client.ImageCreate(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,30 +90,29 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
|
||||
defer containerIDFile.Close()
|
||||
}
|
||||
|
||||
var trustedRef reference.Canonical
|
||||
_, ref, err := reference.ParseIDOrReference(config.Image)
|
||||
ref, err := reference.ParseNamed(config.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ref != nil {
|
||||
ref = reference.WithDefaultTag(ref)
|
||||
ref = reference.WithDefaultTag(ref)
|
||||
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
|
||||
var err error
|
||||
trustedRef, err = cli.trustedReference(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Image = trustedRef.String()
|
||||
var trustedRef reference.Canonical
|
||||
|
||||
if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
|
||||
var err error
|
||||
trustedRef, err = cli.trustedReference(ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Image = trustedRef.String()
|
||||
}
|
||||
|
||||
//create the container
|
||||
response, err := cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
|
||||
response, err := cli.client.ContainerCreate(config, hostConfig, networkingConfig, name)
|
||||
|
||||
//if image not found try to pull it
|
||||
if err != nil {
|
||||
if client.IsErrImageNotFound(err) && ref != nil {
|
||||
if client.IsErrImageNotFound(err) {
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String())
|
||||
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
@@ -129,7 +126,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
|
||||
}
|
||||
// Retry
|
||||
var retryErr error
|
||||
response, retryErr = cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
|
||||
response, retryErr = cli.client.ContainerCreate(config, hostConfig, networkingConfig, name)
|
||||
if retryErr != nil {
|
||||
return nil, retryErr
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package client
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -27,7 +25,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
|
||||
return fmt.Errorf("Container name cannot be empty")
|
||||
}
|
||||
|
||||
changes, err := cli.client.ContainerDiff(context.Background(), cmd.Arg(0))
|
||||
changes, err := cli.client.ContainerDiff(cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/jsonlog"
|
||||
@@ -52,7 +47,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
|
||||
Filters: eventFilterArgs,
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.Events(context.Background(), options)
|
||||
responseBody, err := cli.client.Events(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -104,43 +99,10 @@ func printOutput(event eventtypes.Message, output io.Writer) {
|
||||
|
||||
if len(event.Actor.Attributes) > 0 {
|
||||
var attrs []string
|
||||
var keys []string
|
||||
for k := range event.Actor.Attributes {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
v := event.Actor.Attributes[k]
|
||||
for k, v := range event.Actor.Attributes {
|
||||
attrs = append(attrs, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
fmt.Fprintf(output, " (%s)", strings.Join(attrs, ", "))
|
||||
}
|
||||
fmt.Fprint(output, "\n")
|
||||
}
|
||||
|
||||
type eventHandler struct {
|
||||
handlers map[string]func(eventtypes.Message)
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (w *eventHandler) Handle(action string, h func(eventtypes.Message)) {
|
||||
w.mu.Lock()
|
||||
w.handlers[action] = h
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
// Watch ranges over the passed in event chan and processes the events based on the
|
||||
// handlers created for a given action.
|
||||
// To stop watching, close the event chan.
|
||||
func (w *eventHandler) Watch(c <-chan eventtypes.Message) {
|
||||
for e := range c {
|
||||
w.mu.Lock()
|
||||
h, exists := w.handlers[e.Action]
|
||||
w.mu.Unlock()
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
logrus.Debugf("event handler: received event: %v", e)
|
||||
go h(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -33,7 +31,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
// Send client escape keys
|
||||
execConfig.DetachKeys = cli.configFile.DetachKeys
|
||||
|
||||
response, err := cli.client.ContainerExecCreate(context.Background(), *execConfig)
|
||||
response, err := cli.client.ContainerExecCreate(*execConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -55,7 +53,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
Tty: execConfig.Tty,
|
||||
}
|
||||
|
||||
if err := cli.client.ContainerExecStart(context.Background(), execID, execStartCheck); err != nil {
|
||||
if err := cli.client.ContainerExecStart(execID, execStartCheck); err != nil {
|
||||
return err
|
||||
}
|
||||
// For now don't print this - wait for when we support exec wait()
|
||||
@@ -84,17 +82,11 @@ func (cli *DockerCli) CmdExec(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := cli.client.ContainerExecAttach(context.Background(), execID, *execConfig)
|
||||
resp, err := cli.client.ContainerExecAttach(execID, *execConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
if in != nil && execConfig.Tty {
|
||||
if err := cli.setRawTerminal(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.restoreTerminal(in)
|
||||
}
|
||||
errCh = promise.Go(func() error {
|
||||
return cli.holdHijackedConnection(execConfig.Tty, in, out, stderr, resp)
|
||||
})
|
||||
|
||||
@@ -3,8 +3,7 @@ package client
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"os"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -22,21 +21,25 @@ func (cli *DockerCli) CmdExport(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
if *outfile == "" && cli.isTerminalOut {
|
||||
var (
|
||||
output = cli.out
|
||||
err error
|
||||
)
|
||||
if *outfile != "" {
|
||||
output, err = os.Create(*outfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if cli.isTerminalOut {
|
||||
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ContainerExport(context.Background(), cmd.Arg(0))
|
||||
responseBody, err := cli.client.ContainerExport(cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if *outfile == "" {
|
||||
_, err := io.Copy(cli.out, responseBody)
|
||||
return err
|
||||
}
|
||||
|
||||
return copyToFile(*outfile, responseBody)
|
||||
|
||||
_, err = io.Copy(output, responseBody)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ const (
|
||||
repositoryHeader = "REPOSITORY"
|
||||
tagHeader = "TAG"
|
||||
digestHeader = "DIGEST"
|
||||
mountsHeader = "MOUNTS"
|
||||
)
|
||||
|
||||
type containerContext struct {
|
||||
@@ -143,25 +142,6 @@ func (c *containerContext) Label(name string) string {
|
||||
return c.c.Labels[name]
|
||||
}
|
||||
|
||||
func (c *containerContext) Mounts() string {
|
||||
c.addHeader(mountsHeader)
|
||||
|
||||
var name string
|
||||
var mounts []string
|
||||
for _, m := range c.c.Mounts {
|
||||
if m.Name == "" {
|
||||
name = m.Source
|
||||
} else {
|
||||
name = m.Name
|
||||
}
|
||||
if c.trunc {
|
||||
name = stringutils.Truncate(name, 15)
|
||||
}
|
||||
mounts = append(mounts, name)
|
||||
}
|
||||
return strings.Join(mounts, ",")
|
||||
}
|
||||
|
||||
type imageContext struct {
|
||||
baseSubContext
|
||||
trunc bool
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func TestContainerPsContext(t *testing.T) {
|
||||
containerID := stringid.GenerateRandomID()
|
||||
unix := time.Now().Add(-65 * time.Second).Unix()
|
||||
unix := time.Now().Unix()
|
||||
|
||||
var ctx containerContext
|
||||
cases := []struct {
|
||||
@@ -55,7 +55,7 @@ func TestContainerPsContext(t *testing.T) {
|
||||
{types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10 B (virtual 20 B)", sizeHeader, ctx.Size},
|
||||
{types.Container{}, true, "", labelsHeader, ctx.Labels},
|
||||
{types.Container{Labels: map[string]string{"cpu": "6", "storage": "ssd"}}, true, "cpu=6,storage=ssd", labelsHeader, ctx.Labels},
|
||||
{types.Container{Created: unix}, true, "About a minute", runningForHeader, ctx.RunningFor},
|
||||
{types.Container{Created: unix}, true, "Less than a second", runningForHeader, ctx.RunningFor},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
@@ -55,7 +54,7 @@ func (c *Context) preformat() {
|
||||
}
|
||||
|
||||
func (c *Context) parseFormat() (*template.Template, error) {
|
||||
tmpl, err := templates.Parse(c.finalFormat)
|
||||
tmpl, err := template.New("").Parse(c.finalFormat)
|
||||
if err != nil {
|
||||
c.buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err))
|
||||
c.buffer.WriteTo(c.Output)
|
||||
|
||||
@@ -2,19 +2,41 @@ package client
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
func (cli *DockerCli) holdHijackedConnection(tty bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
||||
var err error
|
||||
func (cli *DockerCli) holdHijackedConnection(setRawTerminal bool, inputStream io.ReadCloser, outputStream, errorStream io.Writer, resp types.HijackedResponse) error {
|
||||
var (
|
||||
err error
|
||||
oldState *term.State
|
||||
)
|
||||
if inputStream != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
|
||||
oldState, err = term.SetRawTerminal(cli.inFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer term.RestoreTerminal(cli.inFd, oldState)
|
||||
}
|
||||
|
||||
receiveStdout := make(chan error, 1)
|
||||
if outputStream != nil || errorStream != nil {
|
||||
go func() {
|
||||
defer func() {
|
||||
if inputStream != nil {
|
||||
if setRawTerminal && cli.isTerminalIn {
|
||||
term.RestoreTerminal(cli.inFd, oldState)
|
||||
}
|
||||
inputStream.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// When TTY is ON, use regular copy
|
||||
if tty && outputStream != nil {
|
||||
if setRawTerminal && outputStream != nil {
|
||||
_, err = io.Copy(outputStream, resp.Reader)
|
||||
} else {
|
||||
_, err = stdcopy.StdCopy(outputStream, errorStream, resp.Reader)
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
@@ -28,7 +26,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
history, err := cli.client.ImageHistory(context.Background(), cmd.Arg(0))
|
||||
history, err := cli.client.ImageHistory(cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client/formatter"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
@@ -50,7 +48,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
||||
Filters: imageFilterArgs,
|
||||
}
|
||||
|
||||
images, err := cli.client.ImageList(context.Background(), options)
|
||||
images, err := cli.client.ImageList(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
@@ -72,7 +70,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
|
||||
Changes: changes,
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ImageImport(context.Background(), options)
|
||||
responseBody, err := cli.client.ImageImport(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,12 +4,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
@@ -22,7 +19,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
info, err := cli.client.Info(context.Background())
|
||||
info, err := cli.client.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -45,14 +42,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
}
|
||||
|
||||
}
|
||||
if info.SystemStatus != nil {
|
||||
for _, pair := range info.SystemStatus {
|
||||
fmt.Fprintf(cli.out, "%s: %s\n", pair[0], pair[1])
|
||||
}
|
||||
}
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Execution Driver: %s\n", info.ExecutionDriver)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Logging Driver: %s\n", info.LoggingDriver)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Cgroup Driver: %s\n", info.CgroupDriver)
|
||||
|
||||
fmt.Fprintf(cli.out, "Plugins: \n")
|
||||
fmt.Fprintf(cli.out, " Volume:")
|
||||
@@ -76,15 +67,16 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
fmt.Fprintf(cli.out, "Total Memory: %s\n", units.BytesSize(float64(info.MemTotal)))
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Name: %s\n", info.Name)
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "ID: %s\n", info.ID)
|
||||
fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", info.DockerRootDir)
|
||||
fmt.Fprintf(cli.out, "Debug mode (client): %v\n", utils.IsDebugEnabled())
|
||||
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", info.Debug)
|
||||
|
||||
if info.Debug {
|
||||
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", info.Debug)
|
||||
fmt.Fprintf(cli.out, " File Descriptors: %d\n", info.NFd)
|
||||
fmt.Fprintf(cli.out, " Goroutines: %d\n", info.NGoroutines)
|
||||
fmt.Fprintf(cli.out, " System Time: %s\n", info.SystemTime)
|
||||
fmt.Fprintf(cli.out, " EventsListeners: %d\n", info.NEventsListener)
|
||||
fmt.Fprintf(cli.out, " Init SHA1: %s\n", info.InitSha1)
|
||||
fmt.Fprintf(cli.out, " Init Path: %s\n", info.InitPath)
|
||||
fmt.Fprintf(cli.out, " Docker Root Dir: %s\n", info.DockerRootDir)
|
||||
}
|
||||
|
||||
ioutils.FprintfIfNotEmpty(cli.out, "Http Proxy: %s\n", info.HTTPProxy)
|
||||
@@ -95,8 +87,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
u := cli.configFile.AuthConfigs[info.IndexServerAddress].Username
|
||||
if len(u) > 0 {
|
||||
fmt.Fprintf(cli.out, "Username: %v\n", u)
|
||||
fmt.Fprintf(cli.out, "Registry: %v\n", info.IndexServerAddress)
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Registry: %v\n", info.IndexServerAddress)
|
||||
}
|
||||
|
||||
// Only output these warnings if the server does not support these features
|
||||
@@ -107,9 +99,6 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||
if !info.SwapLimit {
|
||||
fmt.Fprintln(cli.err, "WARNING: No swap limit support")
|
||||
}
|
||||
if !info.KernelMemory {
|
||||
fmt.Fprintln(cli.err, "WARNING: No kernel memory limit support")
|
||||
}
|
||||
if !info.OomKillDisable {
|
||||
fmt.Fprintln(cli.err, "WARNING: No oom kill disable support")
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker/api/client/inspect"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"github.com/docker/engine-api/client"
|
||||
)
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"json": func(v interface{}) string {
|
||||
a, _ := json.Marshal(v)
|
||||
return string(a)
|
||||
},
|
||||
}
|
||||
|
||||
// CmdInspect displays low-level information on one or more containers or images.
|
||||
//
|
||||
// Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
|
||||
@@ -43,23 +49,23 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||
|
||||
func (cli *DockerCli) inspectContainers(getSize bool) inspectSearcher {
|
||||
return func(ref string) (interface{}, []byte, error) {
|
||||
return cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
|
||||
return cli.client.ContainerInspectWithRaw(ref, getSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) inspectImages(getSize bool) inspectSearcher {
|
||||
return func(ref string) (interface{}, []byte, error) {
|
||||
return cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
|
||||
return cli.client.ImageInspectWithRaw(ref, getSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) inspectAll(getSize bool) inspectSearcher {
|
||||
return func(ref string) (interface{}, []byte, error) {
|
||||
c, rawContainer, err := cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
|
||||
c, rawContainer, err := cli.client.ContainerInspectWithRaw(ref, getSize)
|
||||
if err != nil {
|
||||
// Search for image with that id if a container doesn't exist.
|
||||
if client.IsErrContainerNotFound(err) {
|
||||
i, rawImage, err := cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
|
||||
i, rawImage, err := cli.client.ImageInspectWithRaw(ref, getSize)
|
||||
if err != nil {
|
||||
if client.IsErrImageNotFound(err) {
|
||||
return nil, nil, fmt.Errorf("Error: No such image or container: %s", ref)
|
||||
@@ -117,7 +123,7 @@ func (cli *DockerCli) inspectErrorStatus(err error) (status int) {
|
||||
func (cli *DockerCli) newInspectorWithTemplate(tmplStr string) (inspect.Inspector, error) {
|
||||
elementInspector := inspect.NewIndentedInspector(cli.out)
|
||||
if tmplStr != "" {
|
||||
tmpl, err := templates.Parse(tmplStr)
|
||||
tmpl, err := template.New("").Funcs(funcMap).Parse(tmplStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Template parsing error: %s", err)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type testElement struct {
|
||||
@@ -14,7 +13,7 @@ type testElement struct {
|
||||
|
||||
func TestTemplateInspectorDefault(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := templates.Parse("{{.DNS}}")
|
||||
tmpl, err := template.New("test").Parse("{{.DNS}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -33,7 +32,7 @@ func TestTemplateInspectorDefault(t *testing.T) {
|
||||
|
||||
func TestTemplateInspectorEmpty(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := templates.Parse("{{.DNS}}")
|
||||
tmpl, err := template.New("test").Parse("{{.DNS}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -49,7 +48,7 @@ func TestTemplateInspectorEmpty(t *testing.T) {
|
||||
|
||||
func TestTemplateInspectorTemplateError(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := templates.Parse("{{.Foo}}")
|
||||
tmpl, err := template.New("test").Parse("{{.Foo}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -67,7 +66,7 @@ func TestTemplateInspectorTemplateError(t *testing.T) {
|
||||
|
||||
func TestTemplateInspectorRawFallback(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := templates.Parse("{{.Dns}}")
|
||||
tmpl, err := template.New("test").Parse("{{.Dns}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -86,7 +85,7 @@ func TestTemplateInspectorRawFallback(t *testing.T) {
|
||||
|
||||
func TestTemplateInspectorRawFallbackError(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := templates.Parse("{{.Dns}}")
|
||||
tmpl, err := template.New("test").Parse("{{.Dns}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -103,7 +102,7 @@ func TestTemplateInspectorRawFallbackError(t *testing.T) {
|
||||
|
||||
func TestTemplateInspectorMultiple(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
tmpl, err := templates.Parse("{{.DNS}}")
|
||||
tmpl, err := template.New("test").Parse("{{.DNS}}")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -22,8 +20,8 @@ func (cli *DockerCli) CmdKill(args ...string) error {
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.ContainerKill(context.Background(), name, *signal); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := cli.client.ContainerKill(name, *signal); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to kill container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -19,7 +17,6 @@ import (
|
||||
func (cli *DockerCli) CmdLoad(args ...string) error {
|
||||
cmd := Cli.Subcmd("load", nil, Cli.DockerCommands["load"].Description, true)
|
||||
infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
|
||||
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the load output")
|
||||
cmd.Require(flag.Exact, 0)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
@@ -32,16 +29,14 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
|
||||
defer file.Close()
|
||||
input = file
|
||||
}
|
||||
if !cli.isTerminalOut {
|
||||
*quiet = true
|
||||
}
|
||||
response, err := cli.client.ImageLoad(context.Background(), input, *quiet)
|
||||
|
||||
response, err := cli.client.ImageLoad(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.Body != nil && response.JSON {
|
||||
if response.JSON {
|
||||
return jsonmessage.DisplayJSONMessagesStream(response.Body, cli.out, cli.outFd, cli.isTerminalOut, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,30 +8,28 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/cliconfig/credentials"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// CmdLogin logs in a user to a Docker registry service.
|
||||
// CmdLogin logs in or registers a user to a Docker registry service.
|
||||
//
|
||||
// If no server is specified, the user will be logged into or registered to the registry's index server.
|
||||
//
|
||||
// Usage: docker login SERVER
|
||||
func (cli *DockerCli) CmdLogin(args ...string) error {
|
||||
cmd := Cli.Subcmd("login", []string{"[SERVER]"}, Cli.DockerCommands["login"].Description+".\nIf no server is specified, the default is defined by the daemon.", true)
|
||||
cmd := Cli.Subcmd("login", []string{"[SERVER]"}, Cli.DockerCommands["login"].Description+".\nIf no server is specified \""+registry.IndexServer+"\" is the default.", true)
|
||||
cmd.Require(flag.Max, 1)
|
||||
|
||||
flUser := cmd.String([]string{"u", "-username"}, "", "Username")
|
||||
flPassword := cmd.String([]string{"p", "-password"}, "", "Password")
|
||||
var username, password, email string
|
||||
|
||||
// Deprecated in 1.11: Should be removed in docker 1.13
|
||||
cmd.String([]string{"#e", "#-email"}, "", "Email")
|
||||
cmd.StringVar(&username, []string{"u", "-username"}, "", "Username")
|
||||
cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
|
||||
cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
@@ -40,138 +38,106 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
|
||||
cli.in = os.Stdin
|
||||
}
|
||||
|
||||
var serverAddress string
|
||||
var isDefaultRegistry bool
|
||||
serverAddress := registry.IndexServer
|
||||
if len(cmd.Args()) > 0 {
|
||||
serverAddress = cmd.Arg(0)
|
||||
}
|
||||
|
||||
promptDefault := func(prompt string, configDefault string) {
|
||||
if configDefault == "" {
|
||||
fmt.Fprintf(cli.out, "%s: ", prompt)
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
|
||||
}
|
||||
}
|
||||
|
||||
readInput := func(in io.Reader, out io.Writer) string {
|
||||
reader := bufio.NewReader(in)
|
||||
line, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(line)
|
||||
}
|
||||
|
||||
authconfig, ok := cli.configFile.AuthConfigs[serverAddress]
|
||||
if !ok {
|
||||
authconfig = types.AuthConfig{}
|
||||
}
|
||||
|
||||
if username == "" {
|
||||
promptDefault("Username", authconfig.Username)
|
||||
username = readInput(cli.in, cli.out)
|
||||
username = strings.TrimSpace(username)
|
||||
if username == "" {
|
||||
username = authconfig.Username
|
||||
}
|
||||
}
|
||||
// Assume that a different username means they may not want to use
|
||||
// the password or email from the config file, so prompt them
|
||||
if username != authconfig.Username {
|
||||
if password == "" {
|
||||
oldState, err := term.SaveState(cli.inFd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Password: ")
|
||||
term.DisableEcho(cli.inFd, oldState)
|
||||
|
||||
password = readInput(cli.in, cli.out)
|
||||
fmt.Fprint(cli.out, "\n")
|
||||
|
||||
term.RestoreTerminal(cli.inFd, oldState)
|
||||
if password == "" {
|
||||
return fmt.Errorf("Error : Password Required")
|
||||
}
|
||||
}
|
||||
|
||||
if email == "" {
|
||||
promptDefault("Email", authconfig.Email)
|
||||
email = readInput(cli.in, cli.out)
|
||||
if email == "" {
|
||||
email = authconfig.Email
|
||||
}
|
||||
}
|
||||
} else {
|
||||
serverAddress = cli.electAuthServer()
|
||||
isDefaultRegistry = true
|
||||
// However, if they don't override the username use the
|
||||
// password or email from the cmd line if specified. IOW, allow
|
||||
// then to change/override them. And if not specified, just
|
||||
// use what's in the config file
|
||||
if password == "" {
|
||||
password = authconfig.Password
|
||||
}
|
||||
if email == "" {
|
||||
email = authconfig.Email
|
||||
}
|
||||
}
|
||||
authconfig.Username = username
|
||||
authconfig.Password = password
|
||||
authconfig.Email = email
|
||||
authconfig.ServerAddress = serverAddress
|
||||
cli.configFile.AuthConfigs[serverAddress] = authconfig
|
||||
|
||||
authConfig, err := cli.configureAuth(*flUser, *flPassword, serverAddress, isDefaultRegistry)
|
||||
auth := cli.configFile.AuthConfigs[serverAddress]
|
||||
response, err := cli.client.RegistryLogin(auth)
|
||||
if err != nil {
|
||||
if client.IsErrUnauthorized(err) {
|
||||
delete(cli.configFile.AuthConfigs, serverAddress)
|
||||
if err2 := cli.configFile.Save(); err2 != nil {
|
||||
fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := cli.client.RegistryLogin(context.Background(), authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.IdentityToken != "" {
|
||||
authConfig.Password = ""
|
||||
authConfig.IdentityToken = response.IdentityToken
|
||||
}
|
||||
if err := storeCredentials(cli.configFile, authConfig); err != nil {
|
||||
return fmt.Errorf("Error saving credentials: %v", err)
|
||||
if err := cli.configFile.Save(); err != nil {
|
||||
return fmt.Errorf("Error saving config file: %v", err)
|
||||
}
|
||||
fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s\n", cli.configFile.Filename())
|
||||
|
||||
if response.Status != "" {
|
||||
fmt.Fprintln(cli.out, response.Status)
|
||||
fmt.Fprintf(cli.out, "%s\n", response.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
|
||||
if configDefault == "" {
|
||||
fmt.Fprintf(cli.out, "%s: ", prompt)
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s (%s): ", prompt, configDefault)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
|
||||
authconfig, err := getCredentials(cli.configFile, serverAddress)
|
||||
if err != nil {
|
||||
return authconfig, err
|
||||
}
|
||||
|
||||
authconfig.Username = strings.TrimSpace(authconfig.Username)
|
||||
|
||||
if flUser = strings.TrimSpace(flUser); flUser == "" {
|
||||
if isDefaultRegistry {
|
||||
// if this is a defauly registry (docker hub), then display the following message.
|
||||
fmt.Fprintln(cli.out, "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.")
|
||||
}
|
||||
cli.promptWithDefault("Username", authconfig.Username)
|
||||
flUser = readInput(cli.in, cli.out)
|
||||
flUser = strings.TrimSpace(flUser)
|
||||
if flUser == "" {
|
||||
flUser = authconfig.Username
|
||||
}
|
||||
}
|
||||
|
||||
if flUser == "" {
|
||||
return authconfig, fmt.Errorf("Error: Non-null Username Required")
|
||||
}
|
||||
|
||||
if flPassword == "" {
|
||||
oldState, err := term.SaveState(cli.inFd)
|
||||
if err != nil {
|
||||
return authconfig, err
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Password: ")
|
||||
term.DisableEcho(cli.inFd, oldState)
|
||||
|
||||
flPassword = readInput(cli.in, cli.out)
|
||||
fmt.Fprint(cli.out, "\n")
|
||||
|
||||
term.RestoreTerminal(cli.inFd, oldState)
|
||||
if flPassword == "" {
|
||||
return authconfig, fmt.Errorf("Error: Password Required")
|
||||
}
|
||||
}
|
||||
|
||||
authconfig.Username = flUser
|
||||
authconfig.Password = flPassword
|
||||
authconfig.ServerAddress = serverAddress
|
||||
authconfig.IdentityToken = ""
|
||||
|
||||
return authconfig, nil
|
||||
}
|
||||
|
||||
func readInput(in io.Reader, out io.Writer) string {
|
||||
reader := bufio.NewReader(in)
|
||||
line, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(line)
|
||||
}
|
||||
|
||||
// getCredentials loads the user credentials from a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func getCredentials(c *cliconfig.ConfigFile, serverAddress string) (types.AuthConfig, error) {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.Get(serverAddress)
|
||||
}
|
||||
|
||||
func getAllCredentials(c *cliconfig.ConfigFile) (map[string]types.AuthConfig, error) {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.GetAll()
|
||||
}
|
||||
|
||||
// storeCredentials saves the user credentials in a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func storeCredentials(c *cliconfig.ConfigFile, auth types.AuthConfig) error {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.Store(auth)
|
||||
}
|
||||
|
||||
// eraseCredentials removes the user credentials from a credentials store.
|
||||
// The store is determined by the config file settings.
|
||||
func eraseCredentials(c *cliconfig.ConfigFile, serverAddress string) error {
|
||||
s := loadCredentialsStore(c)
|
||||
return s.Erase(serverAddress)
|
||||
}
|
||||
|
||||
// loadCredentialsStore initializes a new credentials store based
|
||||
// in the settings provided in the configuration file.
|
||||
func loadCredentialsStore(c *cliconfig.ConfigFile) credentials.Store {
|
||||
if c.CredentialsStore != "" {
|
||||
return credentials.NewNativeStore(c)
|
||||
}
|
||||
return credentials.NewFileStore(c)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/registry"
|
||||
)
|
||||
|
||||
// CmdLogout logs a user out from a Docker registry.
|
||||
@@ -13,28 +14,25 @@ import (
|
||||
//
|
||||
// Usage: docker logout [SERVER]
|
||||
func (cli *DockerCli) CmdLogout(args ...string) error {
|
||||
cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, Cli.DockerCommands["logout"].Description+".\nIf no server is specified, the default is defined by the daemon.", true)
|
||||
cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, Cli.DockerCommands["logout"].Description+".\nIf no server is specified \""+registry.IndexServer+"\" is the default.", true)
|
||||
cmd.Require(flag.Max, 1)
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var serverAddress string
|
||||
serverAddress := registry.IndexServer
|
||||
if len(cmd.Args()) > 0 {
|
||||
serverAddress = cmd.Arg(0)
|
||||
} else {
|
||||
serverAddress = cli.electAuthServer()
|
||||
}
|
||||
|
||||
// check if we're logged in based on the records in the config file
|
||||
// which means it couldn't have user/pass cause they may be in the creds store
|
||||
if _, ok := cli.configFile.AuthConfigs[serverAddress]; !ok {
|
||||
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
|
||||
if err := eraseCredentials(cli.configFile, serverAddress); err != nil {
|
||||
fmt.Fprintf(cli.out, "WARNING: could not erase credentials: %v\n", err)
|
||||
delete(cli.configFile.AuthConfigs, serverAddress)
|
||||
if err := cli.configFile.Save(); err != nil {
|
||||
return fmt.Errorf("Failed to save docker config: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
@@ -32,7 +30,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||
|
||||
name := cmd.Arg(0)
|
||||
|
||||
c, err := cli.client.ContainerInspect(context.Background(), name)
|
||||
c, err := cli.client.ContainerInspect(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,7 +48,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
||||
Follow: *follow,
|
||||
Tail: *tail,
|
||||
}
|
||||
responseBody, err := cli.client.ContainerLogs(context.Background(), options)
|
||||
responseBody, err := cli.client.ContainerLogs(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@ package client
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -44,7 +41,6 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
|
||||
flIpamGateway := opts.NewListOpts(nil)
|
||||
flIpamAux := opts.NewMapOpts(nil, nil)
|
||||
flIpamOpt := opts.NewMapOpts(nil, nil)
|
||||
flLabels := opts.NewListOpts(nil)
|
||||
|
||||
cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
|
||||
cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
|
||||
@@ -52,10 +48,8 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
|
||||
cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
|
||||
cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")
|
||||
cmd.Var(flIpamOpt, []string{"-ipam-opt"}, "set IPAM driver specific options")
|
||||
cmd.Var(&flLabels, []string{"-label"}, "set metadata on a network")
|
||||
|
||||
flInternal := cmd.Bool([]string{"-internal"}, false, "restricts external access to the network")
|
||||
flIPv6 := cmd.Bool([]string{"-ipv6"}, false, "enable IPv6 networking")
|
||||
|
||||
cmd.Require(flag.Exact, 1)
|
||||
err := cmd.ParseFlags(args, true)
|
||||
@@ -83,11 +77,9 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
|
||||
Options: flOpts.GetAll(),
|
||||
CheckDuplicate: true,
|
||||
Internal: *flInternal,
|
||||
EnableIPv6: *flIPv6,
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
|
||||
}
|
||||
|
||||
resp, err := cli.client.NetworkCreate(context.Background(), nc)
|
||||
resp, err := cli.client.NetworkCreate(nc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -107,7 +99,7 @@ func (cli *DockerCli) CmdNetworkRm(args ...string) error {
|
||||
|
||||
status := 0
|
||||
for _, net := range cmd.Args() {
|
||||
if err := cli.client.NetworkRemove(context.Background(), net); err != nil {
|
||||
if err := cli.client.NetworkRemove(net); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
status = 1
|
||||
continue
|
||||
@@ -142,7 +134,7 @@ func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
||||
Links: flLinks.GetAll(),
|
||||
Aliases: flAliases.GetAll(),
|
||||
}
|
||||
return cli.client.NetworkConnect(context.Background(), cmd.Arg(0), cmd.Arg(1), epConfig)
|
||||
return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig)
|
||||
}
|
||||
|
||||
// CmdNetworkDisconnect disconnects a container from a network
|
||||
@@ -156,7 +148,7 @@ func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return cli.client.NetworkDisconnect(context.Background(), cmd.Arg(0), cmd.Arg(1), *force)
|
||||
return cli.client.NetworkDisconnect(cmd.Arg(0), cmd.Arg(1), *force)
|
||||
}
|
||||
|
||||
// CmdNetworkLs lists all the networks managed by docker daemon
|
||||
@@ -189,7 +181,7 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
|
||||
Filters: netFilterArgs,
|
||||
}
|
||||
|
||||
networkResources, err := cli.client.NetworkList(context.Background(), options)
|
||||
networkResources, err := cli.client.NetworkList(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -200,7 +192,7 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
|
||||
if !*quiet {
|
||||
fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
|
||||
}
|
||||
sort.Sort(byNetworkName(networkResources))
|
||||
|
||||
for _, networkResource := range networkResources {
|
||||
ID := networkResource.ID
|
||||
netName := networkResource.Name
|
||||
@@ -222,12 +214,6 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type byNetworkName []types.NetworkResource
|
||||
|
||||
func (r byNetworkName) Len() int { return len(r) }
|
||||
func (r byNetworkName) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
func (r byNetworkName) Less(i, j int) bool { return r[i].Name < r[j].Name }
|
||||
|
||||
// CmdNetworkInspect inspects the network object for more details
|
||||
//
|
||||
// Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
|
||||
@@ -241,7 +227,7 @@ func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
|
||||
}
|
||||
|
||||
inspectSearcher := func(name string) (interface{}, []byte, error) {
|
||||
i, err := cli.client.NetworkInspect(context.Background(), name)
|
||||
i, err := cli.client.NetworkInspect(name)
|
||||
return i, nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -21,8 +19,8 @@ func (cli *DockerCli) CmdPause(args ...string) error {
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.ContainerPause(context.Background(), name); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := cli.client.ContainerPause(name); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to pause container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/go-connections/nat"
|
||||
@@ -21,7 +19,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
c, err := cli.client.ContainerInspect(context.Background(), cmd.Arg(0))
|
||||
c, err := cli.client.ContainerInspect(cmd.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/client/formatter"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
@@ -58,7 +56,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
||||
Filter: psFilterArgs,
|
||||
}
|
||||
|
||||
containers, err := cli.client.ContainerList(context.Background(), options)
|
||||
containers, err := cli.client.ContainerList(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -56,7 +54,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
authConfig := cli.resolveAuthConfig(repoInfo.Index)
|
||||
authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, repoInfo.Index)
|
||||
requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "pull")
|
||||
|
||||
if isTrusted() && !ref.HasDigest() {
|
||||
@@ -79,7 +77,7 @@ func (cli *DockerCli) imagePullPrivileged(authConfig types.AuthConfig, imageID,
|
||||
RegistryAuth: encodedAuth,
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ImagePull(context.Background(), options, requestPrivilege)
|
||||
responseBody, err := cli.client.ImagePull(options, requestPrivilege)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -44,7 +42,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
||||
return err
|
||||
}
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := cli.resolveAuthConfig(repoInfo.Index)
|
||||
authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, repoInfo.Index)
|
||||
|
||||
requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "push")
|
||||
if isTrusted() {
|
||||
@@ -72,5 +70,5 @@ func (cli *DockerCli) imagePushPrivileged(authConfig types.AuthConfig, imageID,
|
||||
RegistryAuth: encodedAuth,
|
||||
}
|
||||
|
||||
return cli.client.ImagePush(context.Background(), options, requestPrivilege)
|
||||
return cli.client.ImagePush(options, requestPrivilege)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -26,7 +24,7 @@ func (cli *DockerCli) CmdRename(args ...string) error {
|
||||
return fmt.Errorf("Error: Neither old nor new names may be empty")
|
||||
}
|
||||
|
||||
if err := cli.client.ContainerRename(context.Background(), oldName, newName); err != nil {
|
||||
if err := cli.client.ContainerRename(oldName, newName); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
return fmt.Errorf("Error: failed to rename container named %s", oldName)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -22,8 +20,8 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.ContainerRestart(context.Background(), name, *nSeconds); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := cli.client.ContainerRestart(name, *nSeconds); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to kill container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/engine-api/types"
|
||||
@@ -30,8 +28,15 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
||||
}
|
||||
name = strings.Trim(name, "/")
|
||||
|
||||
if err := cli.removeContainer(name, *v, *link, *force); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
options := types.ContainerRemoveOptions{
|
||||
ContainerID: name,
|
||||
RemoveVolumes: *v,
|
||||
RemoveLinks: *link,
|
||||
Force: *force,
|
||||
}
|
||||
|
||||
if err := cli.client.ContainerRemove(options); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to remove container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
@@ -41,16 +46,3 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) removeContainer(containerID string, removeVolumes, removeLinks, force bool) error {
|
||||
options := types.ContainerRemoveOptions{
|
||||
ContainerID: containerID,
|
||||
RemoveVolumes: removeVolumes,
|
||||
RemoveLinks: removeLinks,
|
||||
Force: force,
|
||||
}
|
||||
if err := cli.client.ContainerRemove(context.Background(), options); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/engine-api/types"
|
||||
@@ -39,9 +37,9 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
|
||||
PruneChildren: !*noprune,
|
||||
}
|
||||
|
||||
dels, err := cli.client.ImageRemove(context.Background(), options)
|
||||
dels, err := cli.client.ImageRemove(options)
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, fmt.Sprintf("Failed to remove image (%s): %s", name, err))
|
||||
} else {
|
||||
for _, del := range dels {
|
||||
if del.Deleted != "" {
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/promise"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
@@ -19,11 +18,6 @@ import (
|
||||
"github.com/docker/libnetwork/resolvconf/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
errCmdNotFound = "not found or does not exist."
|
||||
errCmdCouldNotBeInvoked = "could not be invoked."
|
||||
)
|
||||
|
||||
func (cid *cidFile) Close() error {
|
||||
cid.file.Close()
|
||||
|
||||
@@ -49,16 +43,21 @@ func (cid *cidFile) Write(id string) error {
|
||||
// return 125 for generic docker daemon failures
|
||||
func runStartContainerErr(err error) error {
|
||||
trimmedErr := strings.Trim(err.Error(), "Error response from daemon: ")
|
||||
statusError := Cli.StatusError{StatusCode: 125}
|
||||
|
||||
if strings.HasPrefix(trimmedErr, "Container command") {
|
||||
if strings.Contains(trimmedErr, errCmdNotFound) {
|
||||
statusError = Cli.StatusError{StatusCode: 127}
|
||||
} else if strings.Contains(trimmedErr, errCmdCouldNotBeInvoked) {
|
||||
statusError = Cli.StatusError{StatusCode: 126}
|
||||
}
|
||||
statusError := Cli.StatusError{}
|
||||
derrCmdNotFound := derr.ErrorCodeCmdNotFound.Message()
|
||||
derrCouldNotInvoke := derr.ErrorCodeCmdCouldNotBeInvoked.Message()
|
||||
derrNoSuchImage := derr.ErrorCodeNoSuchImageHash.Message()
|
||||
derrNoSuchImageTag := derr.ErrorCodeNoSuchImageTag.Message()
|
||||
switch trimmedErr {
|
||||
case derrCmdNotFound:
|
||||
statusError = Cli.StatusError{StatusCode: 127}
|
||||
case derrCouldNotInvoke:
|
||||
statusError = Cli.StatusError{StatusCode: 126}
|
||||
case derrNoSuchImage, derrNoSuchImageTag:
|
||||
statusError = Cli.StatusError{StatusCode: 125}
|
||||
default:
|
||||
statusError = Cli.StatusError{StatusCode: 125}
|
||||
}
|
||||
|
||||
return statusError
|
||||
}
|
||||
|
||||
@@ -204,31 +203,29 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
DetachKeys: cli.configFile.DetachKeys,
|
||||
}
|
||||
|
||||
resp, err := cli.client.ContainerAttach(context.Background(), options)
|
||||
resp, err := cli.client.ContainerAttach(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if in != nil && config.Tty {
|
||||
if err := cli.setRawTerminal(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.restoreTerminal(in)
|
||||
}
|
||||
errCh = promise.Go(func() error {
|
||||
return cli.holdHijackedConnection(config.Tty, in, out, stderr, resp)
|
||||
})
|
||||
}
|
||||
|
||||
if *flAutoRemove {
|
||||
defer func() {
|
||||
if err := cli.removeContainer(createResponse.ID, true, false, false); err != nil {
|
||||
fmt.Fprintf(cli.err, "%v\n", err)
|
||||
defer func() {
|
||||
if *flAutoRemove {
|
||||
options := types.ContainerRemoveOptions{
|
||||
ContainerID: createResponse.ID,
|
||||
RemoveVolumes: true,
|
||||
}
|
||||
}()
|
||||
}
|
||||
if err := cli.client.ContainerRemove(options); err != nil {
|
||||
fmt.Fprintf(cli.err, "Error deleting container: %s\n", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
//start the container
|
||||
if err := cli.client.ContainerStart(context.Background(), createResponse.ID); err != nil {
|
||||
if err := cli.client.ContainerStart(createResponse.ID); err != nil {
|
||||
cmd.ReportError(err.Error(), false)
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
@@ -259,7 +256,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
if *flAutoRemove {
|
||||
// Autoremove: wait for the container to finish, retrieve
|
||||
// the exit code and remove the container
|
||||
if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
|
||||
if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
|
||||
return runStartContainerErr(err)
|
||||
}
|
||||
if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
|
||||
@@ -269,7 +266,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
// No Autoremove: Simply retrieve the exit code
|
||||
if !config.Tty {
|
||||
// In non-TTY mode, we can't detach, so we must wait for container exit
|
||||
if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
|
||||
if status, err = cli.client.ContainerWait(createResponse.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -3,8 +3,7 @@ package client
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"os"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -22,21 +21,26 @@ func (cli *DockerCli) CmdSave(args ...string) error {
|
||||
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
var (
|
||||
output = cli.out
|
||||
err error
|
||||
)
|
||||
|
||||
if *outfile == "" && cli.isTerminalOut {
|
||||
return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
|
||||
}
|
||||
if *outfile != "" {
|
||||
if output, err = os.Create(*outfile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
responseBody, err := cli.client.ImageSave(context.Background(), cmd.Args())
|
||||
responseBody, err := cli.client.ImageSave(cmd.Args())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if *outfile == "" {
|
||||
_, err := io.Copy(cli.out, responseBody)
|
||||
return err
|
||||
}
|
||||
|
||||
return copyToFile(*outfile, responseBody)
|
||||
|
||||
_, err = io.Copy(output, responseBody)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/pkg/stringutils"
|
||||
@@ -38,7 +36,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
authConfig := cli.resolveAuthConfig(indexInfo)
|
||||
authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, indexInfo)
|
||||
requestPrivilege := cli.registryAuthenticationPrivilegedFunc(indexInfo, "search")
|
||||
|
||||
encodedAuth, err := encodeAuthToBase64(authConfig)
|
||||
@@ -51,7 +49,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
|
||||
RegistryAuth: encodedAuth,
|
||||
}
|
||||
|
||||
unorderedResults, err := cli.client.ImageSearch(context.Background(), options, requestPrivilege)
|
||||
unorderedResults, err := cli.client.ImageSearch(options, requestPrivilege)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
@@ -21,7 +19,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
|
||||
signal.CatchAll(sigc)
|
||||
go func() {
|
||||
for s := range sigc {
|
||||
if s == signal.SIGCHLD || s == signal.SIGPIPE {
|
||||
if s == signal.SIGCHLD {
|
||||
continue
|
||||
}
|
||||
var sig string
|
||||
@@ -36,7 +34,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := cli.client.ContainerKill(context.Background(), cid, sig); err != nil {
|
||||
if err := cli.client.ContainerKill(cid, sig); err != nil {
|
||||
logrus.Debugf("Error sending signal: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -65,7 +63,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||
|
||||
// 2. Attach to the container.
|
||||
containerID := cmd.Arg(0)
|
||||
c, err := cli.client.ContainerInspect(context.Background(), containerID)
|
||||
c, err := cli.client.ContainerInspect(containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,24 +91,18 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||
in = cli.in
|
||||
}
|
||||
|
||||
resp, err := cli.client.ContainerAttach(context.Background(), options)
|
||||
resp, err := cli.client.ContainerAttach(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Close()
|
||||
if in != nil && c.Config.Tty {
|
||||
if err := cli.setRawTerminal(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.restoreTerminal(in)
|
||||
}
|
||||
|
||||
cErr := promise.Go(func() error {
|
||||
return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp)
|
||||
})
|
||||
|
||||
// 3. Start the container.
|
||||
if err := cli.client.ContainerStart(context.Background(), containerID); err != nil {
|
||||
if err := cli.client.ContainerStart(containerID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -142,7 +134,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||
func (cli *DockerCli) startContainersWithoutAttachments(containerIDs []string) error {
|
||||
var failedContainers []string
|
||||
for _, containerID := range containerIDs {
|
||||
if err := cli.client.ContainerStart(context.Background(), containerID); err != nil {
|
||||
if err := cli.client.ContainerStart(containerID); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
failedContainers = append(failedContainers, containerID)
|
||||
} else {
|
||||
|
||||
@@ -1,21 +1,138 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/events"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
type containerStats struct {
|
||||
Name string
|
||||
CPUPercentage float64
|
||||
Memory float64
|
||||
MemoryLimit float64
|
||||
MemoryPercentage float64
|
||||
NetworkRx float64
|
||||
NetworkTx float64
|
||||
BlockRead float64
|
||||
BlockWrite float64
|
||||
mu sync.RWMutex
|
||||
err error
|
||||
}
|
||||
|
||||
type stats struct {
|
||||
mu sync.Mutex
|
||||
cs []*containerStats
|
||||
}
|
||||
|
||||
func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
|
||||
responseBody, err := cli.client.ContainerStats(s.Name, streamStats)
|
||||
if err != nil {
|
||||
s.mu.Lock()
|
||||
s.err = err
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
var (
|
||||
previousCPU uint64
|
||||
previousSystem uint64
|
||||
dec = json.NewDecoder(responseBody)
|
||||
u = make(chan error, 1)
|
||||
)
|
||||
go func() {
|
||||
for {
|
||||
var v *types.StatsJSON
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
u <- err
|
||||
return
|
||||
}
|
||||
|
||||
var memPercent = 0.0
|
||||
var cpuPercent = 0.0
|
||||
|
||||
// MemoryStats.Limit will never be 0 unless the container is not running and we haven't
|
||||
// got any data from cgroup
|
||||
if v.MemoryStats.Limit != 0 {
|
||||
memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
|
||||
}
|
||||
|
||||
previousCPU = v.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem = v.PreCPUStats.SystemUsage
|
||||
cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
|
||||
blkRead, blkWrite := calculateBlockIO(v.BlkioStats)
|
||||
s.mu.Lock()
|
||||
s.CPUPercentage = cpuPercent
|
||||
s.Memory = float64(v.MemoryStats.Usage)
|
||||
s.MemoryLimit = float64(v.MemoryStats.Limit)
|
||||
s.MemoryPercentage = memPercent
|
||||
s.NetworkRx, s.NetworkTx = calculateNetwork(v.Networks)
|
||||
s.BlockRead = float64(blkRead)
|
||||
s.BlockWrite = float64(blkWrite)
|
||||
s.mu.Unlock()
|
||||
u <- nil
|
||||
if !streamStats {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
// zero out the values if we have not received an update within
|
||||
// the specified duration.
|
||||
s.mu.Lock()
|
||||
s.CPUPercentage = 0
|
||||
s.Memory = 0
|
||||
s.MemoryPercentage = 0
|
||||
s.MemoryLimit = 0
|
||||
s.NetworkRx = 0
|
||||
s.NetworkTx = 0
|
||||
s.BlockRead = 0
|
||||
s.BlockWrite = 0
|
||||
s.mu.Unlock()
|
||||
case err := <-u:
|
||||
if err != nil {
|
||||
s.mu.Lock()
|
||||
s.err = err
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
if !streamStats {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *containerStats) Display(w io.Writer) error {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if s.err != nil {
|
||||
return s.err
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%.2f%%\t%s / %s\t%.2f%%\t%s / %s\t%s / %s\n",
|
||||
s.Name,
|
||||
s.CPUPercentage,
|
||||
units.HumanSize(s.Memory), units.HumanSize(s.MemoryLimit),
|
||||
s.MemoryPercentage,
|
||||
units.HumanSize(s.NetworkRx), units.HumanSize(s.NetworkTx),
|
||||
units.HumanSize(s.BlockRead), units.HumanSize(s.BlockWrite))
|
||||
return nil
|
||||
}
|
||||
|
||||
// CmdStats displays a live stream of resource usage statistics for one or more containers.
|
||||
//
|
||||
// This shows real-time information on CPU usage, memory usage, and network I/O.
|
||||
@@ -30,143 +147,125 @@ func (cli *DockerCli) CmdStats(args ...string) error {
|
||||
|
||||
names := cmd.Args()
|
||||
showAll := len(names) == 0
|
||||
closeChan := make(chan error)
|
||||
|
||||
// monitorContainerEvents watches for container creation and removal (only
|
||||
// used when calling `docker stats` without arguments).
|
||||
monitorContainerEvents := func(started chan<- struct{}, c chan events.Message) {
|
||||
f := filters.NewArgs()
|
||||
f.Add("type", "container")
|
||||
options := types.EventsOptions{
|
||||
Filters: f,
|
||||
}
|
||||
resBody, err := cli.client.Events(context.Background(), options)
|
||||
// Whether we successfully subscribed to events or not, we can now
|
||||
// unblock the main goroutine.
|
||||
close(started)
|
||||
if err != nil {
|
||||
closeChan <- err
|
||||
return
|
||||
}
|
||||
defer resBody.Close()
|
||||
|
||||
decodeEvents(resBody, func(event events.Message, err error) error {
|
||||
if err != nil {
|
||||
closeChan <- err
|
||||
return nil
|
||||
}
|
||||
c <- event
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// waitFirst is a WaitGroup to wait first stat data's reach for each container
|
||||
waitFirst := &sync.WaitGroup{}
|
||||
|
||||
cStats := stats{}
|
||||
// getContainerList simulates creation event for all previously existing
|
||||
// containers (only used when calling `docker stats` without arguments).
|
||||
getContainerList := func() {
|
||||
if showAll {
|
||||
options := types.ContainerListOptions{
|
||||
All: *all,
|
||||
}
|
||||
cs, err := cli.client.ContainerList(context.Background(), options)
|
||||
cs, err := cli.client.ContainerList(options)
|
||||
if err != nil {
|
||||
closeChan <- err
|
||||
return err
|
||||
}
|
||||
for _, container := range cs {
|
||||
s := &containerStats{Name: container.ID[:12]}
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go s.Collect(cli.client, !*noStream, waitFirst)
|
||||
}
|
||||
for _, c := range cs {
|
||||
names = append(names, c.ID[:12])
|
||||
}
|
||||
}
|
||||
|
||||
if showAll {
|
||||
// If no names were specified, start a long running goroutine which
|
||||
// monitors container events. We make sure we're subscribed before
|
||||
// retrieving the list of running containers to avoid a race where we
|
||||
// would "miss" a creation.
|
||||
started := make(chan struct{})
|
||||
eh := eventHandler{handlers: make(map[string]func(events.Message))}
|
||||
eh.Handle("create", func(e events.Message) {
|
||||
if *all {
|
||||
s := &containerStats{Name: e.ID[:12]}
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go s.Collect(cli.client, !*noStream, waitFirst)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
eh.Handle("start", func(e events.Message) {
|
||||
s := &containerStats{Name: e.ID[:12]}
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go s.Collect(cli.client, !*noStream, waitFirst)
|
||||
}
|
||||
})
|
||||
|
||||
eh.Handle("die", func(e events.Message) {
|
||||
if !*all {
|
||||
cStats.remove(e.ID[:12])
|
||||
}
|
||||
})
|
||||
|
||||
eventChan := make(chan events.Message)
|
||||
go eh.Watch(eventChan)
|
||||
go monitorContainerEvents(started, eventChan)
|
||||
defer close(eventChan)
|
||||
<-started
|
||||
|
||||
// Start a short-lived goroutine to retrieve the initial list of
|
||||
// containers.
|
||||
getContainerList()
|
||||
} else {
|
||||
// Artificially send creation events for the containers we were asked to
|
||||
// monitor (same code path than we use when monitoring all containers).
|
||||
for _, name := range names {
|
||||
s := &containerStats{Name: name}
|
||||
if cStats.add(s) {
|
||||
waitFirst.Add(1)
|
||||
go s.Collect(cli.client, !*noStream, waitFirst)
|
||||
}
|
||||
}
|
||||
|
||||
// We don't expect any asynchronous errors: closeChan can be closed.
|
||||
close(closeChan)
|
||||
|
||||
// Do a quick pause to detect any error with the provided list of
|
||||
// container names.
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
var errs []string
|
||||
cStats.mu.Lock()
|
||||
for _, c := range cStats.cs {
|
||||
c.mu.Lock()
|
||||
if c.err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", c.Name, c.err))
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
cStats.mu.Unlock()
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, ", "))
|
||||
}
|
||||
if len(names) == 0 && !showAll {
|
||||
return fmt.Errorf("No containers found")
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
// before print to screen, make sure each container get at least one valid stat data
|
||||
waitFirst.Wait()
|
||||
|
||||
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
var (
|
||||
cStats = stats{}
|
||||
w = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
|
||||
)
|
||||
printHeader := func() {
|
||||
if !*noStream {
|
||||
fmt.Fprint(cli.out, "\033[2J")
|
||||
fmt.Fprint(cli.out, "\033[H")
|
||||
}
|
||||
io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE / LIMIT\tMEM %\tNET I/O\tBLOCK I/O\tPIDS\n")
|
||||
io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE / LIMIT\tMEM %\tNET I/O\tBLOCK I/O\n")
|
||||
}
|
||||
for _, n := range names {
|
||||
s := &containerStats{Name: n}
|
||||
// no need to lock here since only the main goroutine is running here
|
||||
cStats.cs = append(cStats.cs, s)
|
||||
go s.Collect(cli, !*noStream)
|
||||
}
|
||||
closeChan := make(chan error)
|
||||
if showAll {
|
||||
type watch struct {
|
||||
cid string
|
||||
event string
|
||||
err error
|
||||
}
|
||||
getNewContainers := func(c chan<- watch) {
|
||||
f := filters.NewArgs()
|
||||
f.Add("type", "container")
|
||||
options := types.EventsOptions{
|
||||
Filters: f,
|
||||
}
|
||||
resBody, err := cli.client.Events(options)
|
||||
if err != nil {
|
||||
c <- watch{err: err}
|
||||
return
|
||||
}
|
||||
defer resBody.Close()
|
||||
|
||||
decodeEvents(resBody, func(event events.Message, err error) error {
|
||||
if err != nil {
|
||||
c <- watch{err: err}
|
||||
return nil
|
||||
}
|
||||
|
||||
c <- watch{event.ID[:12], event.Action, nil}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
go func(stopChan chan<- error) {
|
||||
cChan := make(chan watch)
|
||||
go getNewContainers(cChan)
|
||||
for {
|
||||
c := <-cChan
|
||||
if c.err != nil {
|
||||
stopChan <- c.err
|
||||
return
|
||||
}
|
||||
switch c.event {
|
||||
case "create":
|
||||
s := &containerStats{Name: c.cid}
|
||||
cStats.mu.Lock()
|
||||
cStats.cs = append(cStats.cs, s)
|
||||
cStats.mu.Unlock()
|
||||
go s.Collect(cli, !*noStream)
|
||||
case "stop":
|
||||
case "die":
|
||||
if !*all {
|
||||
var remove int
|
||||
// cStats cannot be O(1) with a map cause ranging over it would cause
|
||||
// containers in stats to move up and down in the list...:(
|
||||
cStats.mu.Lock()
|
||||
for i, s := range cStats.cs {
|
||||
if s.Name == c.cid {
|
||||
remove = i
|
||||
break
|
||||
}
|
||||
}
|
||||
cStats.cs = append(cStats.cs[:remove], cStats.cs[remove+1:]...)
|
||||
cStats.mu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}(closeChan)
|
||||
} else {
|
||||
close(closeChan)
|
||||
}
|
||||
// do a quick pause so that any failed connections for containers that do not exist are able to be
|
||||
// evicted before we display the initial or default values.
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
var errs []string
|
||||
cStats.mu.Lock()
|
||||
for _, c := range cStats.cs {
|
||||
c.mu.Lock()
|
||||
if c.err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", c.Name, c.err))
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
cStats.mu.Unlock()
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, ", "))
|
||||
}
|
||||
for range time.Tick(500 * time.Millisecond) {
|
||||
printHeader()
|
||||
toRemove := []int{}
|
||||
@@ -206,3 +305,40 @@ func (cli *DockerCli) CmdStats(args ...string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateCPUPercent(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
|
||||
var (
|
||||
cpuPercent = 0.0
|
||||
// calculate the change for the cpu usage of the container in between readings
|
||||
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
|
||||
// calculate the change for the entire system between readings
|
||||
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
|
||||
)
|
||||
|
||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead uint64, blkWrite uint64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
case "read":
|
||||
blkRead = blkRead + bioEntry.Value
|
||||
case "write":
|
||||
blkWrite = blkWrite + bioEntry.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func calculateNetwork(network map[string]types.NetworkStats) (float64, float64) {
|
||||
var rx, tx float64
|
||||
|
||||
for _, v := range network {
|
||||
rx += float64(v.RxBytes)
|
||||
tx += float64(v.TxBytes)
|
||||
}
|
||||
return rx, tx
|
||||
}
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/go-units"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type containerStats struct {
|
||||
Name string
|
||||
CPUPercentage float64
|
||||
Memory float64
|
||||
MemoryLimit float64
|
||||
MemoryPercentage float64
|
||||
NetworkRx float64
|
||||
NetworkTx float64
|
||||
BlockRead float64
|
||||
BlockWrite float64
|
||||
PidsCurrent uint64
|
||||
mu sync.RWMutex
|
||||
err error
|
||||
}
|
||||
|
||||
type stats struct {
|
||||
mu sync.Mutex
|
||||
cs []*containerStats
|
||||
}
|
||||
|
||||
func (s *stats) add(cs *containerStats) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if _, exists := s.isKnownContainer(cs.Name); !exists {
|
||||
s.cs = append(s.cs, cs)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *stats) remove(id string) {
|
||||
s.mu.Lock()
|
||||
if i, exists := s.isKnownContainer(id); exists {
|
||||
s.cs = append(s.cs[:i], s.cs[i+1:]...)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *stats) isKnownContainer(cid string) (int, bool) {
|
||||
for i, c := range s.cs {
|
||||
if c.Name == cid {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFirst *sync.WaitGroup) {
|
||||
var (
|
||||
getFirst bool
|
||||
previousCPU uint64
|
||||
previousSystem uint64
|
||||
u = make(chan error, 1)
|
||||
)
|
||||
|
||||
defer func() {
|
||||
// if error happens and we get nothing of stats, release wait group whatever
|
||||
if !getFirst {
|
||||
getFirst = true
|
||||
waitFirst.Done()
|
||||
}
|
||||
}()
|
||||
|
||||
responseBody, err := cli.ContainerStats(context.Background(), s.Name, streamStats)
|
||||
if err != nil {
|
||||
s.mu.Lock()
|
||||
s.err = err
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
dec := json.NewDecoder(responseBody)
|
||||
go func() {
|
||||
for {
|
||||
var v *types.StatsJSON
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
u <- err
|
||||
return
|
||||
}
|
||||
|
||||
var memPercent = 0.0
|
||||
var cpuPercent = 0.0
|
||||
|
||||
// MemoryStats.Limit will never be 0 unless the container is not running and we haven't
|
||||
// got any data from cgroup
|
||||
if v.MemoryStats.Limit != 0 {
|
||||
memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
|
||||
}
|
||||
|
||||
previousCPU = v.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem = v.PreCPUStats.SystemUsage
|
||||
cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
|
||||
blkRead, blkWrite := calculateBlockIO(v.BlkioStats)
|
||||
s.mu.Lock()
|
||||
s.CPUPercentage = cpuPercent
|
||||
s.Memory = float64(v.MemoryStats.Usage)
|
||||
s.MemoryLimit = float64(v.MemoryStats.Limit)
|
||||
s.MemoryPercentage = memPercent
|
||||
s.NetworkRx, s.NetworkTx = calculateNetwork(v.Networks)
|
||||
s.BlockRead = float64(blkRead)
|
||||
s.BlockWrite = float64(blkWrite)
|
||||
s.PidsCurrent = v.PidsStats.Current
|
||||
s.mu.Unlock()
|
||||
u <- nil
|
||||
if !streamStats {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
// zero out the values if we have not received an update within
|
||||
// the specified duration.
|
||||
s.mu.Lock()
|
||||
s.CPUPercentage = 0
|
||||
s.Memory = 0
|
||||
s.MemoryPercentage = 0
|
||||
s.MemoryLimit = 0
|
||||
s.NetworkRx = 0
|
||||
s.NetworkTx = 0
|
||||
s.BlockRead = 0
|
||||
s.BlockWrite = 0
|
||||
s.PidsCurrent = 0
|
||||
s.mu.Unlock()
|
||||
// if this is the first stat you get, release WaitGroup
|
||||
if !getFirst {
|
||||
getFirst = true
|
||||
waitFirst.Done()
|
||||
}
|
||||
case err := <-u:
|
||||
if err != nil {
|
||||
s.mu.Lock()
|
||||
s.err = err
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
// if this is the first stat you get, release WaitGroup
|
||||
if !getFirst {
|
||||
getFirst = true
|
||||
waitFirst.Done()
|
||||
}
|
||||
}
|
||||
if !streamStats {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *containerStats) Display(w io.Writer) error {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if s.err != nil {
|
||||
return s.err
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%.2f%%\t%s / %s\t%.2f%%\t%s / %s\t%s / %s\t%d\n",
|
||||
s.Name,
|
||||
s.CPUPercentage,
|
||||
units.HumanSize(s.Memory), units.HumanSize(s.MemoryLimit),
|
||||
s.MemoryPercentage,
|
||||
units.HumanSize(s.NetworkRx), units.HumanSize(s.NetworkTx),
|
||||
units.HumanSize(s.BlockRead), units.HumanSize(s.BlockWrite),
|
||||
s.PidsCurrent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateCPUPercent(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
|
||||
var (
|
||||
cpuPercent = 0.0
|
||||
// calculate the change for the cpu usage of the container in between readings
|
||||
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
|
||||
// calculate the change for the entire system between readings
|
||||
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
|
||||
)
|
||||
|
||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead uint64, blkWrite uint64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
case "read":
|
||||
blkRead = blkRead + bioEntry.Value
|
||||
case "write":
|
||||
blkWrite = blkWrite + bioEntry.Value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func calculateNetwork(network map[string]types.NetworkStats) (float64, float64) {
|
||||
var rx, tx float64
|
||||
|
||||
for _, v := range network {
|
||||
rx += float64(v.RxBytes)
|
||||
tx += float64(v.TxBytes)
|
||||
}
|
||||
return rx, tx
|
||||
}
|
||||
@@ -19,7 +19,6 @@ func TestDisplay(t *testing.T) {
|
||||
NetworkTx: 800 * 1024 * 1024,
|
||||
BlockRead: 100 * 1024 * 1024,
|
||||
BlockWrite: 800 * 1024 * 1024,
|
||||
PidsCurrent: 1,
|
||||
mu: sync.RWMutex{},
|
||||
}
|
||||
var b bytes.Buffer
|
||||
@@ -27,7 +26,7 @@ func TestDisplay(t *testing.T) {
|
||||
t.Fatalf("c.Display() gave error: %s", err)
|
||||
}
|
||||
got := b.String()
|
||||
want := "app\t30.00%\t104.9 MB / 2.147 GB\t4.88%\t104.9 MB / 838.9 MB\t104.9 MB / 838.9 MB\t1\n"
|
||||
want := "app\t30.00%\t104.9 MB / 2.147 GB\t4.88%\t104.9 MB / 838.9 MB\t104.9 MB / 838.9 MB\n"
|
||||
if got != want {
|
||||
t.Fatalf("c.Display() = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -24,8 +22,8 @@ func (cli *DockerCli) CmdStop(args ...string) error {
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.ContainerStop(context.Background(), name, *nSeconds); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := cli.client.ContainerStop(name, *nSeconds); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to stop container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package client
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/reference"
|
||||
@@ -42,5 +40,5 @@ func (cli *DockerCli) CmdTag(args ...string) error {
|
||||
Force: *force,
|
||||
}
|
||||
|
||||
return cli.client.ImageTag(context.Background(), options)
|
||||
return cli.client.ImageTag(options)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -25,7 +23,7 @@ func (cli *DockerCli) CmdTop(args ...string) error {
|
||||
arguments = cmd.Args()[1:]
|
||||
}
|
||||
|
||||
procList, err := cli.client.ContainerTop(context.Background(), cmd.Arg(0), arguments)
|
||||
procList, err := cli.client.ContainerTop(cmd.Arg(0), arguments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
@@ -108,17 +106,7 @@ func (scs simpleCredentialStore) Basic(u *url.URL) (string, string) {
|
||||
return scs.auth.Username, scs.auth.Password
|
||||
}
|
||||
|
||||
func (scs simpleCredentialStore) RefreshToken(u *url.URL, service string) string {
|
||||
return scs.auth.IdentityToken
|
||||
}
|
||||
|
||||
func (scs simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {
|
||||
}
|
||||
|
||||
// getNotaryRepository returns a NotaryRepository which stores all the
|
||||
// information needed to operate on a notary repository.
|
||||
// It creates a HTTP transport providing authentication support.
|
||||
func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, authConfig types.AuthConfig, actions ...string) (*client.NotaryRepository, error) {
|
||||
func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, authConfig types.AuthConfig) (*client.NotaryRepository, error) {
|
||||
server, err := trustServer(repoInfo.Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -151,7 +139,7 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut
|
||||
}
|
||||
|
||||
// Skip configuration headers since request is not going to Docker daemon
|
||||
modifiers := registry.DockerHeaders(clientUserAgent(), http.Header{})
|
||||
modifiers := registry.DockerHeaders(http.Header{})
|
||||
authTransport := transport.NewTransport(base, modifiers...)
|
||||
pingClient := &http.Client{
|
||||
Transport: authTransport,
|
||||
@@ -180,7 +168,7 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut
|
||||
}
|
||||
|
||||
creds := simpleCredentialStore{auth: authConfig}
|
||||
tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), actions...)
|
||||
tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), "push", "pull")
|
||||
basicHandler := auth.NewBasicHandler(creds)
|
||||
modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
|
||||
tr := transport.NewTransport(base, modifiers...)
|
||||
@@ -202,17 +190,17 @@ func convertTarget(t client.Target) (target, error) {
|
||||
|
||||
func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
|
||||
aliasMap := map[string]string{
|
||||
"root": "root",
|
||||
"snapshot": "repository",
|
||||
"targets": "repository",
|
||||
"default": "repository",
|
||||
"root": "root",
|
||||
"snapshot": "repository",
|
||||
"targets": "repository",
|
||||
"targets/releases": "repository",
|
||||
}
|
||||
baseRetriever := passphrase.PromptRetrieverWithInOut(cli.in, cli.out, aliasMap)
|
||||
env := map[string]string{
|
||||
"root": os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"),
|
||||
"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
|
||||
"targets": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
|
||||
"default": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
|
||||
"root": os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"),
|
||||
"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
|
||||
"targets": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
|
||||
"targets/releases": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
|
||||
}
|
||||
|
||||
// Backwards compatibility with old env names. We should remove this in 1.10
|
||||
@@ -222,11 +210,11 @@ func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The environment variable DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE has been deprecated and will be removed in v1.10. Please use DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE\n")
|
||||
}
|
||||
}
|
||||
if env["snapshot"] == "" || env["targets"] == "" || env["default"] == "" {
|
||||
if env["snapshot"] == "" || env["targets"] == "" || env["targets/releases"] == "" {
|
||||
if passphrase := os.Getenv("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE"); passphrase != "" {
|
||||
env["snapshot"] = passphrase
|
||||
env["targets"] = passphrase
|
||||
env["default"] = passphrase
|
||||
env["targets/releases"] = passphrase
|
||||
fmt.Fprintf(cli.err, "[DEPRECATED] The environment variable DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE has been deprecated and will be removed in v1.10. Please use DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE\n")
|
||||
}
|
||||
}
|
||||
@@ -235,10 +223,6 @@ func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
|
||||
if v := env[alias]; v != "" {
|
||||
return v, numAttempts > 1, nil
|
||||
}
|
||||
// For non-root roles, we can also try the "default" alias if it is specified
|
||||
if v := env["default"]; v != "" && alias != data.CanonicalRootRole {
|
||||
return v, numAttempts > 1, nil
|
||||
}
|
||||
return baseRetriever(keyName, alias, createNew, numAttempts)
|
||||
}
|
||||
}
|
||||
@@ -250,9 +234,9 @@ func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Can
|
||||
}
|
||||
|
||||
// Resolve the Auth config relevant for this server
|
||||
authConfig := cli.resolveAuthConfig(repoInfo.Index)
|
||||
authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, repoInfo.Index)
|
||||
|
||||
notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
|
||||
notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.out, "Error establishing connection to trust repository: %s\n", err)
|
||||
return nil, err
|
||||
@@ -262,11 +246,6 @@ func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Can
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Only list tags in the top level targets role or the releases delegation role - ignore
|
||||
// all other delegation roles
|
||||
if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole {
|
||||
return nil, notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
|
||||
}
|
||||
r, err := convertTarget(t.Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -286,7 +265,7 @@ func (cli *DockerCli) tagTrusted(trustedRef reference.Canonical, ref reference.N
|
||||
Force: true,
|
||||
}
|
||||
|
||||
return cli.client.ImageTag(context.Background(), options)
|
||||
return cli.client.ImageTag(options)
|
||||
}
|
||||
|
||||
func notaryError(repoName string, err error) error {
|
||||
@@ -322,7 +301,7 @@ func notaryError(repoName string, err error) error {
|
||||
func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege apiclient.RequestPrivilegeFunc) error {
|
||||
var refs []target
|
||||
|
||||
notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
|
||||
notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.out, "Error establishing connection to trust repository: %s\n", err)
|
||||
return err
|
||||
@@ -340,28 +319,13 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
|
||||
fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.Name())
|
||||
continue
|
||||
}
|
||||
// Only list tags in the top level targets role or the releases delegation role - ignore
|
||||
// all other delegation roles
|
||||
if tgt.Role != releasesRole && tgt.Role != data.CanonicalTargetsRole {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, t)
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
return notaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName()))
|
||||
}
|
||||
} else {
|
||||
t, err := notaryRepo.GetTargetByName(ref.String(), releasesRole, data.CanonicalTargetsRole)
|
||||
if err != nil {
|
||||
return notaryError(repoInfo.FullName(), err)
|
||||
}
|
||||
// Only get the tag if it's in the top level targets role or the releases delegation role
|
||||
// ignore it if it's in any other delegation roles
|
||||
if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole {
|
||||
return notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.String()))
|
||||
}
|
||||
|
||||
logrus.Debugf("retrieving target for %s role\n", t.Role)
|
||||
r, err := convertTarget(t.Target)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -407,153 +371,82 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string,
|
||||
|
||||
defer responseBody.Close()
|
||||
|
||||
// If it is a trusted push we would like to find the target entry which match the
|
||||
// tag provided in the function and then do an AddTarget later.
|
||||
target := &client.Target{}
|
||||
// Count the times of calling for handleTarget,
|
||||
// if it is called more that once, that should be considered an error in a trusted push.
|
||||
cnt := 0
|
||||
targets := []target{}
|
||||
handleTarget := func(aux *json.RawMessage) {
|
||||
cnt++
|
||||
if cnt > 1 {
|
||||
// handleTarget should only be called one. This will be treated as an error.
|
||||
return
|
||||
}
|
||||
|
||||
var pushResult distribution.PushResult
|
||||
err := json.Unmarshal(*aux, &pushResult)
|
||||
if err == nil && pushResult.Tag != "" && pushResult.Digest.Validate() == nil {
|
||||
h, err := hex.DecodeString(pushResult.Digest.Hex())
|
||||
if err != nil {
|
||||
target = nil
|
||||
return
|
||||
}
|
||||
target.Name = registry.ParseReference(pushResult.Tag).String()
|
||||
target.Hashes = data.Hashes{string(pushResult.Digest.Algorithm()): h}
|
||||
target.Length = int64(pushResult.Size)
|
||||
targets = append(targets, target{
|
||||
reference: registry.ParseReference(pushResult.Tag),
|
||||
digest: pushResult.Digest,
|
||||
size: int64(pushResult.Size),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// We want trust signatures to always take an explicit tag,
|
||||
// otherwise it will act as an untrusted push.
|
||||
if tag == "" {
|
||||
if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(cli.out, "No tag specified, skipping trust metadata push")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, handleTarget); err != nil {
|
||||
err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, handleTarget)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cnt > 1 {
|
||||
return fmt.Errorf("internal error: only one call to handleTarget expected")
|
||||
if tag == "" {
|
||||
fmt.Fprintf(cli.out, "No tag specified, skipping trust metadata push\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
fmt.Fprintln(cli.out, "No targets found, please provide a specific tag in order to sign it")
|
||||
if len(targets) == 0 {
|
||||
fmt.Fprintf(cli.out, "No targets found, skipping trust metadata push\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintln(cli.out, "Signing and pushing trust metadata")
|
||||
fmt.Fprintf(cli.out, "Signing and pushing trust metadata\n")
|
||||
|
||||
repo, err := cli.getNotaryRepository(repoInfo, authConfig, "push", "pull")
|
||||
repo, err := cli.getNotaryRepository(repoInfo, authConfig)
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.out, "Error establishing connection to notary repository: %s\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// get the latest repository metadata so we can figure out which roles to sign
|
||||
_, err = repo.Update(false)
|
||||
|
||||
switch err.(type) {
|
||||
case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:
|
||||
keys := repo.CryptoService.ListKeys(data.CanonicalRootRole)
|
||||
var rootKeyID string
|
||||
// always select the first root key
|
||||
if len(keys) > 0 {
|
||||
sort.Strings(keys)
|
||||
rootKeyID = keys[0]
|
||||
} else {
|
||||
rootPublicKey, err := repo.CryptoService.Create(data.CanonicalRootRole, "", data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootKeyID = rootPublicKey.ID()
|
||||
for _, target := range targets {
|
||||
h, err := hex.DecodeString(target.digest.Hex())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize the notary repository with a remotely managed snapshot key
|
||||
if err := repo.Initialize(rootKeyID, data.CanonicalSnapshotRole); err != nil {
|
||||
return notaryError(repoInfo.FullName(), err)
|
||||
t := &client.Target{
|
||||
Name: target.reference.String(),
|
||||
Hashes: data.Hashes{
|
||||
string(target.digest.Algorithm()): h,
|
||||
},
|
||||
Length: int64(target.size),
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName())
|
||||
err = repo.AddTarget(target, data.CanonicalTargetsRole)
|
||||
case nil:
|
||||
// already initialized and we have successfully downloaded the latest metadata
|
||||
err = cli.addTargetToAllSignableRoles(repo, target)
|
||||
default:
|
||||
if err := repo.AddTarget(t, releasesRole); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = repo.Publish()
|
||||
if _, ok := err.(client.ErrRepoNotInitialized); !ok {
|
||||
return notaryError(repoInfo.FullName(), err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = repo.Publish()
|
||||
keys := repo.CryptoService.ListKeys(data.CanonicalRootRole)
|
||||
|
||||
var rootKeyID string
|
||||
// always select the first root key
|
||||
if len(keys) > 0 {
|
||||
sort.Strings(keys)
|
||||
rootKeyID = keys[0]
|
||||
} else {
|
||||
rootPublicKey, err := repo.CryptoService.Create(data.CanonicalRootRole, data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootKeyID = rootPublicKey.ID()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.out, "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error())
|
||||
if err := repo.Initialize(rootKeyID); err != nil {
|
||||
return notaryError(repoInfo.FullName(), err)
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName())
|
||||
|
||||
fmt.Fprintf(cli.out, "Successfully signed %q:%s\n", repoInfo.FullName(), tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attempt to add the image target to all the top level delegation roles we can
|
||||
// (based on whether we have the signing key and whether the role's path allows
|
||||
// us to).
|
||||
// If there are no delegation roles, we add to the targets role.
|
||||
func (cli *DockerCli) addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.Target) error {
|
||||
var signableRoles []string
|
||||
|
||||
// translate the full key names, which includes the GUN, into just the key IDs
|
||||
allCanonicalKeyIDs := make(map[string]struct{})
|
||||
for fullKeyID := range repo.CryptoService.ListAllKeys() {
|
||||
allCanonicalKeyIDs[path.Base(fullKeyID)] = struct{}{}
|
||||
}
|
||||
|
||||
allDelegationRoles, err := repo.GetDelegationRoles()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if there are no delegation roles, then just try to sign it into the targets role
|
||||
if len(allDelegationRoles) == 0 {
|
||||
return repo.AddTarget(target, data.CanonicalTargetsRole)
|
||||
}
|
||||
|
||||
// there are delegation roles, find every delegation role we have a key for, and
|
||||
// attempt to sign into into all those roles.
|
||||
for _, delegationRole := range allDelegationRoles {
|
||||
// We do not support signing any delegation role that isn't a direct child of the targets role.
|
||||
// Also don't bother checking the keys if we can't add the target
|
||||
// to this role due to path restrictions
|
||||
if path.Dir(delegationRole.Name) != data.CanonicalTargetsRole || !delegationRole.CheckPaths(target.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, canonicalKeyID := range delegationRole.KeyIDs {
|
||||
if _, ok := allCanonicalKeyIDs[canonicalKeyID]; ok {
|
||||
signableRoles = append(signableRoles, delegationRole.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(signableRoles) == 0 {
|
||||
return fmt.Errorf("no valid signing keys for delegation roles")
|
||||
}
|
||||
|
||||
return repo.AddTarget(target, signableRoles...)
|
||||
return notaryError(repoInfo.FullName(), repo.Publish())
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -21,8 +19,8 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.ContainerUnpause(context.Background(), name); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := cli.client.ContainerUnpause(name); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to unpause container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
@@ -26,9 +23,8 @@ func (cli *DockerCli) CmdUpdate(args ...string) error {
|
||||
flCPUShares := cmd.Int64([]string{"#c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
|
||||
flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit")
|
||||
flMemoryReservation := cmd.String([]string{"-memory-reservation"}, "", "Memory soft limit")
|
||||
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||
flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap")
|
||||
flKernelMemory := cmd.String([]string{"-kernel-memory"}, "", "Kernel memory limit")
|
||||
flRestartPolicy := cmd.String([]string{"-restart"}, "", "Restart policy to apply when a container exits")
|
||||
|
||||
cmd.Require(flag.Min, 1)
|
||||
cmd.ParseFlags(args, true)
|
||||
@@ -73,14 +69,6 @@ func (cli *DockerCli) CmdUpdate(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
var restartPolicy container.RestartPolicy
|
||||
if *flRestartPolicy != "" {
|
||||
restartPolicy, err = opts.ParseRestartPolicy(*flRestartPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
resources := container.Resources{
|
||||
BlkioWeight: *flBlkioWeight,
|
||||
CpusetCpus: *flCpusetCpus,
|
||||
@@ -95,15 +83,14 @@ func (cli *DockerCli) CmdUpdate(args ...string) error {
|
||||
}
|
||||
|
||||
updateConfig := container.UpdateConfig{
|
||||
Resources: resources,
|
||||
RestartPolicy: restartPolicy,
|
||||
Resources: resources,
|
||||
}
|
||||
|
||||
names := cmd.Args()
|
||||
var errs []string
|
||||
for _, name := range names {
|
||||
if err := cli.client.ContainerUpdate(context.Background(), name, updateConfig); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := cli.client.ContainerUpdate(name, updateConfig); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to update container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%s\n", name)
|
||||
}
|
||||
|
||||
@@ -4,16 +4,11 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
gosignal "os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
@@ -23,20 +18,6 @@ import (
|
||||
registrytypes "github.com/docker/engine-api/types/registry"
|
||||
)
|
||||
|
||||
func (cli *DockerCli) electAuthServer() string {
|
||||
// The daemon `/info` endpoint informs us of the default registry being
|
||||
// used. This is essential in cross-platforms environment, where for
|
||||
// example a Linux client might be interacting with a Windows daemon, hence
|
||||
// the default registry URL might be Windows specific.
|
||||
serverAddress := registry.IndexServer
|
||||
if info, err := cli.client.Info(context.Background()); err != nil {
|
||||
fmt.Fprintf(cli.out, "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
|
||||
} else {
|
||||
serverAddress = info.IndexServerAddress
|
||||
}
|
||||
return serverAddress
|
||||
}
|
||||
|
||||
// encodeAuthToBase64 serializes the auth configuration as JSON base64 payload
|
||||
func encodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
|
||||
buf, err := json.Marshal(authConfig)
|
||||
@@ -46,24 +27,23 @@ func encodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) encodeRegistryAuth(index *registrytypes.IndexInfo) (string, error) {
|
||||
authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, index)
|
||||
return encodeAuthToBase64(authConfig)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) client.RequestPrivilegeFunc {
|
||||
return func() (string, error) {
|
||||
fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
|
||||
indexServer := registry.GetAuthConfigKey(index)
|
||||
authConfig, err := cli.configureAuth("", "", indexServer, false)
|
||||
if err != nil {
|
||||
if err := cli.CmdLogin(registry.GetAuthConfigKey(index)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return encodeAuthToBase64(authConfig)
|
||||
return cli.encodeRegistryAuth(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) resizeTty(id string, isExec bool) {
|
||||
height, width := cli.getTtySize()
|
||||
cli.resizeTtyTo(id, height, width, isExec)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
|
||||
if height == 0 && width == 0 {
|
||||
return
|
||||
}
|
||||
@@ -76,9 +56,9 @@ func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
|
||||
|
||||
var err error
|
||||
if isExec {
|
||||
err = cli.client.ContainerExecResize(context.Background(), options)
|
||||
err = cli.client.ContainerExecResize(options)
|
||||
} else {
|
||||
err = cli.client.ContainerResize(context.Background(), options)
|
||||
err = cli.client.ContainerResize(options)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -89,7 +69,7 @@ func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
|
||||
// getExitCode perform an inspect on the container. It returns
|
||||
// the running state and the exit code.
|
||||
func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
|
||||
c, err := cli.client.ContainerInspect(context.Background(), containerID)
|
||||
c, err := cli.client.ContainerInspect(containerID)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != client.ErrConnectionFailed {
|
||||
@@ -104,7 +84,7 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
|
||||
// getExecExitCode perform an inspect on the exec command. It returns
|
||||
// the running state and the exit code.
|
||||
func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
|
||||
resp, err := cli.client.ContainerExecInspect(context.Background(), execID)
|
||||
resp, err := cli.client.ContainerExecInspect(execID)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != client.ErrConnectionFailed {
|
||||
@@ -158,45 +138,3 @@ func (cli *DockerCli) getTtySize() (int, int) {
|
||||
}
|
||||
return int(ws.Height), int(ws.Width)
|
||||
}
|
||||
|
||||
func copyToFile(outfile string, r io.Reader) error {
|
||||
tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpPath := tmpFile.Name()
|
||||
|
||||
_, err = io.Copy(tmpFile, r)
|
||||
tmpFile.Close()
|
||||
|
||||
if err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.Rename(tmpPath, outfile); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveAuthConfig is like registry.ResolveAuthConfig, but if using the
|
||||
// default index, it uses the default index name for the daemon's platform,
|
||||
// not the client's platform.
|
||||
func (cli *DockerCli) resolveAuthConfig(index *registrytypes.IndexInfo) types.AuthConfig {
|
||||
configKey := index.Name
|
||||
if index.Official {
|
||||
configKey = cli.electAuthServer()
|
||||
}
|
||||
|
||||
a, _ := getCredentials(cli.configFile, configKey)
|
||||
return a
|
||||
}
|
||||
|
||||
func (cli *DockerCli) retrieveAuthConfigs() map[string]types.AuthConfig {
|
||||
acs, _ := getAllCredentials(cli.configFile)
|
||||
return acs
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build !windows
|
||||
|
||||
package builder
|
||||
package client
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build windows
|
||||
|
||||
package builder
|
||||
package client
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
@@ -5,13 +5,10 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/docker/utils/templates"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
@@ -51,7 +48,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
|
||||
}
|
||||
|
||||
var tmpl *template.Template
|
||||
if tmpl, err = templates.Parse(templateFormat); err != nil {
|
||||
if tmpl, err = template.New("").Funcs(funcMap).Parse(templateFormat); err != nil {
|
||||
return Cli.StatusError{StatusCode: 64,
|
||||
Status: "Template parsing error: " + err.Error()}
|
||||
}
|
||||
@@ -69,7 +66,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
|
||||
},
|
||||
}
|
||||
|
||||
serverVersion, err := cli.client.ServerVersion(context.Background())
|
||||
serverVersion, err := cli.client.ServerVersion()
|
||||
if err == nil {
|
||||
vd.Server = &serverVersion
|
||||
}
|
||||
|
||||
@@ -2,15 +2,11 @@ package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"text/tabwriter"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/opts"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
)
|
||||
@@ -62,7 +58,7 @@ func (cli *DockerCli) CmdVolumeLs(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
volumes, err := cli.client.VolumeList(context.Background(), volFilterArgs)
|
||||
volumes, err := cli.client.VolumeList(volFilterArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -76,7 +72,6 @@ func (cli *DockerCli) CmdVolumeLs(args ...string) error {
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
|
||||
sort.Sort(byVolumeName(volumes.Volumes))
|
||||
for _, vol := range volumes.Volumes {
|
||||
if *quiet {
|
||||
fmt.Fprintln(w, vol.Name)
|
||||
@@ -88,14 +83,6 @@ func (cli *DockerCli) CmdVolumeLs(args ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type byVolumeName []*types.Volume
|
||||
|
||||
func (r byVolumeName) Len() int { return len(r) }
|
||||
func (r byVolumeName) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
func (r byVolumeName) Less(i, j int) bool {
|
||||
return r[i].Name < r[j].Name
|
||||
}
|
||||
|
||||
// CmdVolumeInspect displays low-level information on one or more volumes.
|
||||
//
|
||||
// Usage: docker volume inspect [OPTIONS] VOLUME [VOLUME...]
|
||||
@@ -111,7 +98,7 @@ func (cli *DockerCli) CmdVolumeInspect(args ...string) error {
|
||||
}
|
||||
|
||||
inspectSearcher := func(name string) (interface{}, []byte, error) {
|
||||
i, err := cli.client.VolumeInspect(context.Background(), name)
|
||||
i, err := cli.client.VolumeInspect(name)
|
||||
return i, nil, err
|
||||
}
|
||||
|
||||
@@ -129,9 +116,6 @@ func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
|
||||
flDriverOpts := opts.NewMapOpts(nil, nil)
|
||||
cmd.Var(flDriverOpts, []string{"o", "-opt"}, "Set driver specific options")
|
||||
|
||||
flLabels := opts.NewListOpts(nil)
|
||||
cmd.Var(&flLabels, []string{"-label"}, "Set metadata for a volume")
|
||||
|
||||
cmd.Require(flag.Exact, 0)
|
||||
cmd.ParseFlags(args, true)
|
||||
|
||||
@@ -139,10 +123,9 @@ func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
|
||||
Driver: *flDriver,
|
||||
DriverOpts: flDriverOpts.GetAll(),
|
||||
Name: *flName,
|
||||
Labels: runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
|
||||
}
|
||||
|
||||
vol, err := cli.client.VolumeCreate(context.Background(), volReq)
|
||||
vol, err := cli.client.VolumeCreate(volReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -162,7 +145,7 @@ func (cli *DockerCli) CmdVolumeRm(args ...string) error {
|
||||
var status = 0
|
||||
|
||||
for _, name := range cmd.Args() {
|
||||
if err := cli.client.VolumeRemove(context.Background(), name); err != nil {
|
||||
if err := cli.client.VolumeRemove(name); err != nil {
|
||||
fmt.Fprintf(cli.err, "%s\n", err)
|
||||
status = 1
|
||||
continue
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
Cli "github.com/docker/docker/cli"
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
@@ -23,9 +21,9 @@ func (cli *DockerCli) CmdWait(args ...string) error {
|
||||
|
||||
var errs []string
|
||||
for _, name := range cmd.Args() {
|
||||
status, err := cli.client.ContainerWait(context.Background(), name)
|
||||
status, err := cli.client.ContainerWait(name)
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, fmt.Sprintf("Failed to wait container (%s): %s", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(cli.out, "%d\n", status)
|
||||
}
|
||||
|
||||
@@ -18,11 +18,14 @@ import (
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
// Version of Current REST API
|
||||
DefaultVersion version.Version = "1.23"
|
||||
DefaultVersion version.Version = "1.22"
|
||||
|
||||
// MinVersion represents Minimum REST API version supported
|
||||
MinVersion version.Version = "1.12"
|
||||
|
||||
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
|
||||
DefaultDockerfileName string = "Dockerfile"
|
||||
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
NoBaseImageSpecifier string = "scratch"
|
||||
|
||||
@@ -310,7 +310,7 @@ func TestLoadOrCreateTrustKeyCreateKey(t *testing.T) {
|
||||
}
|
||||
|
||||
// With the need to create the folder hierarchy as tmpKeyFie is in a path
|
||||
// where some folders do not exist.
|
||||
// where some folder do not exists.
|
||||
tmpKeyFile = filepath.Join(tmpKeyFolderPath, "folder/hierarchy/keyfile")
|
||||
|
||||
if key, err := LoadOrCreateTrustKey(tmpKeyFile); err != nil || key == nil {
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package httputils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// httpStatusError is an interface
|
||||
// that errors with custom status codes
|
||||
// implement to tell the api layer
|
||||
// which response status to set.
|
||||
type httpStatusError interface {
|
||||
HTTPErrorStatusCode() int
|
||||
}
|
||||
|
||||
// inputValidationError is an interface
|
||||
// that errors generated by invalid
|
||||
// inputs can implement to tell the
|
||||
// api layer to set a 400 status code
|
||||
// in the response.
|
||||
type inputValidationError interface {
|
||||
IsValidationError() bool
|
||||
}
|
||||
|
||||
// WriteError decodes a specific docker error and sends it in the response.
|
||||
func WriteError(w http.ResponseWriter, err error) {
|
||||
if err == nil || w == nil {
|
||||
logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
|
||||
return
|
||||
}
|
||||
|
||||
var statusCode int
|
||||
errMsg := err.Error()
|
||||
|
||||
switch e := err.(type) {
|
||||
case httpStatusError:
|
||||
statusCode = e.HTTPErrorStatusCode()
|
||||
case inputValidationError:
|
||||
statusCode = http.StatusBadRequest
|
||||
default:
|
||||
// FIXME: this is brittle and should not be necessary, but we still need to identify if
|
||||
// there are errors falling back into this logic.
|
||||
// If we need to differentiate between different possible error types,
|
||||
// we should create appropriate error types that implement the httpStatusError interface.
|
||||
errStr := strings.ToLower(errMsg)
|
||||
for keyword, status := range map[string]int{
|
||||
"not found": http.StatusNotFound,
|
||||
"no such": http.StatusNotFound,
|
||||
"bad parameter": http.StatusBadRequest,
|
||||
"conflict": http.StatusConflict,
|
||||
"impossible": http.StatusNotAcceptable,
|
||||
"wrong login/password": http.StatusUnauthorized,
|
||||
"hasn't been activated": http.StatusForbidden,
|
||||
} {
|
||||
if strings.Contains(errStr, keyword) {
|
||||
statusCode = status
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
http.Error(w, errMsg, statusCode)
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
)
|
||||
@@ -16,11 +18,8 @@ import (
|
||||
// APIVersionKey is the client's requested API version.
|
||||
const APIVersionKey = "api-version"
|
||||
|
||||
// UAStringKey is used as key type for user-agent string in net/context struct
|
||||
const UAStringKey = "upstream-user-agent"
|
||||
|
||||
// APIFunc is an adapter to allow the use of ordinary functions as Docker API endpoints.
|
||||
// Any function that has the appropriate signature can be registered as a API endpoint (e.g. getVersion).
|
||||
// Any function that has the appropriate signature can be register as a API endpoint (e.g. getVersion).
|
||||
type APIFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error
|
||||
|
||||
// HijackConnection interrupts the http response writer to get the
|
||||
@@ -78,7 +77,7 @@ func ParseForm(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseMultipartForm ensures the request form is parsed, even with invalid content types.
|
||||
// ParseMultipartForm ensure the request form is parsed, even with invalid content types.
|
||||
func ParseMultipartForm(r *http.Request) error {
|
||||
if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
|
||||
return err
|
||||
@@ -86,6 +85,78 @@ func ParseMultipartForm(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteError decodes a specific docker error and sends it in the response.
|
||||
func WriteError(w http.ResponseWriter, err error) {
|
||||
if err == nil || w == nil {
|
||||
logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
|
||||
return
|
||||
}
|
||||
|
||||
statusCode := http.StatusInternalServerError
|
||||
errMsg := err.Error()
|
||||
|
||||
// Based on the type of error we get we need to process things
|
||||
// slightly differently to extract the error message.
|
||||
// In the 'errcode.*' cases there are two different type of
|
||||
// error that could be returned. errocode.ErrorCode is the base
|
||||
// type of error object - it is just an 'int' that can then be
|
||||
// used as the look-up key to find the message. errorcode.Error
|
||||
// extends errorcode.Error by adding error-instance specific
|
||||
// data, like 'details' or variable strings to be inserted into
|
||||
// the message.
|
||||
//
|
||||
// Ideally, we should just be able to call err.Error() for all
|
||||
// cases but the errcode package doesn't support that yet.
|
||||
//
|
||||
// Additionally, in both errcode cases, there might be an http
|
||||
// status code associated with it, and if so use it.
|
||||
switch err.(type) {
|
||||
case errcode.ErrorCode:
|
||||
daError, _ := err.(errcode.ErrorCode)
|
||||
statusCode = daError.Descriptor().HTTPStatusCode
|
||||
errMsg = daError.Message()
|
||||
|
||||
case errcode.Error:
|
||||
// For reference, if you're looking for a particular error
|
||||
// then you can do something like :
|
||||
// import ( derr "github.com/docker/docker/errors" )
|
||||
// if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
|
||||
|
||||
daError, _ := err.(errcode.Error)
|
||||
statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode
|
||||
errMsg = daError.Message
|
||||
|
||||
default:
|
||||
// This part of will be removed once we've
|
||||
// converted everything over to use the errcode package
|
||||
|
||||
// FIXME: this is brittle and should not be necessary.
|
||||
// If we need to differentiate between different possible error types,
|
||||
// we should create appropriate error types with clearly defined meaning
|
||||
errStr := strings.ToLower(err.Error())
|
||||
for keyword, status := range map[string]int{
|
||||
"not found": http.StatusNotFound,
|
||||
"no such": http.StatusNotFound,
|
||||
"bad parameter": http.StatusBadRequest,
|
||||
"conflict": http.StatusConflict,
|
||||
"impossible": http.StatusNotAcceptable,
|
||||
"wrong login/password": http.StatusUnauthorized,
|
||||
"hasn't been activated": http.StatusForbidden,
|
||||
} {
|
||||
if strings.Contains(errStr, keyword) {
|
||||
statusCode = status
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
http.Error(w, errMsg, statusCode)
|
||||
}
|
||||
|
||||
// WriteJSON writes the value v to the http response stream as json with standard json encoding.
|
||||
func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
@@ -1,41 +1,195 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/server/middleware"
|
||||
"github.com/docker/docker/dockerversion"
|
||||
"github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// middleware is an adapter to allow the use of ordinary functions as Docker API filters.
|
||||
// Any function that has the appropriate signature can be register as a middleware.
|
||||
type middleware func(handler httputils.APIFunc) httputils.APIFunc
|
||||
|
||||
// debugRequestMiddleware dumps the request to logger
|
||||
func debugRequestMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
logrus.Debugf("%s %s", r.Method, r.RequestURI)
|
||||
|
||||
if r.Method != "POST" {
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
maxBodySize := 4096 // 4KB
|
||||
if r.ContentLength > int64(maxBodySize) {
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
|
||||
body := r.Body
|
||||
bufReader := bufio.NewReaderSize(body, maxBodySize)
|
||||
r.Body = ioutils.NewReadCloserWrapper(bufReader, func() error { return body.Close() })
|
||||
|
||||
b, err := bufReader.Peek(maxBodySize)
|
||||
if err != io.EOF {
|
||||
// either there was an error reading, or the buffer is full (in which case the request is too large)
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
|
||||
var postForm map[string]interface{}
|
||||
if err := json.Unmarshal(b, &postForm); err == nil {
|
||||
if _, exists := postForm["password"]; exists {
|
||||
postForm["password"] = "*****"
|
||||
}
|
||||
formStr, errMarshal := json.Marshal(postForm)
|
||||
if errMarshal == nil {
|
||||
logrus.Debugf("form data: %s", string(formStr))
|
||||
} else {
|
||||
logrus.Debugf("form data: %q", postForm)
|
||||
}
|
||||
}
|
||||
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
|
||||
// authorizationMiddleware perform authorization on the request.
|
||||
func (s *Server) authorizationMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// FIXME: fill when authN gets in
|
||||
// User and UserAuthNMethod are taken from AuthN plugins
|
||||
// Currently tracked in https://github.com/docker/docker/pull/13994
|
||||
user := ""
|
||||
userAuthNMethod := ""
|
||||
authCtx := authorization.NewCtx(s.authZPlugins, user, userAuthNMethod, r.Method, r.RequestURI)
|
||||
|
||||
if err := authCtx.AuthZRequest(w, r); err != nil {
|
||||
logrus.Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err)
|
||||
return err
|
||||
}
|
||||
|
||||
rw := authorization.NewResponseModifier(w)
|
||||
|
||||
if err := handler(ctx, rw, r, vars); err != nil {
|
||||
logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.RequestURI, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := authCtx.AuthZResponse(rw, r); err != nil {
|
||||
logrus.Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// userAgentMiddleware checks the User-Agent header looking for a valid docker client spec.
|
||||
func (s *Server) userAgentMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
||||
dockerVersion := version.Version(s.cfg.Version)
|
||||
|
||||
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
||||
|
||||
// v1.20 onwards includes the GOOS of the client after the version
|
||||
// such as Docker/1.7.0 (linux)
|
||||
if len(userAgent) == 2 && strings.Contains(userAgent[1], " ") {
|
||||
userAgent[1] = strings.Split(userAgent[1], " ")[0]
|
||||
}
|
||||
|
||||
if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
|
||||
logrus.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
|
||||
}
|
||||
}
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
|
||||
// corsMiddleware sets the CORS header expectations in the server.
|
||||
func (s *Server) corsMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
|
||||
// otherwise, all head values will be passed to HTTP handler
|
||||
corsHeaders := s.cfg.CorsHeaders
|
||||
if corsHeaders == "" && s.cfg.EnableCors {
|
||||
corsHeaders = "*"
|
||||
}
|
||||
|
||||
if corsHeaders != "" {
|
||||
writeCorsHeaders(w, r, corsHeaders)
|
||||
}
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
|
||||
// versionMiddleware checks the api version requirements before passing the request to the server handler.
|
||||
func versionMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
apiVersion := version.Version(vars["version"])
|
||||
if apiVersion == "" {
|
||||
apiVersion = api.DefaultVersion
|
||||
}
|
||||
|
||||
if apiVersion.GreaterThan(api.DefaultVersion) {
|
||||
return errors.ErrorCodeNewerClientVersion.WithArgs(apiVersion, api.DefaultVersion)
|
||||
}
|
||||
if apiVersion.LessThan(api.MinVersion) {
|
||||
return errors.ErrorCodeOldClientVersion.WithArgs(apiVersion, api.DefaultVersion)
|
||||
}
|
||||
|
||||
w.Header().Set("Server", "Docker/"+dockerversion.Version+" ("+runtime.GOOS+")")
|
||||
ctx = context.WithValue(ctx, httputils.APIVersionKey, apiVersion)
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
|
||||
// handleWithGlobalMiddlwares wraps the handler function for a request with
|
||||
// the server's global middlewares. The order of the middlewares is backwards,
|
||||
// meaning that the first in the list will be evaluated last.
|
||||
//
|
||||
// Example: handleWithGlobalMiddlewares(s.getContainersName)
|
||||
//
|
||||
// s.loggingMiddleware(
|
||||
// s.userAgentMiddleware(
|
||||
// s.corsMiddleware(
|
||||
// versionMiddleware(s.getContainersName)
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
func (s *Server) handleWithGlobalMiddlewares(handler httputils.APIFunc) httputils.APIFunc {
|
||||
next := handler
|
||||
|
||||
handleVersion := middleware.NewVersionMiddleware(dockerversion.Version, api.DefaultVersion, api.MinVersion)
|
||||
next = handleVersion(next)
|
||||
|
||||
if s.cfg.EnableCors {
|
||||
handleCORS := middleware.NewCORSMiddleware(s.cfg.CorsHeaders)
|
||||
next = handleCORS(next)
|
||||
middlewares := []middleware{
|
||||
versionMiddleware,
|
||||
s.corsMiddleware,
|
||||
s.userAgentMiddleware,
|
||||
}
|
||||
|
||||
handleUserAgent := middleware.NewUserAgentMiddleware(s.cfg.Version)
|
||||
next = handleUserAgent(next)
|
||||
|
||||
// Only want this on debug level
|
||||
if s.cfg.Logging && logrus.GetLevel() == logrus.DebugLevel {
|
||||
next = middleware.DebugRequestMiddleware(next)
|
||||
middlewares = append(middlewares, debugRequestMiddleware)
|
||||
}
|
||||
|
||||
if len(s.cfg.AuthorizationPluginNames) > 0 {
|
||||
s.authZPlugins = authorization.NewPlugins(s.cfg.AuthorizationPluginNames)
|
||||
handleAuthorization := middleware.NewAuthorizationMiddleware(s.authZPlugins)
|
||||
next = handleAuthorization(next)
|
||||
middlewares = append(middlewares, s.authorizationMiddleware)
|
||||
}
|
||||
|
||||
return next
|
||||
h := handler
|
||||
for _, m := range middlewares {
|
||||
h = m(h)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/pkg/authorization"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// NewAuthorizationMiddleware creates a new Authorization middleware.
|
||||
func NewAuthorizationMiddleware(plugins []authorization.Plugin) Middleware {
|
||||
return func(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// FIXME: fill when authN gets in
|
||||
// User and UserAuthNMethod are taken from AuthN plugins
|
||||
// Currently tracked in https://github.com/docker/docker/pull/13994
|
||||
user := ""
|
||||
userAuthNMethod := ""
|
||||
authCtx := authorization.NewCtx(plugins, user, userAuthNMethod, r.Method, r.RequestURI)
|
||||
|
||||
if err := authCtx.AuthZRequest(w, r); err != nil {
|
||||
logrus.Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err)
|
||||
return err
|
||||
}
|
||||
|
||||
rw := authorization.NewResponseModifier(w)
|
||||
|
||||
if err := handler(ctx, rw, r, vars); err != nil {
|
||||
logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.RequestURI, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := authCtx.AuthZResponse(rw, r); err != nil {
|
||||
logrus.Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// NewCORSMiddleware creates a new CORS middleware.
|
||||
func NewCORSMiddleware(defaultHeaders string) Middleware {
|
||||
return func(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
|
||||
// otherwise, all head values will be passed to HTTP handler
|
||||
corsHeaders := defaultHeaders
|
||||
if corsHeaders == "" {
|
||||
corsHeaders = "*"
|
||||
}
|
||||
|
||||
writeCorsHeaders(w, r, corsHeaders)
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
|
||||
logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders)
|
||||
w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
|
||||
w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
|
||||
w.Header().Add("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS")
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// DebugRequestMiddleware dumps the request to logger
|
||||
func DebugRequestMiddleware(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
logrus.Debugf("Calling %s %s", r.Method, r.RequestURI)
|
||||
|
||||
if r.Method != "POST" {
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
if err := httputils.CheckForJSON(r); err != nil {
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
maxBodySize := 4096 // 4KB
|
||||
if r.ContentLength > int64(maxBodySize) {
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
|
||||
body := r.Body
|
||||
bufReader := bufio.NewReaderSize(body, maxBodySize)
|
||||
r.Body = ioutils.NewReadCloserWrapper(bufReader, func() error { return body.Close() })
|
||||
|
||||
b, err := bufReader.Peek(maxBodySize)
|
||||
if err != io.EOF {
|
||||
// either there was an error reading, or the buffer is full (in which case the request is too large)
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
|
||||
var postForm map[string]interface{}
|
||||
if err := json.Unmarshal(b, &postForm); err == nil {
|
||||
if _, exists := postForm["password"]; exists {
|
||||
postForm["password"] = "*****"
|
||||
}
|
||||
formStr, errMarshal := json.Marshal(postForm)
|
||||
if errMarshal == nil {
|
||||
logrus.Debugf("form data: %s", string(formStr))
|
||||
} else {
|
||||
logrus.Debugf("form data: %q", postForm)
|
||||
}
|
||||
}
|
||||
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
// Middleware is an adapter to allow the use of ordinary functions as Docker API filters.
|
||||
// Any function that has the appropriate signature can be registered as a middleware.
|
||||
type Middleware func(handler httputils.APIFunc) httputils.APIFunc
|
||||
@@ -1,37 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// NewUserAgentMiddleware creates a new UserAgent middleware.
|
||||
func NewUserAgentMiddleware(versionCheck string) Middleware {
|
||||
serverVersion := version.Version(versionCheck)
|
||||
|
||||
return func(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
ctx = context.WithValue(ctx, httputils.UAStringKey, r.Header.Get("User-Agent"))
|
||||
|
||||
if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
|
||||
userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
|
||||
|
||||
// v1.20 onwards includes the GOOS of the client after the version
|
||||
// such as Docker/1.7.0 (linux)
|
||||
if len(userAgent) == 2 && strings.Contains(userAgent[1], " ") {
|
||||
userAgent[1] = strings.Split(userAgent[1], " ")[0]
|
||||
}
|
||||
|
||||
if len(userAgent) == 2 && !serverVersion.Equal(version.Version(userAgent[1])) {
|
||||
logrus.Debugf("Client and server don't have the same version (client: %s, server: %s)", userAgent[1], serverVersion)
|
||||
}
|
||||
}
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type badRequestError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (badRequestError) HTTPErrorStatusCode() int {
|
||||
return http.StatusBadRequest
|
||||
}
|
||||
|
||||
// NewVersionMiddleware creates a new Version middleware.
|
||||
func NewVersionMiddleware(versionCheck string, defaultVersion, minVersion version.Version) Middleware {
|
||||
serverVersion := version.Version(versionCheck)
|
||||
|
||||
return func(handler httputils.APIFunc) httputils.APIFunc {
|
||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
apiVersion := version.Version(vars["version"])
|
||||
if apiVersion == "" {
|
||||
apiVersion = defaultVersion
|
||||
}
|
||||
|
||||
if apiVersion.GreaterThan(defaultVersion) {
|
||||
return badRequestError{fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", apiVersion, defaultVersion)}
|
||||
}
|
||||
if apiVersion.LessThan(minVersion) {
|
||||
return badRequestError{fmt.Errorf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", apiVersion, minVersion)}
|
||||
}
|
||||
|
||||
header := fmt.Sprintf("Docker/%s (%s)", serverVersion, runtime.GOOS)
|
||||
w.Header().Set("Server", header)
|
||||
ctx = context.WithValue(ctx, httputils.APIVersionKey, apiVersion)
|
||||
return handler(ctx, w, r, vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package middleware
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/docker/errors"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -19,10 +19,7 @@ func TestVersionMiddleware(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
defaultVersion := version.Version("1.10.0")
|
||||
minVersion := version.Version("1.2.0")
|
||||
m := NewVersionMiddleware(defaultVersion.String(), defaultVersion, minVersion)
|
||||
h := m(handler)
|
||||
h := versionMiddleware(handler)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
@@ -40,10 +37,7 @@ func TestVersionMiddlewareWithErrors(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
defaultVersion := version.Version("1.10.0")
|
||||
minVersion := version.Version("1.2.0")
|
||||
m := NewVersionMiddleware(defaultVersion.String(), defaultVersion, minVersion)
|
||||
h := m(handler)
|
||||
h := versionMiddleware(handler)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/containers/json", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
@@ -51,14 +45,13 @@ func TestVersionMiddlewareWithErrors(t *testing.T) {
|
||||
|
||||
vars := map[string]string{"version": "0.1"}
|
||||
err := h(ctx, resp, req, vars)
|
||||
|
||||
if !strings.Contains(err.Error(), "client version 0.1 is too old. Minimum supported API version is 1.2.0") {
|
||||
t.Fatalf("Expected too old client error, got %v", err)
|
||||
if derr, ok := err.(errcode.Error); !ok || derr.ErrorCode() != errors.ErrorCodeOldClientVersion {
|
||||
t.Fatalf("Expected ErrorCodeOldClientVersion, got %v", err)
|
||||
}
|
||||
|
||||
vars["version"] = "100000"
|
||||
err = h(ctx, resp, req, vars)
|
||||
if !strings.Contains(err.Error(), "client is newer than server") {
|
||||
t.Fatalf("Expected client newer than server error, got %v", err)
|
||||
if derr, ok := err.(errcode.Error); !ok || derr.ErrorCode() != errors.ErrorCodeNewerClientVersion {
|
||||
t.Fatalf("Expected ErrorCodeNewerClientVersion, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,8 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
const debugPathPrefix = "/debug/"
|
||||
|
||||
func profilerSetup(mainRouter *mux.Router) {
|
||||
var r = mainRouter.PathPrefix(debugPathPrefix).Subrouter()
|
||||
func profilerSetup(mainRouter *mux.Router, path string) {
|
||||
var r = mainRouter.PathPrefix(path).Subrouter()
|
||||
r.HandleFunc("/vars", expVars)
|
||||
r.HandleFunc("/pprof/", pprof.Index)
|
||||
r.HandleFunc("/pprof/cmdline", pprof.Cmdline)
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/engine-api/types"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Backend abstracts an image builder whose only purpose is to build an image referenced by an imageID.
|
||||
type Backend interface {
|
||||
// Build builds a Docker image referenced by an imageID string.
|
||||
@@ -16,5 +8,5 @@ type Backend interface {
|
||||
// by the caller.
|
||||
//
|
||||
// TODO: make this return a reference instead of string
|
||||
Build(clientCtx context.Context, config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error)
|
||||
Build() (imageID string)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package build
|
||||
|
||||
import "github.com/docker/docker/api/server/router"
|
||||
import (
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/api/server/router/local"
|
||||
"github.com/docker/docker/daemon"
|
||||
)
|
||||
|
||||
// buildRouter is a router to talk with the build controller
|
||||
type buildRouter struct {
|
||||
backend Backend
|
||||
backend *daemon.Daemon
|
||||
routes []router.Route
|
||||
}
|
||||
|
||||
// NewRouter initializes a new build router
|
||||
func NewRouter(b Backend) router.Router {
|
||||
func NewRouter(b *daemon.Daemon) router.Router {
|
||||
r := &buildRouter{
|
||||
backend: b,
|
||||
}
|
||||
@@ -24,6 +28,6 @@ func (r *buildRouter) Routes() []router.Route {
|
||||
|
||||
func (r *buildRouter) initRoutes() {
|
||||
r.routes = []router.Route{
|
||||
router.NewPostRoute("/build", r.postBuild),
|
||||
local.NewPostRoute("/build", r.postBuild),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,25 +4,70 @@ import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/dockerfile"
|
||||
"github.com/docker/docker/daemon/daemonbuilder"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/go-units"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// sanitizeRepoAndTags parses the raw "t" parameter received from the client
|
||||
// to a slice of repoAndTag.
|
||||
// It also validates each repoName and tag.
|
||||
func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
|
||||
var (
|
||||
repoAndTags []reference.Named
|
||||
// This map is used for deduplicating the "-t" parameter.
|
||||
uniqNames = make(map[string]struct{})
|
||||
)
|
||||
for _, repo := range names {
|
||||
if repo == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
ref, err := reference.ParseNamed(repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ref = reference.WithDefaultTag(ref)
|
||||
|
||||
if _, isCanonical := ref.(reference.Canonical); isCanonical {
|
||||
return nil, errors.New("build tag cannot contain a digest")
|
||||
}
|
||||
|
||||
if _, isTagged := ref.(reference.NamedTagged); !isTagged {
|
||||
ref, err = reference.WithTag(ref, reference.DefaultTag)
|
||||
}
|
||||
|
||||
nameWithTag := ref.String()
|
||||
|
||||
if _, exists := uniqNames[nameWithTag]; !exists {
|
||||
uniqNames[nameWithTag] = struct{}{}
|
||||
repoAndTags = append(repoAndTags, ref)
|
||||
}
|
||||
}
|
||||
return repoAndTags, nil
|
||||
}
|
||||
|
||||
func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) {
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
options := &types.ImageBuildOptions{}
|
||||
@@ -49,7 +94,6 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
options.CPUSetCPUs = r.FormValue("cpusetcpus")
|
||||
options.CPUSetMems = r.FormValue("cpusetmems")
|
||||
options.CgroupParent = r.FormValue("cgroupparent")
|
||||
options.Tags = r.Form["t"]
|
||||
|
||||
if r.Form.Get("shmsize") != "" {
|
||||
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
|
||||
@@ -59,11 +103,11 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
options.ShmSize = shmSize
|
||||
}
|
||||
|
||||
if i := container.Isolation(r.FormValue("isolation")); i != "" {
|
||||
if !container.Isolation.IsValid(i) {
|
||||
if i := container.IsolationLevel(r.FormValue("isolation")); i != "" {
|
||||
if !container.IsolationLevel.IsValid(i) {
|
||||
return nil, fmt.Errorf("Unsupported isolation: %q", i)
|
||||
}
|
||||
options.Isolation = i
|
||||
options.IsolationLevel = i
|
||||
}
|
||||
|
||||
var buildUlimits = []*units.Ulimit{}
|
||||
@@ -83,30 +127,9 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||
}
|
||||
options.BuildArgs = buildArgs
|
||||
}
|
||||
var labels = map[string]string{}
|
||||
labelsJSON := r.FormValue("labels")
|
||||
if labelsJSON != "" {
|
||||
if err := json.NewDecoder(strings.NewReader(labelsJSON)).Decode(&labels); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Labels = labels
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
type syncWriter struct {
|
||||
w io.Writer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (s *syncWriter) Write(b []byte) (count int, err error) {
|
||||
s.mu.Lock()
|
||||
count, err = s.w.Write(b)
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var (
|
||||
authConfigs = map[string]types.AuthConfig{}
|
||||
@@ -137,7 +160,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(sf.FormatError(err))
|
||||
_, err = w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err))))
|
||||
if err != nil {
|
||||
logrus.Warnf("could not write error response: %v", err)
|
||||
}
|
||||
@@ -149,6 +172,11 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
|
||||
return errf(err)
|
||||
}
|
||||
|
||||
repoAndTags, err := sanitizeRepoAndTags(r.Form["t"])
|
||||
if err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
|
||||
remoteURL := r.FormValue("remote")
|
||||
|
||||
// Currently, only used if context is from a remote url.
|
||||
@@ -164,9 +192,8 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
|
||||
var (
|
||||
context builder.ModifiableContext
|
||||
dockerfileName string
|
||||
out io.Writer
|
||||
)
|
||||
context, dockerfileName, err = builder.DetectContextFromRemoteURL(r.Body, remoteURL, createProgressReader)
|
||||
context, dockerfileName, err = daemonbuilder.DetectContextFromRemoteURL(r.Body, remoteURL, createProgressReader)
|
||||
if err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
@@ -179,28 +206,61 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
|
||||
buildOptions.Dockerfile = dockerfileName
|
||||
}
|
||||
|
||||
buildOptions.AuthConfigs = authConfigs
|
||||
uidMaps, gidMaps := br.backend.GetUIDGIDMaps()
|
||||
defaultArchiver := &archive.Archiver{
|
||||
Untar: chrootarchive.Untar,
|
||||
UIDMaps: uidMaps,
|
||||
GIDMaps: gidMaps,
|
||||
}
|
||||
|
||||
out = output
|
||||
docker := &daemonbuilder.Docker{
|
||||
Daemon: br.backend,
|
||||
OutOld: output,
|
||||
AuthConfigs: authConfigs,
|
||||
Archiver: defaultArchiver,
|
||||
}
|
||||
if buildOptions.SuppressOutput {
|
||||
out = notVerboseBuffer
|
||||
}
|
||||
out = &syncWriter{w: out}
|
||||
stdout := &streamformatter.StdoutFormatter{Writer: out, StreamFormatter: sf}
|
||||
stderr := &streamformatter.StderrFormatter{Writer: out, StreamFormatter: sf}
|
||||
|
||||
closeNotifier := make(<-chan bool)
|
||||
if notifier, ok := w.(http.CloseNotifier); ok {
|
||||
closeNotifier = notifier.CloseNotify()
|
||||
docker.OutOld = notVerboseBuffer
|
||||
}
|
||||
|
||||
imgID, err := br.backend.Build(ctx, buildOptions,
|
||||
b, err := dockerfile.NewBuilder(
|
||||
buildOptions, // result of newBuildConfig
|
||||
docker,
|
||||
builder.DockerIgnoreContext{ModifiableContext: context},
|
||||
stdout, stderr, out,
|
||||
closeNotifier)
|
||||
nil)
|
||||
if err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
b.Stdout = &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
|
||||
b.Stderr = &streamformatter.StderrFormatter{Writer: output, StreamFormatter: sf}
|
||||
if buildOptions.SuppressOutput {
|
||||
b.Stdout = &streamformatter.StdoutFormatter{Writer: notVerboseBuffer, StreamFormatter: sf}
|
||||
b.Stderr = &streamformatter.StderrFormatter{Writer: notVerboseBuffer, StreamFormatter: sf}
|
||||
}
|
||||
|
||||
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
||||
finished := make(chan struct{})
|
||||
defer close(finished)
|
||||
go func() {
|
||||
select {
|
||||
case <-finished:
|
||||
case <-closeNotifier.CloseNotify():
|
||||
logrus.Infof("Client disconnected, cancelling job: build")
|
||||
b.Cancel()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
imgID, err := b.Build()
|
||||
if err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
|
||||
for _, rt := range repoAndTags {
|
||||
if err := br.backend.TagImage(rt, imgID); err != nil {
|
||||
return errf(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Everything worked so if -q was provided the output from the daemon
|
||||
// should be just the image ID and we'll print that to stdout.
|
||||
|
||||
@@ -4,7 +4,8 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/version"
|
||||
"github.com/docker/engine-api/types"
|
||||
@@ -14,7 +15,7 @@ import (
|
||||
// execBackend includes functions to implement to provide exec functionality.
|
||||
type execBackend interface {
|
||||
ContainerExecCreate(config *types.ExecConfig) (string, error)
|
||||
ContainerExecInspect(id string) (*backend.ExecInspect, error)
|
||||
ContainerExecInspect(id string) (*exec.Config, error)
|
||||
ContainerExecResize(name string, height, width int) error
|
||||
ContainerExecStart(name string, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer) error
|
||||
ExecExists(name string) (bool, error)
|
||||
@@ -43,22 +44,24 @@ type stateBackend interface {
|
||||
ContainerUnpause(name string) error
|
||||
ContainerUpdate(name string, hostConfig *container.HostConfig) ([]string, error)
|
||||
ContainerWait(name string, timeout time.Duration) (int, error)
|
||||
Exists(id string) bool
|
||||
}
|
||||
|
||||
// monitorBackend includes functions to implement to provide containers monitoring functionality.
|
||||
type monitorBackend interface {
|
||||
ContainerChanges(name string) ([]archive.Change, error)
|
||||
ContainerInspect(name string, size bool, version version.Version) (interface{}, error)
|
||||
ContainerLogs(name string, config *backend.ContainerLogsConfig, started chan struct{}) error
|
||||
ContainerStats(name string, config *backend.ContainerStatsConfig) error
|
||||
ContainerLogs(name string, config *daemon.ContainerLogsConfig) error
|
||||
ContainerStats(name string, config *daemon.ContainerStatsConfig) error
|
||||
ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error)
|
||||
|
||||
Containers(config *types.ContainerListOptions) ([]*types.Container, error)
|
||||
Containers(config *daemon.ContainersConfig) ([]*types.Container, error)
|
||||
}
|
||||
|
||||
// attachBackend includes function to implement to provide container attaching functionality.
|
||||
type attachBackend interface {
|
||||
ContainerAttach(name string, c *backend.ContainerAttachConfig) error
|
||||
ContainerAttachWithLogs(name string, c *daemon.ContainerAttachWithLogsConfig) error
|
||||
ContainerWsAttachWithLogs(name string, c *daemon.ContainerWsAttachWithLogsConfig) error
|
||||
}
|
||||
|
||||
// Backend is all the methods that need to be implemented to provide container specific functionality.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package container
|
||||
|
||||
import "github.com/docker/docker/api/server/router"
|
||||
import (
|
||||
"github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/api/server/router/local"
|
||||
)
|
||||
|
||||
// containerRouter is a router to talk with the container controller
|
||||
type containerRouter struct {
|
||||
@@ -17,7 +20,7 @@ func NewRouter(b Backend) router.Router {
|
||||
return r
|
||||
}
|
||||
|
||||
// Routes returns the available routes to the container controller
|
||||
// Routes returns the available routers to the container controller
|
||||
func (r *containerRouter) Routes() []router.Route {
|
||||
return r.routes
|
||||
}
|
||||
@@ -26,38 +29,38 @@ func (r *containerRouter) Routes() []router.Route {
|
||||
func (r *containerRouter) initRoutes() {
|
||||
r.routes = []router.Route{
|
||||
// HEAD
|
||||
router.NewHeadRoute("/containers/{name:.*}/archive", r.headContainersArchive),
|
||||
local.NewHeadRoute("/containers/{name:.*}/archive", r.headContainersArchive),
|
||||
// GET
|
||||
router.NewGetRoute("/containers/json", r.getContainersJSON),
|
||||
router.NewGetRoute("/containers/{name:.*}/export", r.getContainersExport),
|
||||
router.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
|
||||
router.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
|
||||
router.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
|
||||
router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
|
||||
router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
|
||||
router.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
|
||||
router.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
|
||||
router.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
|
||||
local.NewGetRoute("/containers/json", r.getContainersJSON),
|
||||
local.NewGetRoute("/containers/{name:.*}/export", r.getContainersExport),
|
||||
local.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges),
|
||||
local.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName),
|
||||
local.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop),
|
||||
local.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs),
|
||||
local.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats),
|
||||
local.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach),
|
||||
local.NewGetRoute("/exec/{id:.*}/json", r.getExecByID),
|
||||
local.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive),
|
||||
// POST
|
||||
router.NewPostRoute("/containers/create", r.postContainersCreate),
|
||||
router.NewPostRoute("/containers/{name:.*}/kill", r.postContainersKill),
|
||||
router.NewPostRoute("/containers/{name:.*}/pause", r.postContainersPause),
|
||||
router.NewPostRoute("/containers/{name:.*}/unpause", r.postContainersUnpause),
|
||||
router.NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
|
||||
router.NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
|
||||
router.NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
|
||||
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
||||
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
||||
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
||||
router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy),
|
||||
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
||||
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||
router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
||||
router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
|
||||
local.NewPostRoute("/containers/create", r.postContainersCreate),
|
||||
local.NewPostRoute("/containers/{name:.*}/kill", r.postContainersKill),
|
||||
local.NewPostRoute("/containers/{name:.*}/pause", r.postContainersPause),
|
||||
local.NewPostRoute("/containers/{name:.*}/unpause", r.postContainersUnpause),
|
||||
local.NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart),
|
||||
local.NewPostRoute("/containers/{name:.*}/start", r.postContainersStart),
|
||||
local.NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop),
|
||||
local.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
||||
local.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
||||
local.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
||||
local.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy),
|
||||
local.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
||||
local.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||
local.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||
local.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename),
|
||||
local.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate),
|
||||
// PUT
|
||||
router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
||||
local.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive),
|
||||
// DELETE
|
||||
router.NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
|
||||
local.NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,18 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/daemon"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
timetypes "github.com/docker/engine-api/types/time"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
@@ -28,17 +31,13 @@ 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"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := &types.ContainerListOptions{
|
||||
All: httputils.BoolValue(r, "all"),
|
||||
Size: httputils.BoolValue(r, "size"),
|
||||
Since: r.Form.Get("since"),
|
||||
Before: r.Form.Get("before"),
|
||||
Filter: filter,
|
||||
config := &daemon.ContainersConfig{
|
||||
All: httputils.BoolValue(r, "all"),
|
||||
Size: httputils.BoolValue(r, "size"),
|
||||
Since: r.Form.Get("since"),
|
||||
Before: r.Form.Get("before"),
|
||||
Filters: r.Form.Get("filters"),
|
||||
}
|
||||
|
||||
if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
|
||||
@@ -63,8 +62,14 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
||||
}
|
||||
|
||||
stream := httputils.BoolValueOrDefault(r, "stream", true)
|
||||
var out io.Writer
|
||||
if !stream {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
out = w
|
||||
} else {
|
||||
wf := ioutils.NewWriteFlusher(w)
|
||||
out = wf
|
||||
defer wf.Close()
|
||||
}
|
||||
|
||||
var closeNotifier <-chan bool
|
||||
@@ -72,11 +77,11 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
||||
closeNotifier = notifier.CloseNotify()
|
||||
}
|
||||
|
||||
config := &backend.ContainerStatsConfig{
|
||||
config := &daemon.ContainerStatsConfig{
|
||||
Stream: stream,
|
||||
OutStream: w,
|
||||
OutStream: out,
|
||||
Stop: closeNotifier,
|
||||
Version: string(httputils.VersionFromContext(ctx)),
|
||||
Version: httputils.VersionFromContext(ctx),
|
||||
}
|
||||
|
||||
return s.backend.ContainerStats(vars["name"], config)
|
||||
@@ -97,36 +102,53 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
|
||||
return fmt.Errorf("Bad parameters: you must choose at least one stream")
|
||||
}
|
||||
|
||||
var since time.Time
|
||||
if r.Form.Get("since") != "" {
|
||||
s, n, err := timetypes.ParseTimestamps(r.Form.Get("since"), 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
since = time.Unix(s, n)
|
||||
}
|
||||
|
||||
var closeNotifier <-chan bool
|
||||
if notifier, ok := w.(http.CloseNotifier); ok {
|
||||
closeNotifier = notifier.CloseNotify()
|
||||
}
|
||||
|
||||
containerName := vars["name"]
|
||||
logsConfig := &backend.ContainerLogsConfig{
|
||||
ContainerLogsOptions: types.ContainerLogsOptions{
|
||||
Follow: httputils.BoolValue(r, "follow"),
|
||||
Timestamps: httputils.BoolValue(r, "timestamps"),
|
||||
Since: r.Form.Get("since"),
|
||||
Tail: r.Form.Get("tail"),
|
||||
ShowStdout: stdout,
|
||||
ShowStderr: stderr,
|
||||
},
|
||||
OutStream: w,
|
||||
Stop: closeNotifier,
|
||||
|
||||
if !s.backend.Exists(containerName) {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
|
||||
}
|
||||
|
||||
chStarted := make(chan struct{})
|
||||
if err := s.backend.ContainerLogs(containerName, logsConfig, chStarted); err != nil {
|
||||
select {
|
||||
case <-chStarted:
|
||||
// The client may be expecting all of the data we're sending to
|
||||
// be multiplexed, so send it through OutStream, which will
|
||||
// have been set up to handle that if needed.
|
||||
fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %v\n", err)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
// write an empty chunk of data (this is to ensure that the
|
||||
// HTTP Response is sent immediately, even if the container has
|
||||
// not yet produced any data)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
|
||||
logsConfig := &daemon.ContainerLogsConfig{
|
||||
Follow: httputils.BoolValue(r, "follow"),
|
||||
Timestamps: httputils.BoolValue(r, "timestamps"),
|
||||
Since: since,
|
||||
Tail: r.Form.Get("tail"),
|
||||
UseStdout: stdout,
|
||||
UseStderr: stderr,
|
||||
OutStream: output,
|
||||
Stop: closeNotifier,
|
||||
}
|
||||
|
||||
if err := s.backend.ContainerLogs(containerName, logsConfig); err != nil {
|
||||
// The client may be expecting all of the data we're sending to
|
||||
// be multiplexed, so send it through OutStream, which will
|
||||
// have been set up to handle that if needed.
|
||||
fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -179,10 +201,6 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
|
||||
return nil
|
||||
}
|
||||
|
||||
type errContainerIsRunning interface {
|
||||
ContainerIsRunning() bool
|
||||
}
|
||||
|
||||
func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
@@ -200,17 +218,15 @@ func (s *containerRouter) postContainersKill(ctx context.Context, w http.Respons
|
||||
}
|
||||
|
||||
if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
|
||||
var isStopped bool
|
||||
if e, ok := err.(errContainerIsRunning); ok {
|
||||
isStopped = !e.ContainerIsRunning()
|
||||
}
|
||||
theErr, isDerr := err.(errcode.ErrorCoder)
|
||||
isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning
|
||||
|
||||
// Return error that's not caused because the container is stopped.
|
||||
// Return error if the container is not running and the api is >= 1.20
|
||||
// to keep backwards compatibility.
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if version.GreaterThanOrEqualTo("1.20") || !isStopped {
|
||||
return fmt.Errorf("Cannot kill container %s: %v", name, err)
|
||||
return fmt.Errorf("Cannot kill container %s: %v", name, utils.GetErrorMessage(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,8 +341,7 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
|
||||
}
|
||||
|
||||
hostConfig := &container.HostConfig{
|
||||
Resources: updateConfig.Resources,
|
||||
RestartPolicy: updateConfig.RestartPolicy,
|
||||
Resources: updateConfig.Resources,
|
||||
}
|
||||
|
||||
name := vars["name"]
|
||||
@@ -431,45 +446,18 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
||||
}
|
||||
}
|
||||
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return fmt.Errorf("error attaching to container %s, hijack connection missing", containerName)
|
||||
}
|
||||
|
||||
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
conn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// set raw mode
|
||||
conn.Write([]byte{})
|
||||
|
||||
if upgrade {
|
||||
fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
||||
} else {
|
||||
fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
||||
}
|
||||
|
||||
closer := func() error {
|
||||
httputils.CloseStreams(conn)
|
||||
return nil
|
||||
}
|
||||
return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
|
||||
}
|
||||
|
||||
attachConfig := &backend.ContainerAttachConfig{
|
||||
GetStreams: setupStreams,
|
||||
attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
||||
Hijacker: w.(http.Hijacker),
|
||||
Upgrade: upgrade,
|
||||
UseStdin: httputils.BoolValue(r, "stdin"),
|
||||
UseStdout: httputils.BoolValue(r, "stdout"),
|
||||
UseStderr: httputils.BoolValue(r, "stderr"),
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
MuxStreams: true,
|
||||
}
|
||||
|
||||
return s.backend.ContainerAttach(containerName, attachConfig)
|
||||
return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig)
|
||||
}
|
||||
|
||||
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
@@ -478,6 +466,10 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
||||
}
|
||||
containerName := vars["name"]
|
||||
|
||||
if !s.backend.Exists(containerName) {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(containerName)
|
||||
}
|
||||
|
||||
var keys []byte
|
||||
var err error
|
||||
detachKeys := r.FormValue("detachKeys")
|
||||
@@ -488,44 +480,24 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
||||
}
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
started := make(chan struct{})
|
||||
h := websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
||||
wsChan := make(chan *websocket.Conn)
|
||||
h := func(conn *websocket.Conn) {
|
||||
wsChan <- conn
|
||||
<-done
|
||||
wsAttachWithLogsConfig := &daemon.ContainerWsAttachWithLogsConfig{
|
||||
InStream: ws,
|
||||
OutStream: ws,
|
||||
ErrStream: ws,
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
}
|
||||
|
||||
srv := websocket.Server{Handler: h, Handshake: nil}
|
||||
go func() {
|
||||
close(started)
|
||||
srv.ServeHTTP(w, r)
|
||||
}()
|
||||
if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
|
||||
logrus.Errorf("Error attaching websocket: %s", utils.GetErrorMessage(err))
|
||||
}
|
||||
})
|
||||
ws := websocket.Server{Handler: h, Handshake: nil}
|
||||
ws.ServeHTTP(w, r)
|
||||
|
||||
conn := <-wsChan
|
||||
return conn, conn, conn, nil
|
||||
}
|
||||
|
||||
attachConfig := &backend.ContainerAttachConfig{
|
||||
GetStreams: setupStreams,
|
||||
Logs: httputils.BoolValue(r, "logs"),
|
||||
Stream: httputils.BoolValue(r, "stream"),
|
||||
DetachKeys: keys,
|
||||
UseStdin: true,
|
||||
UseStdout: true,
|
||||
UseStderr: true,
|
||||
MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
|
||||
}
|
||||
|
||||
err = s.backend.ContainerAttach(containerName, attachConfig)
|
||||
close(done)
|
||||
select {
|
||||
case <-started:
|
||||
logrus.Errorf("Error attaching websocket: %s", err)
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/docker/utils"
|
||||
"github.com/docker/engine-api/types"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@@ -45,7 +46,7 @@ func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.Re
|
||||
// Register an instance of Exec in container.
|
||||
id, err := s.backend.ContainerExecCreate(execConfig)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error setting up exec command in container %s: %v", name, err)
|
||||
logrus.Errorf("Error setting up exec command in container %s: %s", name, utils.GetErrorMessage(err))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -103,6 +104,8 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
stderr = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
||||
stdout = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
||||
}
|
||||
} else {
|
||||
outStream = w
|
||||
}
|
||||
|
||||
// Now run the user process in container.
|
||||
@@ -110,9 +113,7 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||
if execStartCheck.Detach {
|
||||
return err
|
||||
}
|
||||
stdout.Write([]byte(err.Error()))
|
||||
logrus.Errorf("Error running exec in container: %v\n", err)
|
||||
return err
|
||||
logrus.Errorf("Error running exec in container: %v\n", utils.GetErrorMessage(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// getContainersByName inspects container's configuration and serializes it as json.
|
||||
// getContainersByName inspects containers configuration and serializes it as json.
|
||||
func (s *containerRouter) getContainersByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
displaySize := httputils.BoolValue(r, "size")
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/engine-api/types/registry"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Backend is all the methods that need to be implemented
|
||||
// to provide image specific functionality.
|
||||
type Backend interface {
|
||||
containerBackend
|
||||
imageBackend
|
||||
importExportBackend
|
||||
registryBackend
|
||||
}
|
||||
|
||||
type containerBackend interface {
|
||||
Commit(name string, config *types.ContainerCommitConfig) (imageID string, err error)
|
||||
}
|
||||
|
||||
type imageBackend interface {
|
||||
ImageDelete(imageRef string, force, prune bool) ([]types.ImageDelete, error)
|
||||
ImageHistory(imageName string) ([]*types.ImageHistory, error)
|
||||
Images(filterArgs string, filter string, all bool) ([]*types.Image, error)
|
||||
LookupImage(name string) (*types.ImageInspect, error)
|
||||
TagImage(newTag reference.Named, imageName string) error
|
||||
}
|
||||
|
||||
type importExportBackend interface {
|
||||
LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error
|
||||
ImportImage(src string, newRef reference.Named, msg string, inConfig io.ReadCloser, outStream io.Writer, config *container.Config) error
|
||||
ExportImage(names []string, outStream io.Writer) error
|
||||
}
|
||||
|
||||
type registryBackend interface {
|
||||
PullImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
||||
PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
||||
SearchRegistryForImages(ctx context.Context, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package image
|
||||
|
||||
import "github.com/docker/docker/api/server/router"
|
||||
|
||||
// imageRouter is a router to talk with the image controller
|
||||
type imageRouter struct {
|
||||
backend Backend
|
||||
routes []router.Route
|
||||
}
|
||||
|
||||
// NewRouter initializes a new image router
|
||||
func NewRouter(backend Backend) router.Router {
|
||||
r := &imageRouter{
|
||||
backend: backend,
|
||||
}
|
||||
r.initRoutes()
|
||||
return r
|
||||
}
|
||||
|
||||
// Routes returns the available routes to the image controller
|
||||
func (r *imageRouter) Routes() []router.Route {
|
||||
return r.routes
|
||||
}
|
||||
|
||||
// initRoutes initializes the routes in the image router
|
||||
func (r *imageRouter) initRoutes() {
|
||||
r.routes = []router.Route{
|
||||
// GET
|
||||
router.NewGetRoute("/images/json", r.getImagesJSON),
|
||||
router.NewGetRoute("/images/search", r.getImagesSearch),
|
||||
router.NewGetRoute("/images/get", r.getImagesGet),
|
||||
router.NewGetRoute("/images/{name:.*}/get", r.getImagesGet),
|
||||
router.NewGetRoute("/images/{name:.*}/history", r.getImagesHistory),
|
||||
router.NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
|
||||
// POST
|
||||
router.NewPostRoute("/commit", r.postCommit),
|
||||
router.NewPostRoute("/images/create", r.postImagesCreate),
|
||||
router.NewPostRoute("/images/load", r.postImagesLoad),
|
||||
router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush),
|
||||
router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
|
||||
// DELETE
|
||||
router.NewDeleteRoute("/images/{name:.*}", r.deleteImages),
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package router
|
||||
|
||||
import "github.com/docker/docker/api/server/httputils"
|
||||
|
||||
// localRoute defines an individual API route to connect
|
||||
// with the docker daemon. It implements Route.
|
||||
type localRoute struct {
|
||||
method string
|
||||
path string
|
||||
handler httputils.APIFunc
|
||||
}
|
||||
|
||||
// Handler returns the APIFunc to let the server wrap it in middlewares.
|
||||
func (l localRoute) Handler() httputils.APIFunc {
|
||||
return l.handler
|
||||
}
|
||||
|
||||
// Method returns the http method that the route responds to.
|
||||
func (l localRoute) Method() string {
|
||||
return l.method
|
||||
}
|
||||
|
||||
// Path returns the subpath where the route responds to.
|
||||
func (l localRoute) Path() string {
|
||||
return l.path
|
||||
}
|
||||
|
||||
// NewRoute initializes a new local route for the router.
|
||||
func NewRoute(method, path string, handler httputils.APIFunc) Route {
|
||||
return localRoute{method, path, handler}
|
||||
}
|
||||
|
||||
// NewGetRoute initializes a new route with the http method GET.
|
||||
func NewGetRoute(path string, handler httputils.APIFunc) Route {
|
||||
return NewRoute("GET", path, handler)
|
||||
}
|
||||
|
||||
// NewPostRoute initializes a new route with the http method POST.
|
||||
func NewPostRoute(path string, handler httputils.APIFunc) Route {
|
||||
return NewRoute("POST", path, handler)
|
||||
}
|
||||
|
||||
// NewPutRoute initializes a new route with the http method PUT.
|
||||
func NewPutRoute(path string, handler httputils.APIFunc) Route {
|
||||
return NewRoute("PUT", path, handler)
|
||||
}
|
||||
|
||||
// NewDeleteRoute initializes a new route with the http method DELETE.
|
||||
func NewDeleteRoute(path string, handler httputils.APIFunc) Route {
|
||||
return NewRoute("DELETE", path, handler)
|
||||
}
|
||||
|
||||
// NewOptionsRoute initializes a new route with the http method OPTIONS.
|
||||
func NewOptionsRoute(path string, handler httputils.APIFunc) Route {
|
||||
return NewRoute("OPTIONS", path, handler)
|
||||
}
|
||||
|
||||
// NewHeadRoute initializes a new route with the http method HEAD.
|
||||
func NewHeadRoute(path string, handler httputils.APIFunc) Route {
|
||||
return NewRoute("HEAD", path, handler)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package image
|
||||
package local
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -7,13 +7,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
"github.com/docker/docker/builder/dockerfile"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/reference"
|
||||
@@ -23,7 +22,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -48,6 +47,10 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
|
||||
c = &container.Config{}
|
||||
}
|
||||
|
||||
if !s.daemon.Exists(cname) {
|
||||
return derr.ErrorCodeNoSuchContainer.WithArgs(cname)
|
||||
}
|
||||
|
||||
newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"])
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -63,7 +66,7 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
|
||||
MergeConfigs: true,
|
||||
}
|
||||
|
||||
imgID, err := s.backend.Commit(cname, commitCfg)
|
||||
imgID, err := s.daemon.Commit(cname, commitCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -74,7 +77,7 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -84,8 +87,21 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
||||
repo = r.Form.Get("repo")
|
||||
tag = r.Form.Get("tag")
|
||||
message = r.Form.Get("message")
|
||||
err error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
)
|
||||
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{}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
output = ioutils.NewWriteFlusher(w)
|
||||
)
|
||||
defer output.Close()
|
||||
|
||||
@@ -118,26 +134,9 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
||||
}
|
||||
}
|
||||
|
||||
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, ref, metaHeaders, authConfig, output)
|
||||
err = s.daemon.PullImage(ref, metaHeaders, authConfig, output)
|
||||
}
|
||||
}
|
||||
// Check the error from pulling an image to make sure the request
|
||||
// was authorized. Modify the status if the request was
|
||||
// unauthorized to respond with 401 rather than 500.
|
||||
if err != nil && isAuthorizedError(err) {
|
||||
err = errcode.ErrorCodeUnauthorized.WithMessage(fmt.Sprintf("Authentication is required: %s", err))
|
||||
}
|
||||
} else { //import
|
||||
var newRef reference.Named
|
||||
if repo != "" {
|
||||
@@ -170,7 +169,7 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.backend.ImportImage(src, newRef, message, r.Body, output, newConfig)
|
||||
err = s.daemon.ImportImage(src, newRef, message, r.Body, output, newConfig)
|
||||
}
|
||||
if err != nil {
|
||||
if !output.Flushed() {
|
||||
@@ -183,7 +182,7 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
metaHeaders := map[string][]string{}
|
||||
for k, v := range r.Header {
|
||||
if strings.HasPrefix(k, "X-Meta-") {
|
||||
@@ -228,7 +227,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter,
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if err := s.backend.PushImage(ctx, ref, metaHeaders, authConfig, output); err != nil {
|
||||
if err := s.daemon.PushImage(ref, metaHeaders, authConfig, output); err != nil {
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
@@ -238,7 +237,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -254,7 +253,7 @@ func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r
|
||||
names = r.Form["names"]
|
||||
}
|
||||
|
||||
if err := s.backend.ExportImage(names, output); err != nil {
|
||||
if err := s.daemon.ExportImage(names, output); err != nil {
|
||||
if !output.Flushed() {
|
||||
return err
|
||||
}
|
||||
@@ -264,26 +263,11 @@ func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
quiet := httputils.BoolValueOrDefault(r, "quiet", true)
|
||||
|
||||
if !quiet {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
output := ioutils.NewWriteFlusher(w)
|
||||
defer output.Close()
|
||||
if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
|
||||
output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return s.backend.LoadImage(r.Body, w, quiet)
|
||||
func (s *router) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
return s.daemon.LoadImage(r.Body, w)
|
||||
}
|
||||
|
||||
func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -297,7 +281,7 @@ func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r
|
||||
force := httputils.BoolValue(r, "force")
|
||||
prune := !httputils.BoolValue(r, "noprune")
|
||||
|
||||
list, err := s.backend.ImageDelete(name, force, prune)
|
||||
list, err := s.daemon.ImageDelete(name, force, prune)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -305,8 +289,8 @@ func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r
|
||||
return httputils.WriteJSON(w, http.StatusOK, list)
|
||||
}
|
||||
|
||||
func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
imageInspect, err := s.backend.LookupImage(vars["name"])
|
||||
func (s *router) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
imageInspect, err := s.daemon.LookupImage(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -314,13 +298,13 @@ func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter
|
||||
return httputils.WriteJSON(w, http.StatusOK, imageInspect)
|
||||
}
|
||||
|
||||
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: The filter parameter could just be a match filter
|
||||
images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
|
||||
images, err := s.daemon.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -328,9 +312,9 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
|
||||
return httputils.WriteJSON(w, http.StatusOK, images)
|
||||
}
|
||||
|
||||
func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
name := vars["name"]
|
||||
history, err := s.backend.ImageHistory(name)
|
||||
history, err := s.daemon.ImageHistory(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -338,7 +322,7 @@ func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWrite
|
||||
return httputils.WriteJSON(w, http.StatusOK, history)
|
||||
}
|
||||
|
||||
func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -353,14 +337,14 @@ func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter,
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := s.backend.TagImage(newTag, vars["name"]); err != nil {
|
||||
if err := s.daemon.TagImage(newTag, vars["name"]); err != nil {
|
||||
return err
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
func (s *router) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -383,22 +367,9 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter
|
||||
headers[k] = v
|
||||
}
|
||||
}
|
||||
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("term"), config, headers)
|
||||
query, err := s.daemon.SearchRegistryForImages(r.Form.Get("term"), config, headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return httputils.WriteJSON(w, http.StatusOK, query.Results)
|
||||
}
|
||||
|
||||
func isAuthorizedError(err error) bool {
|
||||
if urlError, ok := err.(*url.Error); ok {
|
||||
err = urlError.Err
|
||||
}
|
||||
|
||||
if dError, ok := err.(errcode.Error); ok {
|
||||
if dError.ErrorCode() == errcode.ErrorCodeUnauthorized {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
107
api/server/router/local/local.go
Normal file
107
api/server/router/local/local.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/server/httputils"
|
||||
dkrouter "github.com/docker/docker/api/server/router"
|
||||
"github.com/docker/docker/daemon"
|
||||
)
|
||||
|
||||
// router is a docker router that talks with the local docker daemon.
|
||||
type router struct {
|
||||
daemon *daemon.Daemon
|
||||
routes []dkrouter.Route
|
||||
}
|
||||
|
||||
// localRoute defines an individual API route to connect with the docker daemon.
|
||||
// It implements router.Route.
|
||||
type localRoute struct {
|
||||
method string
|
||||
path string
|
||||
handler httputils.APIFunc
|
||||
}
|
||||
|
||||
// Handler returns the APIFunc to let the server wrap it in middlewares
|
||||
func (l localRoute) Handler() httputils.APIFunc {
|
||||
return l.handler
|
||||
}
|
||||
|
||||
// Method returns the http method that the route responds to.
|
||||
func (l localRoute) Method() string {
|
||||
return l.method
|
||||
}
|
||||
|
||||
// Path returns the subpath where the route responds to.
|
||||
func (l localRoute) Path() string {
|
||||
return l.path
|
||||
}
|
||||
|
||||
// NewRoute initializes a new local router for the reouter
|
||||
func NewRoute(method, path string, handler httputils.APIFunc) dkrouter.Route {
|
||||
return localRoute{method, path, handler}
|
||||
}
|
||||
|
||||
// NewGetRoute initializes a new route with the http method GET.
|
||||
func NewGetRoute(path string, handler httputils.APIFunc) dkrouter.Route {
|
||||
return NewRoute("GET", path, handler)
|
||||
}
|
||||
|
||||
// NewPostRoute initializes a new route with the http method POST.
|
||||
func NewPostRoute(path string, handler httputils.APIFunc) dkrouter.Route {
|
||||
return NewRoute("POST", path, handler)
|
||||
}
|
||||
|
||||
// NewPutRoute initializes a new route with the http method PUT.
|
||||
func NewPutRoute(path string, handler httputils.APIFunc) dkrouter.Route {
|
||||
return NewRoute("PUT", path, handler)
|
||||
}
|
||||
|
||||
// NewDeleteRoute initializes a new route with the http method DELETE.
|
||||
func NewDeleteRoute(path string, handler httputils.APIFunc) dkrouter.Route {
|
||||
return NewRoute("DELETE", path, handler)
|
||||
}
|
||||
|
||||
// NewOptionsRoute initializes a new route with the http method OPTIONS
|
||||
func NewOptionsRoute(path string, handler httputils.APIFunc) dkrouter.Route {
|
||||
return NewRoute("OPTIONS", path, handler)
|
||||
}
|
||||
|
||||
// NewHeadRoute initializes a new route with the http method HEAD.
|
||||
func NewHeadRoute(path string, handler httputils.APIFunc) dkrouter.Route {
|
||||
return NewRoute("HEAD", path, handler)
|
||||
}
|
||||
|
||||
// NewRouter initializes a local router with a new daemon.
|
||||
func NewRouter(daemon *daemon.Daemon) dkrouter.Router {
|
||||
r := &router{
|
||||
daemon: daemon,
|
||||
}
|
||||
r.initRoutes()
|
||||
return r
|
||||
}
|
||||
|
||||
// Routes returns the list of routes registered in the router.
|
||||
func (r *router) Routes() []dkrouter.Route {
|
||||
return r.routes
|
||||
}
|
||||
|
||||
// initRoutes initializes the routes in this router
|
||||
func (r *router) initRoutes() {
|
||||
r.routes = []dkrouter.Route{
|
||||
// OPTIONS
|
||||
// GET
|
||||
NewGetRoute("/images/json", r.getImagesJSON),
|
||||
NewGetRoute("/images/search", r.getImagesSearch),
|
||||
NewGetRoute("/images/get", r.getImagesGet),
|
||||
NewGetRoute("/images/{name:.*}/get", r.getImagesGet),
|
||||
NewGetRoute("/images/{name:.*}/history", r.getImagesHistory),
|
||||
NewGetRoute("/images/{name:.*}/json", r.getImagesByName),
|
||||
// POST
|
||||
NewPostRoute("/commit", r.postCommit),
|
||||
NewPostRoute("/images/create", r.postImagesCreate),
|
||||
NewPostRoute("/images/load", r.postImagesLoad),
|
||||
NewPostRoute("/images/{name:.*}/push", r.postImagesPush),
|
||||
NewPostRoute("/images/{name:.*}/tag", r.postImagesTag),
|
||||
// DELETE
|
||||
NewDeleteRoute("/images/{name:.*}", r.deleteImages),
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user