Compare commits
460 commits
feature_dy
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
f5cdc24dd3 | ||
|
34f1322664 | ||
|
2800ab0224 | ||
|
5dc1f65acc | ||
|
fe1ffa86bc | ||
|
8a8d91529d | ||
|
9466dd4e5a | ||
|
0316f34bf2 | ||
|
e1464fd317 | ||
|
35b26def43 | ||
|
a784441b62 | ||
|
1d0ea8ed7b | ||
|
208d68bd5c | ||
|
f361d443b7 | ||
|
53e18a9d9b | ||
|
7728c5e445 | ||
|
9690d843fa | ||
|
62d0fd45e7 | ||
|
81ba770eff | ||
|
a2ed1b5e80 | ||
|
55f88c35b9 | ||
|
742aab907b | ||
|
17a394f9af | ||
|
78c2ab6646 | ||
|
cdb4ba947a | ||
|
303f1899bb | ||
|
bf56f348be | ||
|
581be91482 | ||
|
be29c05a1e | ||
|
495a4af7cf | ||
|
74d442a058 | ||
|
795892662b | ||
|
ce101280fe | ||
|
4c7c63b557 | ||
|
492a10376c | ||
|
c79aa81772 | ||
|
800cb95821 | ||
|
2d1126ecc1 | ||
|
3b4d1e3ddf | ||
|
1e25ecefe4 | ||
|
db3c418ada | ||
|
98dcc5195e | ||
|
6b972e50fe | ||
|
e65b3f1316 | ||
|
efdba4f210 | ||
|
e9a023180f | ||
|
f1cb8a56c8 | ||
|
bfa6b923e7 | ||
|
f593b8d975 | ||
|
1f77c9a57f | ||
|
861aa2a621 | ||
|
9b6a019081 | ||
|
7e290869e7 | ||
|
016549532f | ||
|
974375f66c | ||
|
4ae059c714 | ||
|
d054b13dc3 | ||
|
244d5246c2 | ||
|
66809646d9 | ||
|
23f6bdd743 | ||
|
a837179414 | ||
|
6ca7b9e9fa | ||
|
f5e84a4939 | ||
|
0f5e2753a6 | ||
|
bdf3438b52 | ||
|
29c300c106 | ||
|
4798651387 | ||
|
f18781257e | ||
|
c9c3324300 | ||
|
5538da4923 | ||
|
fa7d949408 | ||
|
cf77113795 | ||
|
b4694b0d2d | ||
|
070cc010f7 | ||
|
dee21c0394 | ||
|
ae2e973db9 | ||
|
14b96e55d8 | ||
|
f656e60de5 | ||
|
740d4d1211 | ||
|
cc97b94f5d | ||
|
aeaeb84407 | ||
|
07a50201c9 | ||
|
1c481d34d9 | ||
|
a994bb839d | ||
|
c486db2d71 | ||
|
1fb7fffdb2 | ||
|
10f726344d | ||
|
8063102951 | ||
|
438b67feef | ||
|
be07be9904 | ||
|
92d213d2c1 | ||
|
dd3bdee21c | ||
|
90dfea7952 | ||
|
6c72ec2e85 | ||
|
ec84b86013 | ||
|
898b1f2a53 | ||
|
c5d5f938e3 | ||
|
3800c47fd2 | ||
|
a45e5cb13f | ||
|
8b31a894bd | ||
|
94097512db | ||
|
b23dd1ef37 | ||
|
79f6bcbe16 | ||
|
afe29bb697 | ||
|
45b2d0498d | ||
|
84f47e7bb3 | ||
|
6c329e56a2 | ||
|
71309e96e4 | ||
|
0c394fdd84 | ||
|
d80a17d8e0 | ||
|
55287010ce | ||
|
c2254191d4 | ||
|
5223c27422 | ||
|
dcfe05ce6c | ||
|
8f9c8094fb | ||
|
0e2d080a8a | ||
|
3226863cbc | ||
|
5afbf32400 | ||
|
74f429a5ad | ||
|
51bb5cee5b | ||
|
fd77cf43a6 | ||
|
c18c6c33b2 | ||
|
f877726503 | ||
|
6d62eb1d4a | ||
|
3390f32aec | ||
|
ae91d1f429 | ||
|
6e10631d9c | ||
|
e1e72e9563 | ||
|
f9a0506191 | ||
|
d3ddc3572c | ||
|
c192a281f8 | ||
|
a683c7c235 | ||
|
bbc9885aa2 | ||
|
92a6436714 | ||
|
d5a615b8c9 | ||
|
09a63caa37 | ||
|
228bafca0b | ||
|
76da6290b0 | ||
|
8b70616846 | ||
|
ec6566c02b | ||
|
3aa2a282f7 | ||
|
0d3efadf01 | ||
|
48818fdea7 | ||
|
411d6bcfd2 | ||
|
da8db4666b | ||
|
b75069ef13 | ||
|
b1fd12d0c1 | ||
|
15b0204758 | ||
|
cdb62b2b77 | ||
|
7df881dcbe | ||
|
91b0f0559e | ||
|
980d1a696b | ||
|
d1abdeb623 | ||
|
0ac367fd6b | ||
|
eb1a2cd911 | ||
|
b7839211af | ||
|
90bed67126 | ||
|
40b7b5830a | ||
|
08c6bbed05 | ||
|
d9e1218235 | ||
|
63f6c1205d | ||
|
aa985ba889 | ||
|
dd36fd3622 | ||
|
1251e51ad0 | ||
|
9ebf151ac2 | ||
|
7c4d584e58 | ||
|
93e082742a | ||
|
f7046a6d68 | ||
|
cd1648d62c | ||
|
8a800e1292 | ||
|
1cb4180b1a | ||
|
451cd548a0 | ||
|
6335cc258f | ||
|
17b3ff188d | ||
|
f08b3486c8 | ||
|
f3adfea35b | ||
|
e1817db884 | ||
|
de8636b78c | ||
|
97cb7f35b0 | ||
|
2eb7a17225 | ||
|
06a4c2f61b | ||
|
d37f816427 | ||
|
569d18aef9 | ||
|
2e1e6307dd | ||
|
b2bd465760 | ||
|
f730f3ab77 | ||
|
16128bbac4 | ||
|
b089e91688 | ||
|
6133840f49 | ||
|
a927fbdb9b | ||
|
d8bde9b97e | ||
|
bd41413d57 | ||
|
166874ade9 | ||
|
a5c2fdc5b8 | ||
|
9da0f07c92 | ||
|
877d706b38 | ||
|
d1f36d46c9 | ||
|
642075f42c | ||
|
15de837aa8 | ||
|
7a195dd5ca | ||
|
69299d93d9 | ||
|
f9187b2572 | ||
|
b424c3d870 | ||
|
3f9f073cef | ||
|
78238ef1a0 | ||
|
efa4c3bb5f | ||
|
6d66d0367e | ||
|
c88728f217 | ||
|
6b73a9ab89 | ||
|
fd32d5f962 | ||
|
5a74b806f0 | ||
|
9930542dc5 | ||
|
8d7e4cd388 | ||
|
90705d2fb8 | ||
|
b12bd4004a | ||
|
059f301d54 | ||
|
f95ac7db95 | ||
|
3354cf98e3 | ||
|
ef859e1b21 | ||
|
90070b3367 | ||
|
0101db11ef | ||
|
1300e580d1 | ||
|
16eb3b658d | ||
|
02bf4a2887 | ||
|
610440eb19 | ||
|
ee94aa48d7 | ||
|
53bd46af5c | ||
|
6411087274 | ||
|
0a302c7fa9 | ||
|
633401c9e8 | ||
|
0b0d470281 | ||
|
5e4b81a578 | ||
|
40efb602d6 | ||
|
9bf62ca7b3 | ||
|
f0ee5720a5 | ||
|
13f8189f2a | ||
|
94deea2951 | ||
|
de21eb6c96 | ||
|
57212c909b | ||
|
c0995537be | ||
|
2fdb2ac270 | ||
|
a64394ece5 | ||
|
5f37adaa41 | ||
|
88530ef7a0 | ||
|
7d9f067716 | ||
|
d260b18f2f | ||
|
9caa7a81bc | ||
|
b7446e89bf | ||
|
0cbe144826 | ||
|
4a75b72fd3 | ||
|
dc35ae6421 | ||
|
eefe9670bd | ||
|
003aa051b4 | ||
|
db0a4ec1c8 | ||
|
795e11d5fb | ||
|
32e2260be2 | ||
|
8c05756141 | ||
|
dc53e37d98 | ||
|
e4d5a0a17c | ||
|
8f6758278d | ||
|
328069bb4d | ||
|
dc49f84dcc | ||
|
54aef6251e | ||
|
0dae0957e5 | ||
|
20aecf1d7b | ||
|
5f588fbf9b | ||
|
0d8f4ac7b8 | ||
|
276fdce3d9 | ||
|
e8d7941ca6 | ||
|
f5c6357c6d | ||
|
7655a3d91f | ||
|
321d636e76 | ||
|
132abc6de5 | ||
|
60d9c5dfad | ||
|
1d47ef7b80 | ||
|
f6224f78ba | ||
|
ad7ab0853c | ||
|
7b47fb13cf | ||
|
f186e1da1c | ||
|
ec2aa05cdf | ||
|
c1532332ad | ||
|
6bae7ca597 | ||
|
9e3f78b8c8 | ||
|
b0cef05626 | ||
|
426afb3a4c | ||
|
fcaffa38bc | ||
|
c94f28805e | ||
|
6fcea22b0a | ||
|
9986e8ca7c | ||
|
749f6afb45 | ||
|
62797237b9 | ||
|
7bc438a534 | ||
|
fc7e8f42d7 | ||
|
86b030c1ca | ||
|
f0cc927784 | ||
|
7f02f9e450 | ||
|
ed42a4909a | ||
|
dabcfdc5a6 | ||
|
b4dd9b4376 | ||
|
83389a1480 | ||
|
fb7b0ddfc3 | ||
|
23bef416bd | ||
|
607ae5d128 | ||
|
34c706e759 | ||
|
492844e09c | ||
|
fc1d3647c6 | ||
|
f2805894c8 | ||
|
6fca8d6e67 | ||
|
8b93700d99 | ||
|
6664ec7039 | ||
|
e3c37a46e2 | ||
|
13076371a6 | ||
|
005c6e0236 | ||
|
d707ea2428 | ||
|
1ba5b3b553 | ||
|
fea8bd5114 | ||
|
85b4d46b4f | ||
|
4ecb17cc4c | ||
|
a4c32bce50 | ||
|
5cb406d511 | ||
|
cbcbcb02c5 | ||
|
c7b0da2622 | ||
|
b5db8eeeb8 | ||
|
585cdeb571 | ||
|
35b29a609e | ||
|
32ac467992 | ||
|
9f664468ea | ||
|
277ed486c9 | ||
|
ff87ad884c | ||
|
db0bc08b85 | ||
|
70c5e31509 | ||
|
aac2f6c8b7 | ||
|
eaa90a3c6d | ||
|
e9864ce8b9 | ||
|
8777e97b72 | ||
|
f411848591 | ||
|
e8ecc6dc55 | ||
|
bc3c7b0525 | ||
|
8cedd7b3a4 | ||
|
1503fa2109 | ||
|
118c8ee1f1 | ||
|
1bfbeca726 | ||
|
e69837454a | ||
|
c9eba1a5bb | ||
|
e5b5e44386 | ||
|
1a860d8c19 | ||
|
c785740af7 | ||
|
f74613907d | ||
|
fda42e5ef9 | ||
|
3800056b88 | ||
|
d66208108d | ||
|
7484e51bf6 | ||
|
3c5f85abd1 | ||
|
bb49a1685d | ||
|
4abf680c76 | ||
|
8710fa58ae | ||
|
5db89f0ca6 | ||
|
13e0608bc8 | ||
|
2c58ce1a7f | ||
|
30578ca329 | ||
|
a2015272c1 | ||
|
d9e0121fef | ||
|
23f8ca88e1 | ||
|
1618b49d5b | ||
|
5f6282db7d | ||
|
3f771adca6 | ||
|
e0b4f55f2b | ||
|
860b28c5b9 | ||
|
06fa77aa11 | ||
|
9c88801a12 | ||
|
7a8efe719e | ||
|
3d7803ec8c | ||
|
edc3ab29cd | ||
|
e18fe7d3f4 | ||
|
cb851f6598 | ||
|
f7fb45f59a | ||
|
1d95716792 | ||
|
91c507a39a | ||
|
20f225005a | ||
|
5cfdfbdce5 | ||
|
5e5156afa3 | ||
|
fb90a182a9 | ||
|
a11fe173d5 | ||
|
55ea440428 | ||
|
f86db6b226 | ||
|
caa175c710 | ||
|
b9f76758ae | ||
|
a97d7c0c15 | ||
|
5ccd03d28a | ||
|
5b1b6afae2 | ||
|
258345ba0d | ||
|
1e2f10eb65 | ||
|
a1576d6e21 | ||
|
49bb3242da | ||
|
b0f98e9382 | ||
|
079f5b179d | ||
|
646fc9702c | ||
|
5573a13f15 | ||
|
a528cc1fc3 | ||
|
f01bcc8f62 | ||
|
83f857ca12 | ||
|
9098f843d6 | ||
|
df1e488526 | ||
|
37ca688dc0 | ||
|
ce936a8591 | ||
|
7d8dab5fdc | ||
|
a40abc69f2 | ||
|
7cc8e701c9 | ||
|
212f47c318 | ||
|
05ac637aec | ||
|
1d7824702b | ||
|
b7d5d9bfed | ||
|
1935c8d50b | ||
|
f4a1d3e0d5 | ||
|
7f510ae9c9 | ||
|
e85ef3c019 | ||
|
3da015f8aa | ||
|
ac05d143d8 | ||
|
2f728896a0 | ||
|
c3e06c6069 | ||
|
818ba4babf | ||
|
1f0a9dbca0 | ||
|
a73ed75f5a | ||
|
b22c6b7a4e | ||
|
7b9ebdc54f | ||
|
0dd6ca97af | ||
|
364d2e4a5b | ||
|
0700fa570d | ||
|
81a47d9766 | ||
|
d4c3e88426 | ||
|
11c74e07e0 | ||
|
d2da3173db | ||
|
299b90b0bd | ||
|
f6c62456b8 | ||
|
29e5cd5304 | ||
|
eaf60fffee | ||
|
45bb7c9cc9 | ||
|
08b06dc023 | ||
|
4ac39769dc | ||
|
d7905dc725 | ||
|
0810eba2ad | ||
|
3161f9d1fd | ||
|
50133d6372 | ||
|
6a8e2ca84f | ||
|
df5327f76f | ||
|
4f87c80073 | ||
|
9a58c91051 | ||
|
62d8d910b5 | ||
|
72bdf0e320 | ||
|
b38e5838b7 | ||
|
75c2e524a1 | ||
|
20036597bf | ||
|
95daa793b8 | ||
|
b1993c9530 | ||
|
2d20471cd7 | ||
|
fb0bebc4b6 | ||
|
7f565ed65a | ||
|
545102ea07 | ||
|
245ca4659e | ||
|
959659c3fa |
1521 changed files with 421133 additions and 69424 deletions
3
.github/CODE_OF_CONDUCT.md
vendored
Normal file
3
.github/CODE_OF_CONDUCT.md
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
## Docker Distribution Community Code of Conduct
|
||||||
|
|
||||||
|
Docker Distribution follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -35,3 +35,4 @@ bin/*
|
||||||
# Editor/IDE specific files.
|
# Editor/IDE specific files.
|
||||||
*.sublime-project
|
*.sublime-project
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
.idea/*
|
||||||
|
|
20
.golangci.yml
Normal file
20
.golangci.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- structcheck
|
||||||
|
- varcheck
|
||||||
|
- staticcheck
|
||||||
|
- unconvert
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- golint
|
||||||
|
- ineffassign
|
||||||
|
- vet
|
||||||
|
- unused
|
||||||
|
- misspell
|
||||||
|
disable:
|
||||||
|
- errcheck
|
||||||
|
|
||||||
|
run:
|
||||||
|
deadline: 2m
|
||||||
|
skip-dirs:
|
||||||
|
- vendor
|
14
.mailmap
14
.mailmap
|
@ -16,3 +16,17 @@ davidli <wenquan.li@hp.com> davidli <wenquan.li@hpe.com>
|
||||||
Omer Cohen <git@omer.io> Omer Cohen <git@omerc.net>
|
Omer Cohen <git@omer.io> Omer Cohen <git@omerc.net>
|
||||||
Eric Yang <windfarer@gmail.com> Eric Yang <Windfarer@users.noreply.github.com>
|
Eric Yang <windfarer@gmail.com> Eric Yang <Windfarer@users.noreply.github.com>
|
||||||
Nikita Tarasov <nikita@mygento.ru> Nikita <luckyraul@users.noreply.github.com>
|
Nikita Tarasov <nikita@mygento.ru> Nikita <luckyraul@users.noreply.github.com>
|
||||||
|
Yu Wang <yuwa@microsoft.com> yuwaMSFT2 <yuwa@microsoft.com>
|
||||||
|
Yu Wang <yuwa@microsoft.com> Yu Wang (UC) <yuwa@microsoft.com>
|
||||||
|
Olivier Gambier <olivier@docker.com> dmp <dmp@loaner.local>
|
||||||
|
Olivier Gambier <olivier@docker.com> Olivier <o+github@gambier.email>
|
||||||
|
Olivier Gambier <olivier@docker.com> Olivier <dmp42@users.noreply.github.com>
|
||||||
|
Elsan Li 李楠 <elsanli@tencent.com> elsanli(李楠) <elsanli@tencent.com>
|
||||||
|
Rui Cao <ruicao@alauda.io> ruicao <ruicao@alauda.io>
|
||||||
|
Gwendolynne Barr <gwendolynne.barr@docker.com> gbarr01 <gwendolynne.barr@docker.com>
|
||||||
|
Haibing Zhou 周海兵 <zhouhaibing089@gmail.com> zhouhaibing089 <zhouhaibing089@gmail.com>
|
||||||
|
Feng Honglin <tifayuki@gmail.com> tifayuki <tifayuki@gmail.com>
|
||||||
|
Helen Xie <xieyulin821@harmonycloud.cn> Helen-xie <xieyulin821@harmonycloud.cn>
|
||||||
|
Mike Brown <brownwm@us.ibm.com> Mike Brown <mikebrow@users.noreply.github.com>
|
||||||
|
Manish Tomar <manish.tomar@docker.com> Manish Tomar <manishtomar@users.noreply.github.com>
|
||||||
|
Sakeven Jiang <jc5930@sina.cn> sakeven <jc5930@sina.cn>
|
||||||
|
|
56
.travis.yml
Normal file
56
.travis.yml
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
dist: bionic
|
||||||
|
sudo: required
|
||||||
|
# setup travis so that we can run containers for integration tests
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
- arch: amd64
|
||||||
|
- arch: s390x
|
||||||
|
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- "1.14.x"
|
||||||
|
|
||||||
|
go_import_path: github.com/docker/distribution
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- python-minimal
|
||||||
|
|
||||||
|
|
||||||
|
env:
|
||||||
|
- TRAVIS_GOOS=linux DOCKER_BUILDTAGS="include_oss include_gcs" TRAVIS_CGO_ENABLED=1
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- uname -r
|
||||||
|
- sudo apt-get -q update
|
||||||
|
|
||||||
|
install:
|
||||||
|
- cd /tmp && go get -u github.com/vbatts/git-validation
|
||||||
|
# TODO: Add enforcement of license
|
||||||
|
# - go get -u github.com/kunalkushwaha/ltag
|
||||||
|
- cd $TRAVIS_BUILD_DIR
|
||||||
|
|
||||||
|
script:
|
||||||
|
- export GOOS=$TRAVIS_GOOS
|
||||||
|
- export CGO_ENABLED=$TRAVIS_CGO_ENABLED
|
||||||
|
- DCO_VERBOSITY=-q script/validate/dco
|
||||||
|
- GOOS=linux GO111MODULE=on script/setup/install-dev-tools
|
||||||
|
- script/validate/vendor
|
||||||
|
- go build -i .
|
||||||
|
- make check
|
||||||
|
- make build
|
||||||
|
- make binaries
|
||||||
|
# Currently takes too long
|
||||||
|
#- if [ "$GOOS" = "linux" ]; then make test-race ; fi
|
||||||
|
- if [ "$GOOS" = "linux" ]; then make coverage ; fi
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash) -F linux
|
||||||
|
|
||||||
|
before_deploy:
|
||||||
|
# Run tests with storage driver configurations
|
182
AUTHORS
182
AUTHORS
|
@ -1,182 +0,0 @@
|
||||||
a-palchikov <deemok@gmail.com>
|
|
||||||
Aaron Lehmann <aaron.lehmann@docker.com>
|
|
||||||
Aaron Schlesinger <aschlesinger@deis.com>
|
|
||||||
Aaron Vinson <avinson.public@gmail.com>
|
|
||||||
Adam Duke <adam.v.duke@gmail.com>
|
|
||||||
Adam Enger <adamenger@gmail.com>
|
|
||||||
Adrian Mouat <adrian.mouat@gmail.com>
|
|
||||||
Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
|
|
||||||
Alex Chan <alex.chan@metaswitch.com>
|
|
||||||
Alex Elman <aelman@indeed.com>
|
|
||||||
Alexey Gladkov <gladkov.alexey@gmail.com>
|
|
||||||
allencloud <allen.sun@daocloud.io>
|
|
||||||
amitshukla <ashukla73@hotmail.com>
|
|
||||||
Amy Lindburg <amy.lindburg@docker.com>
|
|
||||||
Andrew Hsu <andrewhsu@acm.org>
|
|
||||||
Andrew Meredith <andymeredith@gmail.com>
|
|
||||||
Andrew T Nguyen <andrew.nguyen@docker.com>
|
|
||||||
Andrey Kostov <kostov.andrey@gmail.com>
|
|
||||||
Andy Goldstein <agoldste@redhat.com>
|
|
||||||
Anis Elleuch <vadmeste@gmail.com>
|
|
||||||
Anton Tiurin <noxiouz@yandex.ru>
|
|
||||||
Antonio Mercado <amercado@thinknode.com>
|
|
||||||
Antonio Murdaca <runcom@redhat.com>
|
|
||||||
Anusha Ragunathan <anusha@docker.com>
|
|
||||||
Arien Holthuizen <aholthuizen@schubergphilis.com>
|
|
||||||
Arnaud Porterie <arnaud.porterie@docker.com>
|
|
||||||
Arthur Baars <arthur@semmle.com>
|
|
||||||
Asuka Suzuki <hello@tanksuzuki.com>
|
|
||||||
Avi Miller <avi.miller@oracle.com>
|
|
||||||
Ayose Cazorla <ayosec@gmail.com>
|
|
||||||
BadZen <dave.trombley@gmail.com>
|
|
||||||
Ben Bodenmiller <bbodenmiller@hotmail.com>
|
|
||||||
Ben Firshman <ben@firshman.co.uk>
|
|
||||||
bin liu <liubin0329@gmail.com>
|
|
||||||
Brian Bland <brian.bland@docker.com>
|
|
||||||
burnettk <burnettk@gmail.com>
|
|
||||||
Carson A <ca@carsonoid.net>
|
|
||||||
Cezar Sa Espinola <cezarsa@gmail.com>
|
|
||||||
Charles Smith <charles.smith@docker.com>
|
|
||||||
Chris Dillon <squarism@gmail.com>
|
|
||||||
cuiwei13 <cuiwei13@pku.edu.cn>
|
|
||||||
cyli <cyli@twistedmatrix.com>
|
|
||||||
Daisuke Fujita <dtanshi45@gmail.com>
|
|
||||||
Daniel Huhn <daniel@danielhuhn.de>
|
|
||||||
Darren Shepherd <darren@rancher.com>
|
|
||||||
Dave Trombley <dave.trombley@gmail.com>
|
|
||||||
Dave Tucker <dt@docker.com>
|
|
||||||
David Lawrence <david.lawrence@docker.com>
|
|
||||||
David Verhasselt <david@crowdway.com>
|
|
||||||
David Xia <dxia@spotify.com>
|
|
||||||
davidli <wenquan.li@hp.com>
|
|
||||||
Dejan Golja <dejan@golja.org>
|
|
||||||
Derek McGowan <derek@mcgstyle.net>
|
|
||||||
Diogo Mónica <diogo.monica@gmail.com>
|
|
||||||
DJ Enriquez <dj.enriquez@infospace.com>
|
|
||||||
Donald Huang <don.hcd@gmail.com>
|
|
||||||
Doug Davis <dug@us.ibm.com>
|
|
||||||
Edgar Lee <edgar.lee@docker.com>
|
|
||||||
Eric Yang <windfarer@gmail.com>
|
|
||||||
Fabio Berchtold <jamesclonk@jamesclonk.ch>
|
|
||||||
Fabio Huser <fabio@fh1.ch>
|
|
||||||
farmerworking <farmerworking@gmail.com>
|
|
||||||
Felix Yan <felixonmars@archlinux.org>
|
|
||||||
Florentin Raud <florentin.raud@gmail.com>
|
|
||||||
Frank Chen <frankchn@gmail.com>
|
|
||||||
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
|
|
||||||
gabriell nascimento <gabriell@bluesoft.com.br>
|
|
||||||
Gleb Schukin <gschukin@ptsecurity.com>
|
|
||||||
harche <p.harshal@gmail.com>
|
|
||||||
Henri Gomez <henri.gomez@gmail.com>
|
|
||||||
Hu Keping <hukeping@huawei.com>
|
|
||||||
Hua Wang <wanghua.humble@gmail.com>
|
|
||||||
HuKeping <hukeping@huawei.com>
|
|
||||||
Ian Babrou <ibobrik@gmail.com>
|
|
||||||
igayoso <igayoso@gmail.com>
|
|
||||||
Jack Griffin <jackpg14@gmail.com>
|
|
||||||
James Findley <jfindley@fastmail.com>
|
|
||||||
Jason Freidman <jason.freidman@gmail.com>
|
|
||||||
Jason Heiss <jheiss@aput.net>
|
|
||||||
Jeff Nickoloff <jeff@allingeek.com>
|
|
||||||
Jess Frazelle <acidburn@google.com>
|
|
||||||
Jessie Frazelle <jessie@docker.com>
|
|
||||||
jhaohai <jhaohai@foxmail.com>
|
|
||||||
Jianqing Wang <tsing@jianqing.org>
|
|
||||||
Jihoon Chung <jihoon@gmail.com>
|
|
||||||
Joao Fernandes <joao.fernandes@docker.com>
|
|
||||||
John Mulhausen <john@docker.com>
|
|
||||||
John Starks <jostarks@microsoft.com>
|
|
||||||
Jon Johnson <jonjohnson@google.com>
|
|
||||||
Jon Poler <jonathan.poler@apcera.com>
|
|
||||||
Jonathan Boulle <jonathanboulle@gmail.com>
|
|
||||||
Jordan Liggitt <jliggitt@redhat.com>
|
|
||||||
Josh Chorlton <josh.chorlton@docker.com>
|
|
||||||
Josh Hawn <josh.hawn@docker.com>
|
|
||||||
Julien Fernandez <julien.fernandez@gmail.com>
|
|
||||||
Ke Xu <leonhartx.k@gmail.com>
|
|
||||||
Keerthan Mala <kmala@engineyard.com>
|
|
||||||
Kelsey Hightower <kelsey.hightower@gmail.com>
|
|
||||||
Kenneth Lim <kennethlimcp@gmail.com>
|
|
||||||
Kenny Leung <kleung@google.com>
|
|
||||||
Li Yi <denverdino@gmail.com>
|
|
||||||
Liu Hua <sdu.liu@huawei.com>
|
|
||||||
liuchang0812 <liuchang0812@gmail.com>
|
|
||||||
Lloyd Ramey <lnr0626@gmail.com>
|
|
||||||
Louis Kottmann <louis.kottmann@gmail.com>
|
|
||||||
Luke Carpenter <x@rubynerd.net>
|
|
||||||
Marcus Martins <marcus@docker.com>
|
|
||||||
Mary Anthony <mary@docker.com>
|
|
||||||
Matt Bentley <mbentley@mbentley.net>
|
|
||||||
Matt Duch <matt@learnmetrics.com>
|
|
||||||
Matt Moore <mattmoor@google.com>
|
|
||||||
Matt Robenolt <matt@ydekproductions.com>
|
|
||||||
Matthew Green <greenmr@live.co.uk>
|
|
||||||
Michael Prokop <mika@grml.org>
|
|
||||||
Michal Minar <miminar@redhat.com>
|
|
||||||
Michal Minář <miminar@redhat.com>
|
|
||||||
Mike Brown <brownwm@us.ibm.com>
|
|
||||||
Miquel Sabaté <msabate@suse.com>
|
|
||||||
Misty Stanley-Jones <misty@apache.org>
|
|
||||||
Misty Stanley-Jones <misty@docker.com>
|
|
||||||
Morgan Bauer <mbauer@us.ibm.com>
|
|
||||||
moxiegirl <mary@docker.com>
|
|
||||||
Nathan Sullivan <nathan@nightsys.net>
|
|
||||||
nevermosby <robolwq@qq.com>
|
|
||||||
Nghia Tran <tcnghia@gmail.com>
|
|
||||||
Nikita Tarasov <nikita@mygento.ru>
|
|
||||||
Noah Treuhaft <noah.treuhaft@docker.com>
|
|
||||||
Nuutti Kotivuori <nuutti.kotivuori@poplatek.fi>
|
|
||||||
Oilbeater <liumengxinfly@gmail.com>
|
|
||||||
Olivier Gambier <olivier@docker.com>
|
|
||||||
Olivier Jacques <olivier.jacques@hp.com>
|
|
||||||
Omer Cohen <git@omer.io>
|
|
||||||
Patrick Devine <patrick.devine@docker.com>
|
|
||||||
Phil Estes <estesp@linux.vnet.ibm.com>
|
|
||||||
Philip Misiowiec <philip@atlashealth.com>
|
|
||||||
Pierre-Yves Ritschard <pyr@spootnik.org>
|
|
||||||
Qiao Anran <qiaoanran@gmail.com>
|
|
||||||
Randy Barlow <randy@electronsweatshop.com>
|
|
||||||
Richard Scothern <richard.scothern@docker.com>
|
|
||||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
|
||||||
Rusty Conover <rusty@luckydinosaur.com>
|
|
||||||
Sean Boran <Boran@users.noreply.github.com>
|
|
||||||
Sebastiaan van Stijn <github@gone.nl>
|
|
||||||
Sebastien Coavoux <s.coavoux@free.fr>
|
|
||||||
Serge Dubrouski <sergeyfd@gmail.com>
|
|
||||||
Sharif Nassar <sharif@mrwacky.com>
|
|
||||||
Shawn Falkner-Horine <dreadpirateshawn@gmail.com>
|
|
||||||
Shreyas Karnik <karnik.shreyas@gmail.com>
|
|
||||||
Simon Thulbourn <simon+github@thulbourn.com>
|
|
||||||
spacexnice <yaoyao.xyy@alibaba-inc.com>
|
|
||||||
Spencer Rinehart <anubis@overthemonkey.com>
|
|
||||||
Stan Hu <stanhu@gmail.com>
|
|
||||||
Stefan Majewsky <stefan.majewsky@sap.com>
|
|
||||||
Stefan Weil <sw@weilnetz.de>
|
|
||||||
Stephen J Day <stephen.day@docker.com>
|
|
||||||
Sungho Moon <sungho.moon@navercorp.com>
|
|
||||||
Sven Dowideit <SvenDowideit@home.org.au>
|
|
||||||
Sylvain Baubeau <sbaubeau@redhat.com>
|
|
||||||
Ted Reed <ted.reed@gmail.com>
|
|
||||||
tgic <farmer1992@gmail.com>
|
|
||||||
Thomas Sjögren <konstruktoid@users.noreply.github.com>
|
|
||||||
Tianon Gravi <admwiggin@gmail.com>
|
|
||||||
Tibor Vass <teabee89@gmail.com>
|
|
||||||
Tonis Tiigi <tonistiigi@gmail.com>
|
|
||||||
Tony Holdstock-Brown <tony@docker.com>
|
|
||||||
Trevor Pounds <trevor.pounds@gmail.com>
|
|
||||||
Troels Thomsen <troels@thomsen.io>
|
|
||||||
Victor Vieux <vieux@docker.com>
|
|
||||||
Victoria Bialas <victoria.bialas@docker.com>
|
|
||||||
Vincent Batts <vbatts@redhat.com>
|
|
||||||
Vincent Demeester <vincent@sbr.pm>
|
|
||||||
Vincent Giersch <vincent.giersch@ovh.net>
|
|
||||||
W. Trevor King <wking@tremily.us>
|
|
||||||
weiyuan.yl <weiyuan.yl@alibaba-inc.com>
|
|
||||||
xg.song <xg.song@venusource.com>
|
|
||||||
xiekeyang <xiekeyang@huawei.com>
|
|
||||||
Yann ROBERT <yann.robert@anantaplex.fr>
|
|
||||||
yaoyao.xyy <yaoyao.xyy@alibaba-inc.com>
|
|
||||||
yuexiao-wang <wang.yuexiao@zte.com.cn>
|
|
||||||
yuzou <zouyu7@huawei.com>
|
|
||||||
zhouhaibing089 <zhouhaibing089@gmail.com>
|
|
||||||
姜继忠 <jizhong.jiangjz@alibaba-inc.com>
|
|
|
@ -83,7 +83,7 @@ build:
|
||||||
+ lint
|
+ lint
|
||||||
+ build
|
+ build
|
||||||
github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar
|
github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar
|
||||||
github.com/Sirupsen/logrus
|
github.com/sirupsen/logrus
|
||||||
github.com/docker/libtrust
|
github.com/docker/libtrust
|
||||||
...
|
...
|
||||||
github.com/yvasiyarov/gorelic
|
github.com/yvasiyarov/gorelic
|
||||||
|
@ -108,7 +108,7 @@ directory. This includes formatting, vetting, linting, building,
|
||||||
testing and generating tagged binaries. We can verify this worked by running
|
testing and generating tagged binaries. We can verify this worked by running
|
||||||
the registry binary generated in the "./bin" directory:
|
the registry binary generated in the "./bin" directory:
|
||||||
|
|
||||||
$ ./bin/registry -version
|
$ ./bin/registry --version
|
||||||
./bin/registry github.com/docker/distribution v2.0.0-alpha.2-80-g16d8b2c.m
|
./bin/registry github.com/docker/distribution v2.0.0-alpha.2-80-g16d8b2c.m
|
||||||
|
|
||||||
### Optional build tags
|
### Optional build tags
|
||||||
|
|
147
CONTRIBUTING.md
147
CONTRIBUTING.md
|
@ -1,14 +1,15 @@
|
||||||
# Contributing to the registry
|
# Contributing to the registry
|
||||||
|
|
||||||
## Before reporting an issue...
|
## Before reporting an issue...
|
||||||
|
|
||||||
### If your problem is with...
|
### If your problem is with...
|
||||||
|
|
||||||
- automated builds
|
- automated builds or your [Docker Hub](https://hub.docker.com/) account
|
||||||
- your account on the [Docker Hub](https://hub.docker.com/)
|
- Report it to [Hub Support](https://hub.docker.com/support/)
|
||||||
- any other [Docker Hub](https://hub.docker.com/) issue
|
- Distributions of Docker for desktop or Linux
|
||||||
|
- Report [Mac Desktop issues](https://github.com/docker/for-mac)
|
||||||
Then please do not report your issue here - you should instead report it to [https://support.docker.com](https://support.docker.com)
|
- Report [Windows Desktop issues](https://github.com/docker/for-win)
|
||||||
|
- Report [Linux issues](https://github.com/docker/for-linux)
|
||||||
|
|
||||||
### If you...
|
### If you...
|
||||||
|
|
||||||
|
@ -16,10 +17,16 @@ Then please do not report your issue here - you should instead report it to [htt
|
||||||
- can't figure out something
|
- can't figure out something
|
||||||
- are not sure what's going on or what your problem is
|
- are not sure what's going on or what your problem is
|
||||||
|
|
||||||
Then please do not open an issue here yet - you should first try one of the following support forums:
|
Please ask first in the #distribution channel on Docker community slack.
|
||||||
|
[Click here for an invite to Docker community slack](https://dockr.ly/slack)
|
||||||
|
|
||||||
- irc: #docker-distribution on freenode
|
### Reporting security issues
|
||||||
- mailing-list: <distribution@dockerproject.org> or https://groups.google.com/a/dockerproject.org/forum/#!forum/distribution
|
|
||||||
|
The Docker maintainers take security seriously. If you discover a security
|
||||||
|
issue, please bring it to their attention right away!
|
||||||
|
|
||||||
|
Please **DO NOT** file a public issue, instead send your report privately to
|
||||||
|
[security@docker.com](mailto:security@docker.com).
|
||||||
|
|
||||||
## Reporting an issue properly
|
## Reporting an issue properly
|
||||||
|
|
||||||
|
@ -27,7 +34,7 @@ By following these simple rules you will get better and faster feedback on your
|
||||||
|
|
||||||
- search the bugtracker for an already reported issue
|
- search the bugtracker for an already reported issue
|
||||||
|
|
||||||
### If you found an issue that describes your problem:
|
### If you found an issue that describes your problem:
|
||||||
|
|
||||||
- please read other user comments first, and confirm this is the same issue: a given error condition might be indicative of different problems - you may also find a workaround in the comments
|
- please read other user comments first, and confirm this is the same issue: a given error condition might be indicative of different problems - you may also find a workaround in the comments
|
||||||
- please refrain from adding "same thing here" or "+1" comments
|
- please refrain from adding "same thing here" or "+1" comments
|
||||||
|
@ -43,7 +50,7 @@ By following these simple rules you will get better and faster feedback on your
|
||||||
2. copy the output of:
|
2. copy the output of:
|
||||||
- `docker version`
|
- `docker version`
|
||||||
- `docker info`
|
- `docker info`
|
||||||
- `docker exec <registry-container> registry -version`
|
- `docker exec <registry-container> registry --version`
|
||||||
3. copy the command line you used to launch your Registry
|
3. copy the command line you used to launch your Registry
|
||||||
4. restart your docker daemon in debug mode (add `-D` to the daemon launch arguments)
|
4. restart your docker daemon in debug mode (add `-D` to the daemon launch arguments)
|
||||||
5. reproduce your problem and get your docker daemon logs showing the error
|
5. reproduce your problem and get your docker daemon logs showing the error
|
||||||
|
@ -51,90 +58,72 @@ By following these simple rules you will get better and faster feedback on your
|
||||||
7. provide any relevant detail about your specific Registry configuration (e.g., storage backend used)
|
7. provide any relevant detail about your specific Registry configuration (e.g., storage backend used)
|
||||||
8. indicate if you are using an enterprise proxy, Nginx, or anything else between you and your Registry
|
8. indicate if you are using an enterprise proxy, Nginx, or anything else between you and your Registry
|
||||||
|
|
||||||
## Contributing a patch for a known bug, or a small correction
|
## Contributing Code
|
||||||
|
|
||||||
|
Contributions should be made via pull requests. Pull requests will be reviewed
|
||||||
|
by one or more maintainers or reviewers and merged when acceptable.
|
||||||
|
|
||||||
You should follow the basic GitHub workflow:
|
You should follow the basic GitHub workflow:
|
||||||
|
|
||||||
1. fork
|
1. Use your own [fork](https://help.github.com/en/articles/about-forks)
|
||||||
2. commit a change
|
2. Create your [change](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes)
|
||||||
3. make sure the tests pass
|
3. Test your code
|
||||||
4. PR
|
4. [Commit](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages) your work, always [sign your commits](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages)
|
||||||
|
5. Push your change to your fork and create a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork)
|
||||||
|
|
||||||
Additionally, you must [sign your commits](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work). It's very simple:
|
Refer to [containerd's contribution guide](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes)
|
||||||
|
for tips on creating a successful contribution.
|
||||||
|
|
||||||
- configure your name with git: `git config user.name "Real Name" && git config user.email mail@example.com`
|
## Sign your work
|
||||||
- sign your commits using `-s`: `git commit -s -m "My commit"`
|
|
||||||
|
|
||||||
Some simple rules to ensure quick merge:
|
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||||
|
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||||
|
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||||
|
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||||
|
|
||||||
- clearly point to the issue(s) you want to fix in your PR comment (e.g., `closes #12345`)
|
```
|
||||||
- prefer multiple (smaller) PRs addressing individual issues over a big one trying to address multiple issues at once
|
Developer Certificate of Origin
|
||||||
- if you need to amend your PR following comments, please squash instead of adding more commits
|
Version 1.1
|
||||||
|
|
||||||
## Contributing new features
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
660 York Street, Suite 102,
|
||||||
|
San Francisco, CA 94110 USA
|
||||||
|
|
||||||
You are heavily encouraged to first discuss what you want to do. You can do so on the irc channel, or by opening an issue that clearly describes the use case you want to fulfill, or the problem you are trying to solve.
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
If this is a major new feature, you should then submit a proposal that describes your technical solution and reasoning.
|
Developer's Certificate of Origin 1.1
|
||||||
If you did discuss it first, this will likely be greenlighted very fast. It's advisable to address all feedback on this proposal before starting actual work.
|
|
||||||
|
|
||||||
Then you should submit your implementation, clearly linking to the issue (and possible proposal).
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
Your PR will be reviewed by the community, then ultimately by the project maintainers, before being merged.
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
It's mandatory to:
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
- interact respectfully with other community members and maintainers - more generally, you are expected to abide by the [Docker community rules](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#docker-community-guidelines)
|
(c) The contribution was provided directly to me by some other
|
||||||
- address maintainers' comments and modify your submission accordingly
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
- write tests for any new code
|
it.
|
||||||
|
|
||||||
Complying to these simple rules will greatly accelerate the review process, and will ensure you have a pleasant experience in contributing code to the Registry.
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
Have a look at a great, successful contribution: the [Swift driver PR](https://github.com/docker/distribution/pull/493)
|
Then you just add a line to every git commit message:
|
||||||
|
|
||||||
## Coding Style
|
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||||
|
|
||||||
Unless explicitly stated, we follow all coding guidelines from the Go
|
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||||
community. While some of these standards may seem arbitrary, they somehow seem
|
|
||||||
to result in a solid, consistent codebase.
|
|
||||||
|
|
||||||
It is possible that the code base does not currently comply with these
|
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||||
guidelines. We are not looking for a massive PR that fixes this, since that
|
commit automatically with `git commit -s`.
|
||||||
goes against the spirit of the guidelines. All new contributions should make a
|
|
||||||
best effort to clean up and make the code base better than they left it.
|
|
||||||
Obviously, apply your best judgement. Remember, the goal here is to make the
|
|
||||||
code base easier for humans to navigate and understand. Always keep that in
|
|
||||||
mind when nudging others to comply.
|
|
||||||
|
|
||||||
The rules:
|
|
||||||
|
|
||||||
1. All code should be formatted with `gofmt -s`.
|
|
||||||
2. All code should pass the default levels of
|
|
||||||
[`golint`](https://github.com/golang/lint).
|
|
||||||
3. All code should follow the guidelines covered in [Effective
|
|
||||||
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
|
|
||||||
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
|
||||||
4. Comment the code. Tell us the why, the history and the context.
|
|
||||||
5. Document _all_ declarations and methods, even private ones. Declare
|
|
||||||
expectations, caveats and anything else that may be important. If a type
|
|
||||||
gets exported, having the comments already there will ensure it's ready.
|
|
||||||
6. Variable name length should be proportional to its context and no longer.
|
|
||||||
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
|
||||||
In practice, short methods will have short variable names and globals will
|
|
||||||
have longer names.
|
|
||||||
7. No underscores in package names. If you need a compound name, step back,
|
|
||||||
and re-examine why you need a compound name. If you still think you need a
|
|
||||||
compound name, lose the underscore.
|
|
||||||
8. No utils or helpers packages. If a function is not general enough to
|
|
||||||
warrant its own package, it has not been written generally enough to be a
|
|
||||||
part of a util package. Just leave it unexported and well-documented.
|
|
||||||
9. All tests should run with `go test` and outside tooling should not be
|
|
||||||
required. No, we don't need another unit testing framework. Assertion
|
|
||||||
packages are acceptable if they provide _real_ incremental value.
|
|
||||||
10. Even though we call these "rules" above, they are actually just
|
|
||||||
guidelines. Since you've read all the rules, you now know that.
|
|
||||||
|
|
||||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
|
||||||
reading through [Effective Go](http://golang.org/doc/effective_go.html). The
|
|
||||||
[Go Blog](http://blog.golang.org/) is also a great resource. Drinking the
|
|
||||||
kool-aid is a lot easier than going thirsty.
|
|
||||||
|
|
25
Dockerfile
25
Dockerfile
|
@ -1,17 +1,30 @@
|
||||||
FROM golang:1.8-alpine
|
ARG GO_VERSION=1.13.8
|
||||||
|
|
||||||
|
FROM golang:${GO_VERSION}-alpine3.11 AS build
|
||||||
|
|
||||||
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
||||||
ENV DOCKER_BUILDTAGS include_oss include_gcs
|
ENV BUILDTAGS include_oss include_gcs
|
||||||
|
|
||||||
|
ARG GOOS=linux
|
||||||
|
ARG GOARCH=amd64
|
||||||
|
ARG GOARM=6
|
||||||
|
ARG VERSION
|
||||||
|
ARG REVISION
|
||||||
|
|
||||||
RUN set -ex \
|
RUN set -ex \
|
||||||
&& apk add --no-cache make git build-base
|
&& apk add --no-cache make git file
|
||||||
|
|
||||||
WORKDIR $DISTRIBUTION_DIR
|
WORKDIR $DISTRIBUTION_DIR
|
||||||
COPY . $DISTRIBUTION_DIR
|
COPY . $DISTRIBUTION_DIR
|
||||||
|
RUN CGO_ENABLED=0 make PREFIX=/go clean binaries && file ./bin/registry | grep "statically linked"
|
||||||
|
|
||||||
|
FROM alpine:3.11
|
||||||
|
|
||||||
|
RUN set -ex \
|
||||||
|
&& apk add --no-cache ca-certificates apache2-utils
|
||||||
|
|
||||||
COPY cmd/registry/config-dev.yml /etc/docker/registry/config.yml
|
COPY cmd/registry/config-dev.yml /etc/docker/registry/config.yml
|
||||||
|
COPY --from=build /go/src/github.com/docker/distribution/bin/registry /bin/registry
|
||||||
RUN make PREFIX=/go clean binaries
|
|
||||||
|
|
||||||
VOLUME ["/var/lib/registry"]
|
VOLUME ["/var/lib/registry"]
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
ENTRYPOINT ["registry"]
|
ENTRYPOINT ["registry"]
|
||||||
|
|
144
GOVERNANCE.md
Normal file
144
GOVERNANCE.md
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
# docker/distribution Project Governance
|
||||||
|
|
||||||
|
Docker distribution abides by the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||||
|
|
||||||
|
For specific guidance on practical contribution steps please
|
||||||
|
see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide.
|
||||||
|
|
||||||
|
## Maintainership
|
||||||
|
|
||||||
|
There are different types of maintainers, with different responsibilities, but
|
||||||
|
all maintainers have 3 things in common:
|
||||||
|
|
||||||
|
1) They share responsibility in the project's success.
|
||||||
|
2) They have made a long-term, recurring time investment to improve the project.
|
||||||
|
3) They spend that time doing whatever needs to be done, not necessarily what
|
||||||
|
is the most interesting or fun.
|
||||||
|
|
||||||
|
Maintainers are often under-appreciated, because their work is harder to appreciate.
|
||||||
|
It's easy to appreciate a really cool and technically advanced feature. It's harder
|
||||||
|
to appreciate the absence of bugs, the slow but steady improvement in stability,
|
||||||
|
or the reliability of a release process. But those things distinguish a good
|
||||||
|
project from a great one.
|
||||||
|
|
||||||
|
## Reviewers
|
||||||
|
|
||||||
|
A reviewer is a core role within the project.
|
||||||
|
They share in reviewing issues and pull requests and their LGTM counts towards the
|
||||||
|
required LGTM count to merge a code change into the project.
|
||||||
|
|
||||||
|
Reviewers are part of the organization but do not have write access.
|
||||||
|
Becoming a reviewer is a core aspect in the journey to becoming a maintainer.
|
||||||
|
|
||||||
|
## Adding maintainers
|
||||||
|
|
||||||
|
Maintainers are first and foremost contributors that have shown they are
|
||||||
|
committed to the long term success of a project. Contributors wanting to become
|
||||||
|
maintainers are expected to be deeply involved in contributing code, pull
|
||||||
|
request review, and triage of issues in the project for more than three months.
|
||||||
|
|
||||||
|
Just contributing does not make you a maintainer, it is about building trust
|
||||||
|
with the current maintainers of the project and being a person that they can
|
||||||
|
depend on and trust to make decisions in the best interest of the project.
|
||||||
|
|
||||||
|
Periodically, the existing maintainers curate a list of contributors that have
|
||||||
|
shown regular activity on the project over the prior months. From this list,
|
||||||
|
maintainer candidates are selected and proposed in a pull request or a
|
||||||
|
maintainers communication channel.
|
||||||
|
|
||||||
|
After a candidate has been announced to the maintainers, the existing
|
||||||
|
maintainers are given five business days to discuss the candidate, raise
|
||||||
|
objections and cast their vote. Votes may take place on the communication
|
||||||
|
channel or via pull request comment. Candidates must be approved by at least 66%
|
||||||
|
of the current maintainers by adding their vote on the mailing list. The
|
||||||
|
reviewer role has the same process but only requires 33% of current maintainers.
|
||||||
|
Only maintainers of the repository that the candidate is proposed for are
|
||||||
|
allowed to vote.
|
||||||
|
|
||||||
|
If a candidate is approved, a maintainer will contact the candidate to invite
|
||||||
|
the candidate to open a pull request that adds the contributor to the
|
||||||
|
MAINTAINERS file. The voting process may take place inside a pull request if a
|
||||||
|
maintainer has already discussed the candidacy with the candidate and a
|
||||||
|
maintainer is willing to be a sponsor by opening the pull request. The candidate
|
||||||
|
becomes a maintainer once the pull request is merged.
|
||||||
|
|
||||||
|
## Stepping down policy
|
||||||
|
|
||||||
|
Life priorities, interests, and passions can change. If you're a maintainer but
|
||||||
|
feel you must remove yourself from the list, inform other maintainers that you
|
||||||
|
intend to step down, and if possible, help find someone to pick up your work.
|
||||||
|
At the very least, ensure your work can be continued where you left off.
|
||||||
|
|
||||||
|
After you've informed other maintainers, create a pull request to remove
|
||||||
|
yourself from the MAINTAINERS file.
|
||||||
|
|
||||||
|
## Removal of inactive maintainers
|
||||||
|
|
||||||
|
Similar to the procedure for adding new maintainers, existing maintainers can
|
||||||
|
be removed from the list if they do not show significant activity on the
|
||||||
|
project. Periodically, the maintainers review the list of maintainers and their
|
||||||
|
activity over the last three months.
|
||||||
|
|
||||||
|
If a maintainer has shown insufficient activity over this period, a neutral
|
||||||
|
person will contact the maintainer to ask if they want to continue being
|
||||||
|
a maintainer. If the maintainer decides to step down as a maintainer, they
|
||||||
|
open a pull request to be removed from the MAINTAINERS file.
|
||||||
|
|
||||||
|
If the maintainer wants to remain a maintainer, but is unable to perform the
|
||||||
|
required duties they can be removed with a vote of at least 66% of the current
|
||||||
|
maintainers. In this case, maintainers should first propose the change to
|
||||||
|
maintainers via the maintainers communication channel, then open a pull request
|
||||||
|
for voting. The voting period is five business days. The voting pull request
|
||||||
|
should not come as a surpise to any maintainer and any discussion related to
|
||||||
|
performance must not be discussed on the pull request.
|
||||||
|
|
||||||
|
## How are decisions made?
|
||||||
|
|
||||||
|
Docker distribution is an open-source project with an open design philosophy.
|
||||||
|
This means that the repository is the source of truth for EVERY aspect of the
|
||||||
|
project, including its philosophy, design, road map, and APIs. *If it's part of
|
||||||
|
the project, it's in the repo. If it's in the repo, it's part of the project.*
|
||||||
|
|
||||||
|
As a result, all decisions can be expressed as changes to the repository. An
|
||||||
|
implementation change is a change to the source code. An API change is a change
|
||||||
|
to the API specification. A philosophy change is a change to the philosophy
|
||||||
|
manifesto, and so on.
|
||||||
|
|
||||||
|
All decisions affecting distribution, big and small, follow the same 3 steps:
|
||||||
|
|
||||||
|
* Step 1: Open a pull request. Anyone can do this.
|
||||||
|
|
||||||
|
* Step 2: Discuss the pull request. Anyone can do this.
|
||||||
|
|
||||||
|
* Step 3: Merge or refuse the pull request. Who does this depends on the nature
|
||||||
|
of the pull request and which areas of the project it affects.
|
||||||
|
|
||||||
|
## Helping contributors with the DCO
|
||||||
|
|
||||||
|
The [DCO or `Sign your work`](./CONTRIBUTING.md#sign-your-work)
|
||||||
|
requirement is not intended as a roadblock or speed bump.
|
||||||
|
|
||||||
|
Some contributors are not as familiar with `git`, or have used a web
|
||||||
|
based editor, and thus asking them to `git commit --amend -s` is not the best
|
||||||
|
way forward.
|
||||||
|
|
||||||
|
In this case, maintainers can update the commits based on clause (c) of the DCO.
|
||||||
|
The most trivial way for a contributor to allow the maintainer to do this, is to
|
||||||
|
add a DCO signature in a pull requests's comment, or a maintainer can simply
|
||||||
|
note that the change is sufficiently trivial that it does not substantially
|
||||||
|
change the existing contribution - i.e., a spelling change.
|
||||||
|
|
||||||
|
When you add someone's DCO, please also add your own to keep a log.
|
||||||
|
|
||||||
|
## I'm a maintainer. Should I make pull requests too?
|
||||||
|
|
||||||
|
Yes. Nobody should ever push to master directly. All changes should be
|
||||||
|
made through a pull request.
|
||||||
|
|
||||||
|
## Conflict Resolution
|
||||||
|
|
||||||
|
If you have a technical dispute that you feel has reached an impasse with a
|
||||||
|
subset of the community, any contributor may open an issue, specifically
|
||||||
|
calling for a resolution vote of the current core maintainers to resolve the
|
||||||
|
dispute. The same voting quorums required (2/3) for adding and removing
|
||||||
|
maintainers will apply to conflict resolution.
|
68
MAINTAINERS
68
MAINTAINERS
|
@ -1,58 +1,16 @@
|
||||||
# Distribution maintainers file
|
# Docker distribution project maintainers & reviewers
|
||||||
#
|
#
|
||||||
# This file describes who runs the docker/distribution project and how.
|
# See GOVERNANCE.md for maintainer versus reviewer roles
|
||||||
# This is a living document - if you see something out of date or missing, speak up!
|
|
||||||
#
|
#
|
||||||
# It is structured to be consumable by both humans and programs.
|
# MAINTAINERS
|
||||||
# To extract its contents programmatically, use any TOML-compliant parser.
|
# GitHub ID, Name, Email address
|
||||||
|
"dmcgowan","Derek McGowan","derek@mcgstyle.net"
|
||||||
|
"manishtomar","Manish Tomar","manish.tomar@docker.com"
|
||||||
|
"stevvooe","Stephen Day","stevvooe@gmail.com"
|
||||||
#
|
#
|
||||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
# REVIEWERS
|
||||||
#
|
# GitHub ID, Name, Email address
|
||||||
[Org]
|
"caervs","Ryan Abrams","rdabrams@gmail.com"
|
||||||
[Org."Core maintainers"]
|
"davidswu","David Wu","dwu7401@gmail.com"
|
||||||
people = [
|
"RobbKistler","Robb Kistler","robb.kistler@docker.com"
|
||||||
"aaronlehmann",
|
"thajeztah","Sebastiaan van Stijn","github@gone.nl"
|
||||||
"dmcgowan",
|
|
||||||
"dmp42",
|
|
||||||
"richardscothern",
|
|
||||||
"shykes",
|
|
||||||
"stevvooe",
|
|
||||||
]
|
|
||||||
|
|
||||||
[people]
|
|
||||||
|
|
||||||
# A reference list of all people associated with the project.
|
|
||||||
# All other sections should refer to people by their canonical key
|
|
||||||
# in the people section.
|
|
||||||
|
|
||||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
|
||||||
|
|
||||||
[people.aaronlehmann]
|
|
||||||
Name = "Aaron Lehmann"
|
|
||||||
Email = "aaron.lehmann@docker.com"
|
|
||||||
GitHub = "aaronlehmann"
|
|
||||||
|
|
||||||
[people.dmcgowan]
|
|
||||||
Name = "Derek McGowan"
|
|
||||||
Email = "derek@mcgstyle.net"
|
|
||||||
GitHub = "dmcgowan"
|
|
||||||
|
|
||||||
[people.dmp42]
|
|
||||||
Name = "Olivier Gambier"
|
|
||||||
Email = "olivier@docker.com"
|
|
||||||
GitHub = "dmp42"
|
|
||||||
|
|
||||||
[people.richardscothern]
|
|
||||||
Name = "Richard Scothern"
|
|
||||||
Email = "richard.scothern@gmail.com"
|
|
||||||
GitHub = "richardscothern"
|
|
||||||
|
|
||||||
[people.shykes]
|
|
||||||
Name = "Solomon Hykes"
|
|
||||||
Email = "solomon@docker.com"
|
|
||||||
GitHub = "shykes"
|
|
||||||
|
|
||||||
[people.stevvooe]
|
|
||||||
Name = "Stephen Day"
|
|
||||||
Email = "stephen.day@docker.com"
|
|
||||||
GitHub = "stevvooe"
|
|
||||||
|
|
144
Makefile
144
Makefile
|
@ -1,9 +1,21 @@
|
||||||
# Set an output prefix, which is the local directory if not specified
|
# Root directory of the project (absolute path).
|
||||||
PREFIX?=$(shell pwd)
|
ROOTDIR=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
|
|
||||||
# Used to populate version variable in main package.
|
# Used to populate version variable in main package.
|
||||||
VERSION=$(shell git describe --match 'v[0-9]*' --dirty='.m' --always)
|
VERSION ?= $(shell git describe --match 'v[0-9]*' --dirty='.m' --always)
|
||||||
|
REVISION ?= $(shell git rev-parse HEAD)$(shell if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi)
|
||||||
|
|
||||||
|
|
||||||
|
PKG=github.com/docker/distribution
|
||||||
|
|
||||||
|
# Project packages.
|
||||||
|
PACKAGES=$(shell go list -tags "${BUILDTAGS}" ./... | grep -v /vendor/)
|
||||||
|
INTEGRATION_PACKAGE=${PKG}
|
||||||
|
COVERAGE_PACKAGES=$(filter-out ${PKG}/registry/storage/driver/%,${PACKAGES})
|
||||||
|
|
||||||
|
|
||||||
|
# Project binaries.
|
||||||
|
COMMANDS=registry digest registry-api-descriptor-template
|
||||||
|
|
||||||
# Allow turning off function inlining and variable registerization
|
# Allow turning off function inlining and variable registerization
|
||||||
ifeq (${DISABLE_OPTIMIZATION},true)
|
ifeq (${DISABLE_OPTIMIZATION},true)
|
||||||
|
@ -11,88 +23,80 @@ ifeq (${DISABLE_OPTIMIZATION},true)
|
||||||
VERSION:="$(VERSION)-noopt"
|
VERSION:="$(VERSION)-noopt"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
GO_LDFLAGS=-ldflags "-X `go list ./version`.Version=$(VERSION)"
|
WHALE = "+"
|
||||||
|
|
||||||
.PHONY: all build binaries clean dep-restore dep-save dep-validate fmt lint test test-full vet
|
# Go files
|
||||||
|
#
|
||||||
|
TESTFLAGS_RACE=
|
||||||
|
GOFILES=$(shell find . -type f -name '*.go')
|
||||||
|
GO_TAGS=$(if $(BUILDTAGS),-tags "$(BUILDTAGS)",)
|
||||||
|
GO_LDFLAGS=-ldflags '-s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PKG) $(EXTRA_LDFLAGS)'
|
||||||
|
|
||||||
|
BINARIES=$(addprefix bin/,$(COMMANDS))
|
||||||
|
|
||||||
|
# Flags passed to `go test`
|
||||||
|
TESTFLAGS ?= -v $(TESTFLAGS_RACE)
|
||||||
|
TESTFLAGS_PARALLEL ?= 8
|
||||||
|
|
||||||
|
.PHONY: all build binaries check clean test test-race test-full integration coverage
|
||||||
.DEFAULT: all
|
.DEFAULT: all
|
||||||
all: fmt vet lint build test binaries
|
|
||||||
|
|
||||||
AUTHORS: .mailmap .git/HEAD
|
all: binaries
|
||||||
git log --format='%aN <%aE>' | sort -fu > $@
|
|
||||||
|
|
||||||
# This only needs to be generated by hand when cutting full releases.
|
# This only needs to be generated by hand when cutting full releases.
|
||||||
version/version.go:
|
version/version.go:
|
||||||
|
@echo "$(WHALE) $@"
|
||||||
./version/version.sh > $@
|
./version/version.sh > $@
|
||||||
|
|
||||||
# Required for go 1.5 to build
|
check: ## run all linters (TODO: enable "unused", "varcheck", "ineffassign", "unconvert", "staticheck", "goimports", "structcheck")
|
||||||
GO15VENDOREXPERIMENT := 1
|
@echo "$(WHALE) $@"
|
||||||
|
@GO111MODULE=off golangci-lint run
|
||||||
|
|
||||||
# Go files
|
test: ## run tests, except integration test with test.short
|
||||||
GOFILES=$(shell find . -type f -name '*.go')
|
@echo "$(WHALE) $@"
|
||||||
|
@go test ${GO_TAGS} -test.short ${TESTFLAGS} $(filter-out ${INTEGRATION_PACKAGE},${PACKAGES})
|
||||||
|
|
||||||
# Package list
|
test-race: ## run tests, except integration test with test.short and race
|
||||||
PKGS=$(shell go list -tags "${DOCKER_BUILDTAGS}" ./... | grep -v ^github.com/docker/distribution/vendor/)
|
@echo "$(WHALE) $@"
|
||||||
|
@go test ${GO_TAGS} -race -test.short ${TESTFLAGS} $(filter-out ${INTEGRATION_PACKAGE},${PACKAGES})
|
||||||
|
|
||||||
# Resolving binary dependencies for specific targets
|
test-full: ## run tests, except integration tests
|
||||||
GOLINT=$(shell which golint || echo '')
|
@echo "$(WHALE) $@"
|
||||||
VNDR=$(shell which vndr || echo '')
|
@go test ${GO_TAGS} ${TESTFLAGS} $(filter-out ${INTEGRATION_PACKAGE},${PACKAGES})
|
||||||
|
|
||||||
${PREFIX}/bin/registry: $(GOFILES)
|
integration: ## run integration tests
|
||||||
@echo "+ $@"
|
@echo "$(WHALE) $@"
|
||||||
@go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/registry
|
@go test ${TESTFLAGS} -parallel ${TESTFLAGS_PARALLEL} ${INTEGRATION_PACKAGE}
|
||||||
|
|
||||||
${PREFIX}/bin/digest: $(GOFILES)
|
coverage: ## generate coverprofiles from the unit tests
|
||||||
@echo "+ $@"
|
@echo "$(WHALE) $@"
|
||||||
@go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/digest
|
@rm -f coverage.txt
|
||||||
|
@go test ${GO_TAGS} -i ${TESTFLAGS} $(filter-out ${INTEGRATION_PACKAGE},${COVERAGE_PACKAGES}) 2> /dev/null
|
||||||
|
@( for pkg in $(filter-out ${INTEGRATION_PACKAGE},${COVERAGE_PACKAGES}); do \
|
||||||
|
go test ${GO_TAGS} ${TESTFLAGS} \
|
||||||
|
-cover \
|
||||||
|
-coverprofile=profile.out \
|
||||||
|
-covermode=atomic $$pkg || exit; \
|
||||||
|
if [ -f profile.out ]; then \
|
||||||
|
cat profile.out >> coverage.txt; \
|
||||||
|
rm profile.out; \
|
||||||
|
fi; \
|
||||||
|
done )
|
||||||
|
|
||||||
${PREFIX}/bin/registry-api-descriptor-template: $(GOFILES)
|
FORCE:
|
||||||
@echo "+ $@"
|
|
||||||
@go build -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/registry-api-descriptor-template
|
|
||||||
|
|
||||||
docs/spec/api.md: docs/spec/api.md.tmpl ${PREFIX}/bin/registry-api-descriptor-template
|
# Build a binary from a cmd.
|
||||||
./bin/registry-api-descriptor-template $< > $@
|
bin/%: cmd/% FORCE
|
||||||
|
@echo "$(WHALE) $@${BINARY_SUFFIX}"
|
||||||
|
@go build ${GO_GCFLAGS} ${GO_BUILD_FLAGS} -o $@${BINARY_SUFFIX} ${GO_LDFLAGS} ${GO_TAGS} ./$<
|
||||||
|
|
||||||
vet:
|
binaries: $(BINARIES) ## build binaries
|
||||||
@echo "+ $@"
|
@echo "$(WHALE) $@"
|
||||||
@go vet -tags "${DOCKER_BUILDTAGS}" $(PKGS)
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
@echo "+ $@"
|
|
||||||
@test -z "$$(gofmt -s -l . 2>&1 | grep -v ^vendor/ | tee /dev/stderr)" || \
|
|
||||||
(echo >&2 "+ please format Go code with 'gofmt -s'" && false)
|
|
||||||
|
|
||||||
lint:
|
|
||||||
@echo "+ $@"
|
|
||||||
$(if $(GOLINT), , \
|
|
||||||
$(error Please install golint: `go get -u github.com/golang/lint/golint`))
|
|
||||||
@test -z "$$($(GOLINT) ./... 2>&1 | grep -v ^vendor/ | tee /dev/stderr)"
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@echo "+ $@"
|
@echo "$(WHALE) $@"
|
||||||
@go build -tags "${DOCKER_BUILDTAGS}" -v ${GO_LDFLAGS} $(PKGS)
|
@go build ${GO_GCFLAGS} ${GO_BUILD_FLAGS} ${GO_LDFLAGS} ${GO_TAGS} $(PACKAGES)
|
||||||
|
|
||||||
test:
|
clean: ## clean up binaries
|
||||||
@echo "+ $@"
|
@echo "$(WHALE) $@"
|
||||||
@go test -test.short -tags "${DOCKER_BUILDTAGS}" $(PKGS)
|
@rm -f $(BINARIES)
|
||||||
|
|
||||||
test-full:
|
|
||||||
@echo "+ $@"
|
|
||||||
@go test -tags "${DOCKER_BUILDTAGS}" $(PKGS)
|
|
||||||
|
|
||||||
binaries: ${PREFIX}/bin/registry ${PREFIX}/bin/digest ${PREFIX}/bin/registry-api-descriptor-template
|
|
||||||
@echo "+ $@"
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@echo "+ $@"
|
|
||||||
@rm -rf "${PREFIX}/bin/registry" "${PREFIX}/bin/digest" "${PREFIX}/bin/registry-api-descriptor-template"
|
|
||||||
|
|
||||||
dep-validate:
|
|
||||||
@echo "+ $@"
|
|
||||||
$(if $(VNDR), , \
|
|
||||||
$(error Please install vndr: go get github.com/lk4d4/vndr))
|
|
||||||
@rm -Rf .vendor.bak
|
|
||||||
@mv vendor .vendor.bak
|
|
||||||
@$(VNDR)
|
|
||||||
@test -z "$$(diff -r vendor .vendor.bak 2>&1 | tee /dev/stderr)" || \
|
|
||||||
(echo >&2 "+ inconsistent dependencies! what you have in vendor.conf does not match with what you have in vendor" && false)
|
|
||||||
@rm -Rf .vendor.bak
|
|
||||||
|
|
94
README.md
94
README.md
|
@ -2,31 +2,32 @@
|
||||||
|
|
||||||
The Docker toolset to pack, ship, store, and deliver content.
|
The Docker toolset to pack, ship, store, and deliver content.
|
||||||
|
|
||||||
This repository's main product is the Docker Registry 2.0 implementation
|
This repository's main product is the Open Source Docker Registry implementation
|
||||||
for storing and distributing Docker images. It supersedes the
|
for storing and distributing Docker and OCI images using the
|
||||||
[docker/docker-registry](https://github.com/docker/docker-registry)
|
[OCI Distribution Specification](https://github.com/opencontainers/distribution-spec).
|
||||||
project with a new API design, focused around security and performance.
|
The goal of this project is to provide a simple, secure, and scalable base
|
||||||
|
for building a registry solution or running a simple private registry.
|
||||||
|
|
||||||
<img src="https://www.docker.com/sites/default/files/oyster-registry-3.png" width=200px/>
|
<img src="https://www.docker.com/sites/default/files/oyster-registry-3.png" width=200px/>
|
||||||
|
|
||||||
[![Circle CI](https://circleci.com/gh/docker/distribution/tree/master.svg?style=svg)](https://circleci.com/gh/docker/distribution/tree/master)
|
[![Build Status](https://travis-ci.org/docker/distribution.svg?branch=master)](https://travis-ci.org/docker/distribution)
|
||||||
[![GoDoc](https://godoc.org/github.com/docker/distribution?status.svg)](https://godoc.org/github.com/docker/distribution)
|
[![GoDoc](https://godoc.org/github.com/docker/distribution?status.svg)](https://godoc.org/github.com/docker/distribution)
|
||||||
|
|
||||||
This repository contains the following components:
|
This repository contains the following components:
|
||||||
|
|
||||||
|**Component** |Description |
|
|**Component** |Description |
|
||||||
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| **registry** | An implementation of the [Docker Registry HTTP API V2](docs/spec/api.md) for use with docker 1.6+. |
|
| **registry** | An implementation of the [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec). |
|
||||||
| **libraries** | A rich set of libraries for interacting with distribution components. Please see [godoc](https://godoc.org/github.com/docker/distribution) for details. **Note**: These libraries are **unstable**. |
|
| **libraries** | A rich set of libraries for interacting with distribution components. Please see [godoc](https://godoc.org/github.com/docker/distribution) for details. **Note**: The interfaces for these libraries are **unstable**. |
|
||||||
| **specifications** | _Distribution_ related specifications are available in [docs/spec](docs/spec) |
|
|
||||||
| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/) related just to the registry. |
|
| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/) related just to the registry. |
|
||||||
|
|
||||||
### How does this integrate with Docker engine?
|
### How does this integrate with Docker, containerd, and other OCI client?
|
||||||
|
|
||||||
This project should provide an implementation to a V2 API for use in the [Docker
|
Clients implement against the OCI specification and communicate with the
|
||||||
core project](https://github.com/docker/docker). The API should be embeddable
|
registry using HTTP. This project contains an client implementation which
|
||||||
and simplify the process of securely pulling and pushing content from `docker`
|
is currently in use by Docker, however, it is deprecated for the
|
||||||
daemons.
|
[implementation in containerd](https://github.com/containerd/containerd/tree/master/remotes/docker)
|
||||||
|
and will not support new features.
|
||||||
|
|
||||||
### What are the long term goals of the Distribution project?
|
### What are the long term goals of the Distribution project?
|
||||||
|
|
||||||
|
@ -43,18 +44,6 @@ system that allow users to:
|
||||||
* Implement their own home made solution through good specs, and solid
|
* Implement their own home made solution through good specs, and solid
|
||||||
extensions mechanism.
|
extensions mechanism.
|
||||||
|
|
||||||
## More about Registry 2.0
|
|
||||||
|
|
||||||
The new registry implementation provides the following benefits:
|
|
||||||
|
|
||||||
- faster push and pull
|
|
||||||
- new, more efficient implementation
|
|
||||||
- simplified deployment
|
|
||||||
- pluggable storage backend
|
|
||||||
- webhook notifications
|
|
||||||
|
|
||||||
For information on upcoming functionality, please see [ROADMAP.md](ROADMAP.md).
|
|
||||||
|
|
||||||
### Who needs to deploy a registry?
|
### Who needs to deploy a registry?
|
||||||
|
|
||||||
By default, Docker users pull images from Docker's public registry instance.
|
By default, Docker users pull images from Docker's public registry instance.
|
||||||
|
@ -76,56 +65,27 @@ may be the better choice.
|
||||||
For those who have previously deployed their own registry based on the Registry
|
For those who have previously deployed their own registry based on the Registry
|
||||||
1.0 implementation and wish to deploy a Registry 2.0 while retaining images,
|
1.0 implementation and wish to deploy a Registry 2.0 while retaining images,
|
||||||
data migration is required. A tool to assist with migration efforts has been
|
data migration is required. A tool to assist with migration efforts has been
|
||||||
created. For more information see [docker/migrator]
|
created. For more information see [docker/migrator](https://github.com/docker/migrator).
|
||||||
(https://github.com/docker/migrator).
|
|
||||||
|
|
||||||
## Contribute
|
## Contribution
|
||||||
|
|
||||||
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute
|
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute
|
||||||
issues, fixes, and patches to this project. If you are contributing code, see
|
issues, fixes, and patches to this project. If you are contributing code, see
|
||||||
the instructions for [building a development environment](BUILDING.md).
|
the instructions for [building a development environment](BUILDING.md).
|
||||||
|
|
||||||
## Support
|
## Communication
|
||||||
|
|
||||||
If any issues are encountered while using the _Distribution_ project, several
|
For async communication and long running discussions please use issues and pull requests on the github repo.
|
||||||
avenues are available for support:
|
This will be the best place to discuss design and implementation.
|
||||||
|
|
||||||
<table>
|
For sync communication we have a community slack with a #distribution channel that everyone is welcome to join and chat about development.
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
IRC
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
#docker-distribution on FreeNode
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
Issue Tracker
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
github.com/docker/distribution/issues
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
Google Groups
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
https://groups.google.com/a/dockerproject.org/forum/#!forum/distribution
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
Mailing List
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
docker@dockerproject.org
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
**Slack:** Catch us in the #distribution channels on dockercommunity.slack.com.
|
||||||
|
[Click here for an invite to Docker community slack.](https://dockr.ly/slack)
|
||||||
|
|
||||||
## License
|
## Licenses
|
||||||
|
|
||||||
This project is distributed under [Apache License, Version 2.0](LICENSE).
|
The distribution codebase is released under the [Apache 2.0 license](LICENSE).
|
||||||
|
The README.md file, and files in the "docs" folder are licensed under the
|
||||||
|
Creative Commons Attribution 4.0 International License. You may obtain a
|
||||||
|
copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/.
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
## Registry Release Checklist
|
|
||||||
|
|
||||||
10. Compile release notes detailing features and since the last release. Update the `CHANGELOG.md` file.
|
|
||||||
|
|
||||||
20. Update the version file: `https://github.com/docker/distribution/blob/master/version/version.go`
|
|
||||||
|
|
||||||
30. Update the `MAINTAINERS` (if necessary), `AUTHORS` and `.mailmap` files.
|
|
||||||
|
|
||||||
```
|
|
||||||
make AUTHORS
|
|
||||||
```
|
|
||||||
|
|
||||||
40. Create a signed tag.
|
|
||||||
|
|
||||||
Distribution uses semantic versioning. Tags are of the format `vx.y.z[-rcn]`
|
|
||||||
You will need PGP installed and a PGP key which has been added to your Github account. The comment for the tag should include the release notes.
|
|
||||||
|
|
||||||
50. Push the signed tag
|
|
||||||
|
|
||||||
60. Create a new [release](https://github.com/docker/distribution/releases). In the case of a release candidate, tick the `pre-release` checkbox.
|
|
||||||
|
|
||||||
70. Update the registry binary in [distribution library image repo](https://github.com/docker/distribution-library-image) by running the update script and opening a pull request.
|
|
||||||
|
|
||||||
80. Update the official image. Add the new version in the [official images repo](https://github.com/docker-library/official-images) by appending a new version to the `registry/registry` file with the git hash pointed to by the signed tag. Update the major version to point to the latest version and the minor version to point to new patch release if necessary.
|
|
||||||
e.g. to release `2.3.1`
|
|
||||||
|
|
||||||
`2.3.1 (new)`
|
|
||||||
|
|
||||||
`2.3.0 -> 2.3.0` can be removed
|
|
||||||
|
|
||||||
`2 -> 2.3.1`
|
|
||||||
|
|
||||||
`2.3 -> 2.3.1`
|
|
||||||
|
|
||||||
90. Build a new distribution/registry image on [Docker hub](https://hub.docker.com/u/distribution/dashboard) by adding a new automated build with the new tag and re-building the images.
|
|
||||||
|
|
14
blobs.go
14
blobs.go
|
@ -1,15 +1,16 @@
|
||||||
package distribution
|
package distribution
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution/context"
|
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -66,12 +67,19 @@ type Descriptor struct {
|
||||||
Size int64 `json:"size,omitempty"`
|
Size int64 `json:"size,omitempty"`
|
||||||
|
|
||||||
// Digest uniquely identifies the content. A byte stream can be verified
|
// Digest uniquely identifies the content. A byte stream can be verified
|
||||||
// against against this digest.
|
// against this digest.
|
||||||
Digest digest.Digest `json:"digest,omitempty"`
|
Digest digest.Digest `json:"digest,omitempty"`
|
||||||
|
|
||||||
// URLs contains the source URLs of this content.
|
// URLs contains the source URLs of this content.
|
||||||
URLs []string `json:"urls,omitempty"`
|
URLs []string `json:"urls,omitempty"`
|
||||||
|
|
||||||
|
// Annotations contains arbitrary metadata relating to the targeted content.
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
|
||||||
|
// Platform describes the platform which the image in the manifest runs on.
|
||||||
|
// This should only be used when referring to a manifest.
|
||||||
|
Platform *v1.Platform `json:"platform,omitempty"`
|
||||||
|
|
||||||
// NOTE: Before adding a field here, please ensure that all
|
// NOTE: Before adding a field here, please ensure that all
|
||||||
// other options have been exhausted. Much of the type relationships
|
// other options have been exhausted. Much of the type relationships
|
||||||
// depend on the simplicity of this type.
|
// depend on the simplicity of this type.
|
||||||
|
@ -152,7 +160,7 @@ type BlobProvider interface {
|
||||||
|
|
||||||
// BlobServer can serve blobs via http.
|
// BlobServer can serve blobs via http.
|
||||||
type BlobServer interface {
|
type BlobServer interface {
|
||||||
// ServeBlob attempts to serve the blob, identifed by dgst, via http. The
|
// ServeBlob attempts to serve the blob, identified by dgst, via http. The
|
||||||
// service may decide to redirect the client elsewhere or serve the data
|
// service may decide to redirect the client elsewhere or serve the data
|
||||||
// directly.
|
// directly.
|
||||||
//
|
//
|
||||||
|
|
97
circle.yml
97
circle.yml
|
@ -1,97 +0,0 @@
|
||||||
# Pony-up!
|
|
||||||
machine:
|
|
||||||
pre:
|
|
||||||
# Install gvm
|
|
||||||
- bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/1.0.22/binscripts/gvm-installer)
|
|
||||||
# Install codecov for coverage
|
|
||||||
- pip install --user codecov
|
|
||||||
|
|
||||||
post:
|
|
||||||
# go
|
|
||||||
- gvm install go1.8 --prefer-binary --name=stable
|
|
||||||
|
|
||||||
environment:
|
|
||||||
# Convenient shortcuts to "common" locations
|
|
||||||
CHECKOUT: /home/ubuntu/$CIRCLE_PROJECT_REPONAME
|
|
||||||
BASE_DIR: src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
|
||||||
# Trick circle brainflat "no absolute path" behavior
|
|
||||||
BASE_STABLE: ../../../$HOME/.gvm/pkgsets/stable/global/$BASE_DIR
|
|
||||||
DOCKER_BUILDTAGS: "include_oss include_gcs"
|
|
||||||
# Workaround Circle parsing dumb bugs and/or YAML wonkyness
|
|
||||||
CIRCLE_PAIN: "mode: set"
|
|
||||||
|
|
||||||
hosts:
|
|
||||||
# Not used yet
|
|
||||||
fancy: 127.0.0.1
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
pre:
|
|
||||||
# Copy the code to the gopath of all go versions
|
|
||||||
- >
|
|
||||||
gvm use stable &&
|
|
||||||
mkdir -p "$(dirname $BASE_STABLE)" &&
|
|
||||||
cp -R "$CHECKOUT" "$BASE_STABLE"
|
|
||||||
|
|
||||||
override:
|
|
||||||
# Install dependencies for every copied clone/go version
|
|
||||||
- gvm use stable && go get github.com/lk4d4/vndr:
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
post:
|
|
||||||
# For the stable go version, additionally install linting tools
|
|
||||||
- >
|
|
||||||
gvm use stable &&
|
|
||||||
go get github.com/axw/gocov/gocov github.com/golang/lint/golint
|
|
||||||
|
|
||||||
test:
|
|
||||||
pre:
|
|
||||||
# Output the go versions we are going to test
|
|
||||||
# - gvm use old && go version
|
|
||||||
- gvm use stable && go version
|
|
||||||
|
|
||||||
# Ensure validation of dependencies
|
|
||||||
- gvm use stable && if test -n "`git diff --stat=1000 master | grep -E \"^[[:space:]]*vendor\"`"; then make dep-validate; fi:
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
# First thing: build everything. This will catch compile errors, and it's
|
|
||||||
# also necessary for go vet to work properly (see #807).
|
|
||||||
- gvm use stable && go install $(go list ./... | grep -v "/vendor/"):
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
# FMT
|
|
||||||
- gvm use stable && make fmt:
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
# VET
|
|
||||||
- gvm use stable && make vet:
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
# LINT
|
|
||||||
- gvm use stable && make lint:
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
override:
|
|
||||||
# Test stable, and report
|
|
||||||
- gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | grep -v pluginloader | xargs -L 1 -I{} bash -c 'export PACKAGE={}; go test -tags "$DOCKER_BUILDTAGS" -test.short -coverprofile=$GOPATH/src/$PACKAGE/coverage.out -coverpkg=$(./coverpkg.sh $PACKAGE $ROOT_PACKAGE) $PACKAGE':
|
|
||||||
timeout: 1000
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
# coverpkg changes the signature of compiled packages
|
|
||||||
- gvm use stable; export PACKAGE=github.com/docker/distribution/registry/pluginloader; go test -tags "$DOCKER_BUILDTAGS" -test.short -coverprofile=$GOPATH/src/$PACKAGE/coverage.out $PACKAGE:
|
|
||||||
timeout: 1000
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
# Test stable with race
|
|
||||||
- gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | grep -v "registry/handlers" | grep -v "registry/storage/driver" | xargs -L 1 -I{} bash -c 'export PACKAGE={}; go test -race -tags "$DOCKER_BUILDTAGS" -test.short $PACKAGE':
|
|
||||||
timeout: 1000
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
post:
|
|
||||||
# Report to codecov
|
|
||||||
- bash <(curl -s https://codecov.io/bash):
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
# Do we want these as well?
|
|
||||||
# - go get code.google.com/p/go.tools/cmd/goimports
|
|
||||||
# - test -z "$(goimports -l -w ./... | tee /dev/stderr)"
|
|
||||||
# http://labix.org/gocheck
|
|
|
@ -9,6 +9,9 @@ import (
|
||||||
|
|
||||||
"github.com/docker/distribution/version"
|
"github.com/docker/distribution/version"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
|
||||||
|
_ "crypto/sha256"
|
||||||
|
_ "crypto/sha512"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -32,7 +35,7 @@ func init() {
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
fmt.Fprintf(os.Stderr, "usage: %s [files...]\n", os.Args[0])
|
fmt.Fprintf(os.Stderr, "usage: %s [files...]\n", os.Args[0])
|
||||||
fmt.Fprintf(os.Stderr, `
|
fmt.Fprint(os.Stderr, `
|
||||||
Calculate the digest of one or more input files, emitting the result
|
Calculate the digest of one or more input files, emitting the result
|
||||||
to standard out. If no files are provided, the digest of stdin will
|
to standard out. If no files are provided, the digest of stdin will
|
||||||
be calculated.
|
be calculated.
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
"github.com/docker/distribution/registry/api/v2"
|
v2 "github.com/docker/distribution/registry/api/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var spaceRegex = regexp.MustCompile(`\n\s*`)
|
var spaceRegex = regexp.MustCompile(`\n\s*`)
|
||||||
|
|
|
@ -29,6 +29,8 @@ redis:
|
||||||
readtimeout: 10ms
|
readtimeout: 10ms
|
||||||
writetimeout: 10ms
|
writetimeout: 10ms
|
||||||
notifications:
|
notifications:
|
||||||
|
events:
|
||||||
|
includereferences: true
|
||||||
endpoints:
|
endpoints:
|
||||||
- name: local-8082
|
- name: local-8082
|
||||||
url: http://localhost:5003/callback
|
url: http://localhost:5003/callback
|
||||||
|
|
|
@ -31,7 +31,10 @@ storage:
|
||||||
http:
|
http:
|
||||||
addr: :5000
|
addr: :5000
|
||||||
debug:
|
debug:
|
||||||
addr: localhost:5001
|
addr: :5001
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
path: /metrics
|
||||||
headers:
|
headers:
|
||||||
X-Content-Type-Options: [nosniff]
|
X-Content-Type-Options: [nosniff]
|
||||||
redis:
|
redis:
|
||||||
|
@ -44,6 +47,8 @@ redis:
|
||||||
readtimeout: 10ms
|
readtimeout: 10ms
|
||||||
writetimeout: 10ms
|
writetimeout: 10ms
|
||||||
notifications:
|
notifications:
|
||||||
|
events:
|
||||||
|
includereferences: true
|
||||||
endpoints:
|
endpoints:
|
||||||
- name: local-5003
|
- name: local-5003
|
||||||
url: http://localhost:5003/callback
|
url: http://localhost:5003/callback
|
||||||
|
|
|
@ -11,6 +11,10 @@ http:
|
||||||
addr: :5000
|
addr: :5000
|
||||||
headers:
|
headers:
|
||||||
X-Content-Type-Options: [nosniff]
|
X-Content-Type-Options: [nosniff]
|
||||||
|
auth:
|
||||||
|
htpasswd:
|
||||||
|
realm: basic-realm
|
||||||
|
path: /etc/registry
|
||||||
health:
|
health:
|
||||||
storagedriver:
|
storagedriver:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
|
@ -12,11 +12,11 @@ import (
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/filesystem"
|
_ "github.com/docker/distribution/registry/storage/driver/filesystem"
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/gcs"
|
_ "github.com/docker/distribution/registry/storage/driver/gcs"
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/inmemory"
|
_ "github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||||
|
_ "github.com/docker/distribution/registry/storage/driver/middleware/alicdn"
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/middleware/cloudfront"
|
_ "github.com/docker/distribution/registry/storage/driver/middleware/cloudfront"
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/middleware/redirect"
|
_ "github.com/docker/distribution/registry/storage/driver/middleware/redirect"
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/oss"
|
_ "github.com/docker/distribution/registry/storage/driver/oss"
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/s3-aws"
|
_ "github.com/docker/distribution/registry/storage/driver/s3-aws"
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/s3-goamz"
|
|
||||||
_ "github.com/docker/distribution/registry/storage/driver/swift"
|
_ "github.com/docker/distribution/registry/storage/driver/swift"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -29,7 +30,7 @@ type Configuration struct {
|
||||||
} `yaml:"accesslog,omitempty"`
|
} `yaml:"accesslog,omitempty"`
|
||||||
|
|
||||||
// Level is the granularity at which registry operations are logged.
|
// Level is the granularity at which registry operations are logged.
|
||||||
Level Loglevel `yaml:"level"`
|
Level Loglevel `yaml:"level,omitempty"`
|
||||||
|
|
||||||
// Formatter overrides the default formatter with another. Options
|
// Formatter overrides the default formatter with another. Options
|
||||||
// include "text", "json" and "logstash".
|
// include "text", "json" and "logstash".
|
||||||
|
@ -44,13 +45,11 @@ type Configuration struct {
|
||||||
Hooks []LogHook `yaml:"hooks,omitempty"`
|
Hooks []LogHook `yaml:"hooks,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loglevel is the level at which registry operations are logged. This is
|
// Loglevel is the level at which registry operations are logged.
|
||||||
// deprecated. Please use Log.Level in the future.
|
//
|
||||||
|
// Deprecated: Use Log.Level instead.
|
||||||
Loglevel Loglevel `yaml:"loglevel,omitempty"`
|
Loglevel Loglevel `yaml:"loglevel,omitempty"`
|
||||||
|
|
||||||
// Plugins is a path where plugins are expected to be found
|
|
||||||
Plugins []string `yaml:"plugins,omitempty"`
|
|
||||||
|
|
||||||
// Storage is the configuration for the registry's storage driver
|
// Storage is the configuration for the registry's storage driver
|
||||||
Storage Storage `yaml:"storage"`
|
Storage Storage `yaml:"storage"`
|
||||||
|
|
||||||
|
@ -86,6 +85,10 @@ type Configuration struct {
|
||||||
// Location headers
|
// Location headers
|
||||||
RelativeURLs bool `yaml:"relativeurls,omitempty"`
|
RelativeURLs bool `yaml:"relativeurls,omitempty"`
|
||||||
|
|
||||||
|
// Amount of time to wait for connection to drain before shutting down when registry
|
||||||
|
// receives a stop signal
|
||||||
|
DrainTimeout time.Duration `yaml:"draintimeout,omitempty"`
|
||||||
|
|
||||||
// TLS instructs the http server to listen with a TLS configuration.
|
// TLS instructs the http server to listen with a TLS configuration.
|
||||||
// This only support simple tls configuration with a cert and key.
|
// This only support simple tls configuration with a cert and key.
|
||||||
// Mostly, this is useful for testing situations or simple deployments
|
// Mostly, this is useful for testing situations or simple deployments
|
||||||
|
@ -105,6 +108,9 @@ type Configuration struct {
|
||||||
// A file may contain multiple CA certificates encoded as PEM
|
// A file may contain multiple CA certificates encoded as PEM
|
||||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||||
|
|
||||||
|
// Specifies the lowest TLS version allowed
|
||||||
|
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||||
|
|
||||||
// LetsEncrypt is used to configuration setting up TLS through
|
// LetsEncrypt is used to configuration setting up TLS through
|
||||||
// Let's Encrypt instead of manually specifying certificate and
|
// Let's Encrypt instead of manually specifying certificate and
|
||||||
// key. If a TLS certificate is specified, the Let's Encrypt
|
// key. If a TLS certificate is specified, the Let's Encrypt
|
||||||
|
@ -116,6 +122,10 @@ type Configuration struct {
|
||||||
|
|
||||||
// Email is the email to use during Let's Encrypt registration
|
// Email is the email to use during Let's Encrypt registration
|
||||||
Email string `yaml:"email,omitempty"`
|
Email string `yaml:"email,omitempty"`
|
||||||
|
|
||||||
|
// Hosts specifies the hosts which are allowed to obtain Let's
|
||||||
|
// Encrypt certificates.
|
||||||
|
Hosts []string `yaml:"hosts,omitempty"`
|
||||||
} `yaml:"letsencrypt,omitempty"`
|
} `yaml:"letsencrypt,omitempty"`
|
||||||
} `yaml:"tls,omitempty"`
|
} `yaml:"tls,omitempty"`
|
||||||
|
|
||||||
|
@ -131,11 +141,16 @@ type Configuration struct {
|
||||||
Debug struct {
|
Debug struct {
|
||||||
// Addr specifies the bind address for the debug server.
|
// Addr specifies the bind address for the debug server.
|
||||||
Addr string `yaml:"addr,omitempty"`
|
Addr string `yaml:"addr,omitempty"`
|
||||||
|
// Prometheus configures the Prometheus telemetry endpoint.
|
||||||
|
Prometheus struct {
|
||||||
|
Enabled bool `yaml:"enabled,omitempty"`
|
||||||
|
Path string `yaml:"path,omitempty"`
|
||||||
|
} `yaml:"prometheus,omitempty"`
|
||||||
} `yaml:"debug,omitempty"`
|
} `yaml:"debug,omitempty"`
|
||||||
|
|
||||||
// HTTP2 configuration options
|
// HTTP2 configuration options
|
||||||
HTTP2 struct {
|
HTTP2 struct {
|
||||||
// Specifies wether the registry should disallow clients attempting
|
// Specifies whether the registry should disallow clients attempting
|
||||||
// to connect via http2. If set to true, only http/1.1 is supported.
|
// to connect via http2. If set to true, only http/1.1 is supported.
|
||||||
Disabled bool `yaml:"disabled,omitempty"`
|
Disabled bool `yaml:"disabled,omitempty"`
|
||||||
} `yaml:"http2,omitempty"`
|
} `yaml:"http2,omitempty"`
|
||||||
|
@ -186,6 +201,8 @@ type Configuration struct {
|
||||||
// TrustKey is the signing key to use for adding the signature to
|
// TrustKey is the signing key to use for adding the signature to
|
||||||
// schema1 manifests.
|
// schema1 manifests.
|
||||||
TrustKey string `yaml:"signingkeyfile,omitempty"`
|
TrustKey string `yaml:"signingkeyfile,omitempty"`
|
||||||
|
// Enabled determines if schema1 manifests should be pullable
|
||||||
|
Enabled bool `yaml:"enabled,omitempty"`
|
||||||
} `yaml:"schema1,omitempty"`
|
} `yaml:"schema1,omitempty"`
|
||||||
} `yaml:"compatibility,omitempty"`
|
} `yaml:"compatibility,omitempty"`
|
||||||
|
|
||||||
|
@ -238,7 +255,7 @@ type LogHook struct {
|
||||||
// Levels set which levels of log message will let hook executed.
|
// Levels set which levels of log message will let hook executed.
|
||||||
Levels []string `yaml:"levels,omitempty"`
|
Levels []string `yaml:"levels,omitempty"`
|
||||||
|
|
||||||
// MailOptions allows user to configurate email parameters.
|
// MailOptions allows user to configure email parameters.
|
||||||
MailOptions MailOptions `yaml:"options,omitempty"`
|
MailOptions MailOptions `yaml:"options,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +349,7 @@ type Health struct {
|
||||||
type v0_1Configuration Configuration
|
type v0_1Configuration Configuration
|
||||||
|
|
||||||
// UnmarshalYAML implements the yaml.Unmarshaler interface
|
// UnmarshalYAML implements the yaml.Unmarshaler interface
|
||||||
// Unmarshals a string of the form X.Y into a Version, validating that X and Y can represent uints
|
// Unmarshals a string of the form X.Y into a Version, validating that X and Y can represent unsigned integers
|
||||||
func (version *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (version *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
var versionString string
|
var versionString string
|
||||||
err := unmarshal(&versionString)
|
err := unmarshal(&versionString)
|
||||||
|
@ -374,7 +391,7 @@ func (loglevel *Loglevel) UnmarshalYAML(unmarshal func(interface{}) error) error
|
||||||
switch loglevelString {
|
switch loglevelString {
|
||||||
case "error", "warn", "info", "debug":
|
case "error", "warn", "info", "debug":
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Invalid loglevel %s Must be one of [error, warn, info, debug]", loglevelString)
|
return fmt.Errorf("invalid loglevel %s Must be one of [error, warn, info, debug]", loglevelString)
|
||||||
}
|
}
|
||||||
|
|
||||||
*loglevel = Loglevel(loglevelString)
|
*loglevel = Loglevel(loglevelString)
|
||||||
|
@ -449,7 +466,7 @@ func (storage *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(types) > 1 {
|
if len(types) > 1 {
|
||||||
return fmt.Errorf("Must provide exactly one storage type. Provided: %v", types)
|
return fmt.Errorf("must provide exactly one storage type. Provided: %v", types)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*storage = storageMap
|
*storage = storageMap
|
||||||
|
@ -537,6 +554,8 @@ func (auth Auth) MarshalYAML() (interface{}, error) {
|
||||||
|
|
||||||
// Notifications configures multiple http endpoints.
|
// Notifications configures multiple http endpoints.
|
||||||
type Notifications struct {
|
type Notifications struct {
|
||||||
|
// EventConfig is the configuration for the event format that is sent to each Endpoint.
|
||||||
|
EventConfig Events `yaml:"events,omitempty"`
|
||||||
// Endpoints is a list of http configurations for endpoints that
|
// Endpoints is a list of http configurations for endpoints that
|
||||||
// respond to webhook notifications. In the future, we may allow other
|
// respond to webhook notifications. In the future, we may allow other
|
||||||
// kinds of endpoints, such as external queues.
|
// kinds of endpoints, such as external queues.
|
||||||
|
@ -554,6 +573,18 @@ type Endpoint struct {
|
||||||
Threshold int `yaml:"threshold"` // circuit breaker threshold before backing off on failure
|
Threshold int `yaml:"threshold"` // circuit breaker threshold before backing off on failure
|
||||||
Backoff time.Duration `yaml:"backoff"` // backoff duration
|
Backoff time.Duration `yaml:"backoff"` // backoff duration
|
||||||
IgnoredMediaTypes []string `yaml:"ignoredmediatypes"` // target media types to ignore
|
IgnoredMediaTypes []string `yaml:"ignoredmediatypes"` // target media types to ignore
|
||||||
|
Ignore Ignore `yaml:"ignore"` // ignore event types
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events configures notification events.
|
||||||
|
type Events struct {
|
||||||
|
IncludeReferences bool `yaml:"includereferences"` // include reference data in manifest events
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ignore configures mediaTypes and actions of the event, that it won't be propagated
|
||||||
|
type Ignore struct {
|
||||||
|
MediaTypes []string `yaml:"mediatypes"` // target media types to ignore
|
||||||
|
Actions []string `yaml:"actions"` // ignore action types
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reporting defines error reporting methods.
|
// Reporting defines error reporting methods.
|
||||||
|
@ -626,15 +657,22 @@ func Parse(rd io.Reader) (*Configuration, error) {
|
||||||
ParseAs: reflect.TypeOf(v0_1Configuration{}),
|
ParseAs: reflect.TypeOf(v0_1Configuration{}),
|
||||||
ConversionFunc: func(c interface{}) (interface{}, error) {
|
ConversionFunc: func(c interface{}) (interface{}, error) {
|
||||||
if v0_1, ok := c.(*v0_1Configuration); ok {
|
if v0_1, ok := c.(*v0_1Configuration); ok {
|
||||||
if v0_1.Loglevel == Loglevel("") {
|
if v0_1.Log.Level == Loglevel("") {
|
||||||
v0_1.Loglevel = Loglevel("info")
|
if v0_1.Loglevel != Loglevel("") {
|
||||||
|
v0_1.Log.Level = v0_1.Loglevel
|
||||||
|
} else {
|
||||||
|
v0_1.Log.Level = Loglevel("info")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v0_1.Loglevel != Loglevel("") {
|
||||||
|
v0_1.Loglevel = Loglevel("")
|
||||||
}
|
}
|
||||||
if v0_1.Storage.Type() == "" {
|
if v0_1.Storage.Type() == "" {
|
||||||
return nil, fmt.Errorf("No storage configuration provided")
|
return nil, errors.New("no storage configuration provided")
|
||||||
}
|
}
|
||||||
return (*Configuration)(v0_1), nil
|
return (*Configuration)(v0_1), nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Expected *v0_1Configuration, received %#v", c)
|
return nil, fmt.Errorf("expected *v0_1Configuration, received %#v", c)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
@ -22,14 +23,14 @@ var configStruct = Configuration{
|
||||||
AccessLog struct {
|
AccessLog struct {
|
||||||
Disabled bool `yaml:"disabled,omitempty"`
|
Disabled bool `yaml:"disabled,omitempty"`
|
||||||
} `yaml:"accesslog,omitempty"`
|
} `yaml:"accesslog,omitempty"`
|
||||||
Level Loglevel `yaml:"level"`
|
Level Loglevel `yaml:"level,omitempty"`
|
||||||
Formatter string `yaml:"formatter,omitempty"`
|
Formatter string `yaml:"formatter,omitempty"`
|
||||||
Fields map[string]interface{} `yaml:"fields,omitempty"`
|
Fields map[string]interface{} `yaml:"fields,omitempty"`
|
||||||
Hooks []LogHook `yaml:"hooks,omitempty"`
|
Hooks []LogHook `yaml:"hooks,omitempty"`
|
||||||
}{
|
}{
|
||||||
|
Level: "info",
|
||||||
Fields: map[string]interface{}{"environment": "test"},
|
Fields: map[string]interface{}{"environment": "test"},
|
||||||
},
|
},
|
||||||
Loglevel: "info",
|
|
||||||
Storage: Storage{
|
Storage: Storage{
|
||||||
"s3": Parameters{
|
"s3": Parameters{
|
||||||
"region": "us-east-1",
|
"region": "us-east-1",
|
||||||
|
@ -63,6 +64,10 @@ var configStruct = Configuration{
|
||||||
"Authorization": []string{"Bearer <example>"},
|
"Authorization": []string{"Bearer <example>"},
|
||||||
},
|
},
|
||||||
IgnoredMediaTypes: []string{"application/octet-stream"},
|
IgnoredMediaTypes: []string{"application/octet-stream"},
|
||||||
|
Ignore: Ignore{
|
||||||
|
MediaTypes: []string{"application/octet-stream"},
|
||||||
|
Actions: []string{"pull"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -73,18 +78,25 @@ var configStruct = Configuration{
|
||||||
Prefix string `yaml:"prefix,omitempty"`
|
Prefix string `yaml:"prefix,omitempty"`
|
||||||
Secret string `yaml:"secret,omitempty"`
|
Secret string `yaml:"secret,omitempty"`
|
||||||
RelativeURLs bool `yaml:"relativeurls,omitempty"`
|
RelativeURLs bool `yaml:"relativeurls,omitempty"`
|
||||||
|
DrainTimeout time.Duration `yaml:"draintimeout,omitempty"`
|
||||||
TLS struct {
|
TLS struct {
|
||||||
Certificate string `yaml:"certificate,omitempty"`
|
Certificate string `yaml:"certificate,omitempty"`
|
||||||
Key string `yaml:"key,omitempty"`
|
Key string `yaml:"key,omitempty"`
|
||||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||||
|
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||||
LetsEncrypt struct {
|
LetsEncrypt struct {
|
||||||
CacheFile string `yaml:"cachefile,omitempty"`
|
CacheFile string `yaml:"cachefile,omitempty"`
|
||||||
Email string `yaml:"email,omitempty"`
|
Email string `yaml:"email,omitempty"`
|
||||||
|
Hosts []string `yaml:"hosts,omitempty"`
|
||||||
} `yaml:"letsencrypt,omitempty"`
|
} `yaml:"letsencrypt,omitempty"`
|
||||||
} `yaml:"tls,omitempty"`
|
} `yaml:"tls,omitempty"`
|
||||||
Headers http.Header `yaml:"headers,omitempty"`
|
Headers http.Header `yaml:"headers,omitempty"`
|
||||||
Debug struct {
|
Debug struct {
|
||||||
Addr string `yaml:"addr,omitempty"`
|
Addr string `yaml:"addr,omitempty"`
|
||||||
|
Prometheus struct {
|
||||||
|
Enabled bool `yaml:"enabled,omitempty"`
|
||||||
|
Path string `yaml:"path,omitempty"`
|
||||||
|
} `yaml:"prometheus,omitempty"`
|
||||||
} `yaml:"debug,omitempty"`
|
} `yaml:"debug,omitempty"`
|
||||||
HTTP2 struct {
|
HTTP2 struct {
|
||||||
Disabled bool `yaml:"disabled,omitempty"`
|
Disabled bool `yaml:"disabled,omitempty"`
|
||||||
|
@ -94,9 +106,11 @@ var configStruct = Configuration{
|
||||||
Certificate string `yaml:"certificate,omitempty"`
|
Certificate string `yaml:"certificate,omitempty"`
|
||||||
Key string `yaml:"key,omitempty"`
|
Key string `yaml:"key,omitempty"`
|
||||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||||
|
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||||
LetsEncrypt struct {
|
LetsEncrypt struct {
|
||||||
CacheFile string `yaml:"cachefile,omitempty"`
|
CacheFile string `yaml:"cachefile,omitempty"`
|
||||||
Email string `yaml:"email,omitempty"`
|
Email string `yaml:"email,omitempty"`
|
||||||
|
Hosts []string `yaml:"hosts,omitempty"`
|
||||||
} `yaml:"letsencrypt,omitempty"`
|
} `yaml:"letsencrypt,omitempty"`
|
||||||
}{
|
}{
|
||||||
ClientCAs: []string{"/path/to/ca.pem"},
|
ClientCAs: []string{"/path/to/ca.pem"},
|
||||||
|
@ -116,9 +130,9 @@ var configStruct = Configuration{
|
||||||
var configYamlV0_1 = `
|
var configYamlV0_1 = `
|
||||||
version: 0.1
|
version: 0.1
|
||||||
log:
|
log:
|
||||||
|
level: info
|
||||||
fields:
|
fields:
|
||||||
environment: test
|
environment: test
|
||||||
loglevel: info
|
|
||||||
storage:
|
storage:
|
||||||
s3:
|
s3:
|
||||||
region: us-east-1
|
region: us-east-1
|
||||||
|
@ -142,6 +156,11 @@ notifications:
|
||||||
Authorization: [Bearer <example>]
|
Authorization: [Bearer <example>]
|
||||||
ignoredmediatypes:
|
ignoredmediatypes:
|
||||||
- application/octet-stream
|
- application/octet-stream
|
||||||
|
ignore:
|
||||||
|
mediatypes:
|
||||||
|
- application/octet-stream
|
||||||
|
actions:
|
||||||
|
- pull
|
||||||
reporting:
|
reporting:
|
||||||
bugsnag:
|
bugsnag:
|
||||||
apikey: BugsnagApiKey
|
apikey: BugsnagApiKey
|
||||||
|
@ -156,7 +175,8 @@ http:
|
||||||
// storage driver with no parameters
|
// storage driver with no parameters
|
||||||
var inmemoryConfigYamlV0_1 = `
|
var inmemoryConfigYamlV0_1 = `
|
||||||
version: 0.1
|
version: 0.1
|
||||||
loglevel: info
|
log:
|
||||||
|
level: info
|
||||||
storage: inmemory
|
storage: inmemory
|
||||||
auth:
|
auth:
|
||||||
silly:
|
silly:
|
||||||
|
@ -170,6 +190,11 @@ notifications:
|
||||||
Authorization: [Bearer <example>]
|
Authorization: [Bearer <example>]
|
||||||
ignoredmediatypes:
|
ignoredmediatypes:
|
||||||
- application/octet-stream
|
- application/octet-stream
|
||||||
|
ignore:
|
||||||
|
mediatypes:
|
||||||
|
- application/octet-stream
|
||||||
|
actions:
|
||||||
|
- pull
|
||||||
http:
|
http:
|
||||||
headers:
|
headers:
|
||||||
X-Content-Type-Options: [nosniff]
|
X-Content-Type-Options: [nosniff]
|
||||||
|
@ -192,6 +217,7 @@ func (suite *ConfigSuite) TestMarshalRoundtrip(c *C) {
|
||||||
configBytes, err := yaml.Marshal(suite.expectedConfig)
|
configBytes, err := yaml.Marshal(suite.expectedConfig)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
config, err := Parse(bytes.NewReader(configBytes))
|
config, err := Parse(bytes.NewReader(configBytes))
|
||||||
|
c.Log(string(configBytes))
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||||
}
|
}
|
||||||
|
@ -314,9 +340,9 @@ func (suite *ConfigSuite) TestParseWithSameEnvLoglevel(c *C) {
|
||||||
// TestParseWithDifferentEnvLoglevel validates that providing an environment variable defining the
|
// TestParseWithDifferentEnvLoglevel validates that providing an environment variable defining the
|
||||||
// log level will override the value provided in the yaml document
|
// log level will override the value provided in the yaml document
|
||||||
func (suite *ConfigSuite) TestParseWithDifferentEnvLoglevel(c *C) {
|
func (suite *ConfigSuite) TestParseWithDifferentEnvLoglevel(c *C) {
|
||||||
suite.expectedConfig.Loglevel = "error"
|
suite.expectedConfig.Log.Level = "error"
|
||||||
|
|
||||||
os.Setenv("REGISTRY_LOGLEVEL", "error")
|
os.Setenv("REGISTRY_LOG_LEVEL", "error")
|
||||||
|
|
||||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
@ -516,9 +542,7 @@ func copyConfig(config Configuration) *Configuration {
|
||||||
}
|
}
|
||||||
|
|
||||||
configCopy.Notifications = Notifications{Endpoints: []Endpoint{}}
|
configCopy.Notifications = Notifications{Endpoints: []Endpoint{}}
|
||||||
for _, v := range config.Notifications.Endpoints {
|
configCopy.Notifications.Endpoints = append(configCopy.Notifications.Endpoints, config.Notifications.Endpoints...)
|
||||||
configCopy.Notifications.Endpoints = append(configCopy.Notifications.Endpoints, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
configCopy.HTTP.Headers = make(http.Header)
|
configCopy.HTTP.Headers = make(http.Header)
|
||||||
for k, v := range config.HTTP.Headers {
|
for k, v := range config.HTTP.Headers {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ func (p *Parser) Parse(in []byte, v interface{}) error {
|
||||||
|
|
||||||
parseInfo, ok := p.mapping[versionedStruct.Version]
|
parseInfo, ok := p.mapping[versionedStruct.Version]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Unsupported version: %q", versionedStruct.Version)
|
return fmt.Errorf("unsupported version: %q", versionedStruct.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAs := reflect.New(parseInfo.ParseAs)
|
parseAs := reflect.New(parseInfo.ParseAs)
|
||||||
|
@ -220,7 +220,7 @@ func (p *Parser) overwriteStruct(v reflect.Value, fullpath string, path []string
|
||||||
}
|
}
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if field.IsNil() {
|
if field.IsNil() {
|
||||||
field.Set(reflect.New(sf.Type))
|
field.Set(reflect.New(field.Type().Elem()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
70
configuration/parser_test.go
Normal file
70
configuration/parser_test.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type localConfiguration struct {
|
||||||
|
Version Version `yaml:"version"`
|
||||||
|
Log *Log `yaml:"log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Log struct {
|
||||||
|
Formatter string `yaml:"formatter,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedConfig = localConfiguration{
|
||||||
|
Version: "0.1",
|
||||||
|
Log: &Log{
|
||||||
|
Formatter: "json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParserSuite struct{}
|
||||||
|
|
||||||
|
var _ = Suite(new(ParserSuite))
|
||||||
|
|
||||||
|
func (suite *ParserSuite) TestParserOverwriteIninitializedPoiner(c *C) {
|
||||||
|
config := localConfiguration{}
|
||||||
|
|
||||||
|
os.Setenv("REGISTRY_LOG_FORMATTER", "json")
|
||||||
|
defer os.Unsetenv("REGISTRY_LOG_FORMATTER")
|
||||||
|
|
||||||
|
p := NewParser("registry", []VersionedParseInfo{
|
||||||
|
{
|
||||||
|
Version: "0.1",
|
||||||
|
ParseAs: reflect.TypeOf(config),
|
||||||
|
ConversionFunc: func(c interface{}) (interface{}, error) {
|
||||||
|
return c, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
err := p.Parse([]byte(`{version: "0.1", log: {formatter: "text"}}`), &config)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Assert(config, DeepEquals, expectedConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ParserSuite) TestParseOverwriteUnininitializedPoiner(c *C) {
|
||||||
|
config := localConfiguration{}
|
||||||
|
|
||||||
|
os.Setenv("REGISTRY_LOG_FORMATTER", "json")
|
||||||
|
defer os.Unsetenv("REGISTRY_LOG_FORMATTER")
|
||||||
|
|
||||||
|
p := NewParser("registry", []VersionedParseInfo{
|
||||||
|
{
|
||||||
|
Version: "0.1",
|
||||||
|
ParseAs: reflect.TypeOf(config),
|
||||||
|
ConversionFunc: func(c interface{}) (interface{}, error) {
|
||||||
|
return c, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
err := p.Parse([]byte(`{version: "0.1"}`), &config)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Assert(config, DeepEquals, expectedConfig)
|
||||||
|
}
|
|
@ -1,21 +1,16 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/distribution/uuid"
|
"github.com/docker/distribution/uuid"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context is a copy of Context from the golang.org/x/net/context package.
|
|
||||||
type Context interface {
|
|
||||||
context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// instanceContext is a context that provides only an instance id. It is
|
// instanceContext is a context that provides only an instance id. It is
|
||||||
// provided as the main background context.
|
// provided as the main background context.
|
||||||
type instanceContext struct {
|
type instanceContext struct {
|
||||||
Context
|
context.Context
|
||||||
id string // id of context, logged as "instance.id"
|
id string // id of context, logged as "instance.id"
|
||||||
once sync.Once // once protect generation of the id
|
once sync.Once // once protect generation of the id
|
||||||
}
|
}
|
||||||
|
@ -42,17 +37,10 @@ var background = &instanceContext{
|
||||||
// Background returns a non-nil, empty Context. The background context
|
// Background returns a non-nil, empty Context. The background context
|
||||||
// provides a single key, "instance.id" that is globally unique to the
|
// provides a single key, "instance.id" that is globally unique to the
|
||||||
// process.
|
// process.
|
||||||
func Background() Context {
|
func Background() context.Context {
|
||||||
return background
|
return background
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithValue returns a copy of parent in which the value associated with key is
|
|
||||||
// val. Use context Values only for request-scoped data that transits processes
|
|
||||||
// and APIs, not for passing optional parameters to functions.
|
|
||||||
func WithValue(parent Context, key, val interface{}) Context {
|
|
||||||
return context.WithValue(parent, key, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringMapContext is a simple context implementation that checks a map for a
|
// stringMapContext is a simple context implementation that checks a map for a
|
||||||
// key, falling back to a parent if not present.
|
// key, falling back to a parent if not present.
|
||||||
type stringMapContext struct {
|
type stringMapContext struct {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Package context provides several utilities for working with
|
// Package context provides several utilities for working with
|
||||||
// golang.org/x/net/context in http requests. Primarily, the focus is on
|
// Go's context in http requests. Primarily, the focus is on logging relevant
|
||||||
// logging relevant request information but this package is not limited to
|
// request information but this package is not limited to that purpose.
|
||||||
// that purpose.
|
|
||||||
//
|
//
|
||||||
// The easiest way to get started is to get the background context:
|
// The easiest way to get started is to get the background context:
|
||||||
//
|
//
|
||||||
|
@ -64,7 +63,7 @@
|
||||||
// Note that this only affects the new context, the previous context, with the
|
// Note that this only affects the new context, the previous context, with the
|
||||||
// version field, can be used independently. Put another way, the new logger,
|
// version field, can be used independently. Put another way, the new logger,
|
||||||
// added to the request context, is unique to that context and can have
|
// added to the request context, is unique to that context and can have
|
||||||
// request scoped varaibles.
|
// request scoped variables.
|
||||||
//
|
//
|
||||||
// HTTP Requests
|
// HTTP Requests
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -8,9 +9,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/distribution/uuid"
|
"github.com/docker/distribution/uuid"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Common errors used with this package.
|
// Common errors used with this package.
|
||||||
|
@ -68,7 +69,7 @@ func RemoteIP(r *http.Request) string {
|
||||||
// is available at "http.request". Other common attributes are available under
|
// is available at "http.request". Other common attributes are available under
|
||||||
// the prefix "http.request.". If a request is already present on the context,
|
// the prefix "http.request.". If a request is already present on the context,
|
||||||
// this method will panic.
|
// this method will panic.
|
||||||
func WithRequest(ctx Context, r *http.Request) Context {
|
func WithRequest(ctx context.Context, r *http.Request) context.Context {
|
||||||
if ctx.Value("http.request") != nil {
|
if ctx.Value("http.request") != nil {
|
||||||
// NOTE(stevvooe): This needs to be considered a programming error. It
|
// NOTE(stevvooe): This needs to be considered a programming error. It
|
||||||
// is unlikely that we'd want to have more than one request in
|
// is unlikely that we'd want to have more than one request in
|
||||||
|
@ -87,7 +88,7 @@ func WithRequest(ctx Context, r *http.Request) Context {
|
||||||
// GetRequest returns the http request in the given context. Returns
|
// GetRequest returns the http request in the given context. Returns
|
||||||
// ErrNoRequestContext if the context does not have an http request associated
|
// ErrNoRequestContext if the context does not have an http request associated
|
||||||
// with it.
|
// with it.
|
||||||
func GetRequest(ctx Context) (*http.Request, error) {
|
func GetRequest(ctx context.Context) (*http.Request, error) {
|
||||||
if r, ok := ctx.Value("http.request").(*http.Request); r != nil && ok {
|
if r, ok := ctx.Value("http.request").(*http.Request); r != nil && ok {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
@ -96,25 +97,13 @@ func GetRequest(ctx Context) (*http.Request, error) {
|
||||||
|
|
||||||
// GetRequestID attempts to resolve the current request id, if possible. An
|
// GetRequestID attempts to resolve the current request id, if possible. An
|
||||||
// error is return if it is not available on the context.
|
// error is return if it is not available on the context.
|
||||||
func GetRequestID(ctx Context) string {
|
func GetRequestID(ctx context.Context) string {
|
||||||
return GetStringValue(ctx, "http.request.id")
|
return GetStringValue(ctx, "http.request.id")
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithResponseWriter returns a new context and response writer that makes
|
// WithResponseWriter returns a new context and response writer that makes
|
||||||
// interesting response statistics available within the context.
|
// interesting response statistics available within the context.
|
||||||
func WithResponseWriter(ctx Context, w http.ResponseWriter) (Context, http.ResponseWriter) {
|
func WithResponseWriter(ctx context.Context, w http.ResponseWriter) (context.Context, http.ResponseWriter) {
|
||||||
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
|
||||||
irwCN := &instrumentedResponseWriterCN{
|
|
||||||
instrumentedResponseWriter: instrumentedResponseWriter{
|
|
||||||
ResponseWriter: w,
|
|
||||||
Context: ctx,
|
|
||||||
},
|
|
||||||
CloseNotifier: closeNotifier,
|
|
||||||
}
|
|
||||||
|
|
||||||
return irwCN, irwCN
|
|
||||||
}
|
|
||||||
|
|
||||||
irw := instrumentedResponseWriter{
|
irw := instrumentedResponseWriter{
|
||||||
ResponseWriter: w,
|
ResponseWriter: w,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
|
@ -125,7 +114,7 @@ func WithResponseWriter(ctx Context, w http.ResponseWriter) (Context, http.Respo
|
||||||
// GetResponseWriter returns the http.ResponseWriter from the provided
|
// GetResponseWriter returns the http.ResponseWriter from the provided
|
||||||
// context. If not present, ErrNoResponseWriterContext is returned. The
|
// context. If not present, ErrNoResponseWriterContext is returned. The
|
||||||
// returned instance provides instrumentation in the context.
|
// returned instance provides instrumentation in the context.
|
||||||
func GetResponseWriter(ctx Context) (http.ResponseWriter, error) {
|
func GetResponseWriter(ctx context.Context) (http.ResponseWriter, error) {
|
||||||
v := ctx.Value("http.response")
|
v := ctx.Value("http.response")
|
||||||
|
|
||||||
rw, ok := v.(http.ResponseWriter)
|
rw, ok := v.(http.ResponseWriter)
|
||||||
|
@ -145,7 +134,7 @@ var getVarsFromRequest = mux.Vars
|
||||||
// example, if looking for the variable "name", it can be accessed as
|
// example, if looking for the variable "name", it can be accessed as
|
||||||
// "vars.name". Implementations that are accessing values need not know that
|
// "vars.name". Implementations that are accessing values need not know that
|
||||||
// the underlying context is implemented with gorilla/mux vars.
|
// the underlying context is implemented with gorilla/mux vars.
|
||||||
func WithVars(ctx Context, r *http.Request) Context {
|
func WithVars(ctx context.Context, r *http.Request) context.Context {
|
||||||
return &muxVarsContext{
|
return &muxVarsContext{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
vars: getVarsFromRequest(r),
|
vars: getVarsFromRequest(r),
|
||||||
|
@ -155,7 +144,7 @@ func WithVars(ctx Context, r *http.Request) Context {
|
||||||
// GetRequestLogger returns a logger that contains fields from the request in
|
// GetRequestLogger returns a logger that contains fields from the request in
|
||||||
// the current context. If the request is not available in the context, no
|
// the current context. If the request is not available in the context, no
|
||||||
// fields will display. Request loggers can safely be pushed onto the context.
|
// fields will display. Request loggers can safely be pushed onto the context.
|
||||||
func GetRequestLogger(ctx Context) Logger {
|
func GetRequestLogger(ctx context.Context) Logger {
|
||||||
return GetLogger(ctx,
|
return GetLogger(ctx,
|
||||||
"http.request.id",
|
"http.request.id",
|
||||||
"http.request.method",
|
"http.request.method",
|
||||||
|
@ -171,7 +160,7 @@ func GetRequestLogger(ctx Context) Logger {
|
||||||
// Because the values are read at call time, pushing a logger returned from
|
// Because the values are read at call time, pushing a logger returned from
|
||||||
// this function on the context will lead to missing or invalid data. Only
|
// this function on the context will lead to missing or invalid data. Only
|
||||||
// call this at the end of a request, after the response has been written.
|
// call this at the end of a request, after the response has been written.
|
||||||
func GetResponseLogger(ctx Context) Logger {
|
func GetResponseLogger(ctx context.Context) Logger {
|
||||||
l := getLogrusLogger(ctx,
|
l := getLogrusLogger(ctx,
|
||||||
"http.response.written",
|
"http.response.written",
|
||||||
"http.response.status",
|
"http.response.status",
|
||||||
|
@ -188,7 +177,7 @@ func GetResponseLogger(ctx Context) Logger {
|
||||||
|
|
||||||
// httpRequestContext makes information about a request available to context.
|
// httpRequestContext makes information about a request available to context.
|
||||||
type httpRequestContext struct {
|
type httpRequestContext struct {
|
||||||
Context
|
context.Context
|
||||||
|
|
||||||
startedAt time.Time
|
startedAt time.Time
|
||||||
id string
|
id string
|
||||||
|
@ -247,7 +236,7 @@ fallback:
|
||||||
}
|
}
|
||||||
|
|
||||||
type muxVarsContext struct {
|
type muxVarsContext struct {
|
||||||
Context
|
context.Context
|
||||||
vars map[string]string
|
vars map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,20 +258,12 @@ func (ctx *muxVarsContext) Value(key interface{}) interface{} {
|
||||||
return ctx.Context.Value(key)
|
return ctx.Context.Value(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// instrumentedResponseWriterCN provides response writer information in a
|
|
||||||
// context. It implements http.CloseNotifier so that users can detect
|
|
||||||
// early disconnects.
|
|
||||||
type instrumentedResponseWriterCN struct {
|
|
||||||
instrumentedResponseWriter
|
|
||||||
http.CloseNotifier
|
|
||||||
}
|
|
||||||
|
|
||||||
// instrumentedResponseWriter provides response writer information in a
|
// instrumentedResponseWriter provides response writer information in a
|
||||||
// context. This variant is only used in the case where CloseNotifier is not
|
// context. This variant is only used in the case where CloseNotifier is not
|
||||||
// implemented by the parent ResponseWriter.
|
// implemented by the parent ResponseWriter.
|
||||||
type instrumentedResponseWriter struct {
|
type instrumentedResponseWriter struct {
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
Context
|
context.Context
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
status int
|
status int
|
||||||
|
@ -354,13 +335,3 @@ func (irw *instrumentedResponseWriter) Value(key interface{}) interface{} {
|
||||||
fallback:
|
fallback:
|
||||||
return irw.Context.Value(key)
|
return irw.Context.Value(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (irw *instrumentedResponseWriterCN) Value(key interface{}) interface{} {
|
|
||||||
if keyStr, ok := key.(string); ok {
|
|
||||||
if keyStr == "http.response" {
|
|
||||||
return irw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return irw.instrumentedResponseWriter.Value(key)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger provides a leveled-logging interface.
|
// Logger provides a leveled-logging interface.
|
||||||
|
@ -38,24 +39,28 @@ type Logger interface {
|
||||||
Warn(args ...interface{})
|
Warn(args ...interface{})
|
||||||
Warnf(format string, args ...interface{})
|
Warnf(format string, args ...interface{})
|
||||||
Warnln(args ...interface{})
|
Warnln(args ...interface{})
|
||||||
|
|
||||||
|
WithError(err error) *logrus.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type loggerKey struct{}
|
||||||
|
|
||||||
// WithLogger creates a new context with provided logger.
|
// WithLogger creates a new context with provided logger.
|
||||||
func WithLogger(ctx Context, logger Logger) Context {
|
func WithLogger(ctx context.Context, logger Logger) context.Context {
|
||||||
return WithValue(ctx, "logger", logger)
|
return context.WithValue(ctx, loggerKey{}, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLoggerWithField returns a logger instance with the specified field key
|
// GetLoggerWithField returns a logger instance with the specified field key
|
||||||
// and value without affecting the context. Extra specified keys will be
|
// and value without affecting the context. Extra specified keys will be
|
||||||
// resolved from the context.
|
// resolved from the context.
|
||||||
func GetLoggerWithField(ctx Context, key, value interface{}, keys ...interface{}) Logger {
|
func GetLoggerWithField(ctx context.Context, key, value interface{}, keys ...interface{}) Logger {
|
||||||
return getLogrusLogger(ctx, keys...).WithField(fmt.Sprint(key), value)
|
return getLogrusLogger(ctx, keys...).WithField(fmt.Sprint(key), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLoggerWithFields returns a logger instance with the specified fields
|
// GetLoggerWithFields returns a logger instance with the specified fields
|
||||||
// without affecting the context. Extra specified keys will be resolved from
|
// without affecting the context. Extra specified keys will be resolved from
|
||||||
// the context.
|
// the context.
|
||||||
func GetLoggerWithFields(ctx Context, fields map[interface{}]interface{}, keys ...interface{}) Logger {
|
func GetLoggerWithFields(ctx context.Context, fields map[interface{}]interface{}, keys ...interface{}) Logger {
|
||||||
// must convert from interface{} -> interface{} to string -> interface{} for logrus.
|
// must convert from interface{} -> interface{} to string -> interface{} for logrus.
|
||||||
lfields := make(logrus.Fields, len(fields))
|
lfields := make(logrus.Fields, len(fields))
|
||||||
for key, value := range fields {
|
for key, value := range fields {
|
||||||
|
@ -71,7 +76,7 @@ func GetLoggerWithFields(ctx Context, fields map[interface{}]interface{}, keys .
|
||||||
// argument passed to GetLogger will be passed to fmt.Sprint when expanded as
|
// argument passed to GetLogger will be passed to fmt.Sprint when expanded as
|
||||||
// a logging key field. If context keys are integer constants, for example,
|
// a logging key field. If context keys are integer constants, for example,
|
||||||
// its recommended that a String method is implemented.
|
// its recommended that a String method is implemented.
|
||||||
func GetLogger(ctx Context, keys ...interface{}) Logger {
|
func GetLogger(ctx context.Context, keys ...interface{}) Logger {
|
||||||
return getLogrusLogger(ctx, keys...)
|
return getLogrusLogger(ctx, keys...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,11 +84,11 @@ func GetLogger(ctx Context, keys ...interface{}) Logger {
|
||||||
// are provided, they will be resolved on the context and included in the
|
// are provided, they will be resolved on the context and included in the
|
||||||
// logger. Only use this function if specific logrus functionality is
|
// logger. Only use this function if specific logrus functionality is
|
||||||
// required.
|
// required.
|
||||||
func getLogrusLogger(ctx Context, keys ...interface{}) *logrus.Entry {
|
func getLogrusLogger(ctx context.Context, keys ...interface{}) *logrus.Entry {
|
||||||
var logger *logrus.Entry
|
var logger *logrus.Entry
|
||||||
|
|
||||||
// Get a logger, if it is present.
|
// Get a logger, if it is present.
|
||||||
loggerInterface := ctx.Value("logger")
|
loggerInterface := ctx.Value(loggerKey{})
|
||||||
if loggerInterface != nil {
|
if loggerInterface != nil {
|
||||||
if lgr, ok := loggerInterface.(*logrus.Entry); ok {
|
if lgr, ok := loggerInterface.(*logrus.Entry); ok {
|
||||||
logger = lgr
|
logger = lgr
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ import (
|
||||||
//
|
//
|
||||||
// Notice that the function name is automatically resolved, along with the
|
// Notice that the function name is automatically resolved, along with the
|
||||||
// package and a trace id is emitted that can be linked with parent ids.
|
// package and a trace id is emitted that can be linked with parent ids.
|
||||||
func WithTrace(ctx Context) (Context, func(format string, a ...interface{})) {
|
func WithTrace(ctx context.Context) (context.Context, func(format string, a ...interface{})) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = Background()
|
ctx = Background()
|
||||||
}
|
}
|
||||||
|
@ -69,7 +70,7 @@ func WithTrace(ctx Context) (Context, func(format string, a ...interface{})) {
|
||||||
// also provides fast lookup for the various attributes that are available on
|
// also provides fast lookup for the various attributes that are available on
|
||||||
// the trace.
|
// the trace.
|
||||||
type traced struct {
|
type traced struct {
|
||||||
Context
|
context.Context
|
||||||
id string
|
id string
|
||||||
parent string
|
parent string
|
||||||
start time.Time
|
start time.Time
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -35,7 +36,7 @@ func TestWithTrace(t *testing.T) {
|
||||||
ctx, done := WithTrace(Background())
|
ctx, done := WithTrace(Background())
|
||||||
defer done("this will be emitted at end of test")
|
defer done("this will be emitted at end of test")
|
||||||
|
|
||||||
checkContextForValues(t, ctx, append(base, valueTestCase{
|
checkContextForValues(ctx, t, append(base, valueTestCase{
|
||||||
key: "trace.func",
|
key: "trace.func",
|
||||||
expected: f.Name(),
|
expected: f.Name(),
|
||||||
}))
|
}))
|
||||||
|
@ -48,7 +49,7 @@ func TestWithTrace(t *testing.T) {
|
||||||
ctx, done := WithTrace(ctx)
|
ctx, done := WithTrace(ctx)
|
||||||
defer done("this should be subordinate to the other trace")
|
defer done("this should be subordinate to the other trace")
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
checkContextForValues(t, ctx, append(base, valueTestCase{
|
checkContextForValues(ctx, t, append(base, valueTestCase{
|
||||||
key: "trace.func",
|
key: "trace.func",
|
||||||
expected: f.Name(),
|
expected: f.Name(),
|
||||||
}, valueTestCase{
|
}, valueTestCase{
|
||||||
|
@ -67,8 +68,7 @@ type valueTestCase struct {
|
||||||
notnilorempty bool // just check not empty/not nil
|
notnilorempty bool // just check not empty/not nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkContextForValues(t *testing.T, ctx Context, values []valueTestCase) {
|
func checkContextForValues(ctx context.Context, t *testing.T, values []valueTestCase) {
|
||||||
|
|
||||||
for _, testcase := range values {
|
for _, testcase := range values {
|
||||||
v := ctx.Value(testcase.key)
|
v := ctx.Value(testcase.key)
|
||||||
if testcase.notnilorempty {
|
if testcase.notnilorempty {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Since looks up key, which should be a time.Time, and returns the duration
|
// Since looks up key, which should be a time.Time, and returns the duration
|
||||||
// since that time. If the key is not found, the value returned will be zero.
|
// since that time. If the key is not found, the value returned will be zero.
|
||||||
// This is helpful when inferring metrics related to context execution times.
|
// This is helpful when inferring metrics related to context execution times.
|
||||||
func Since(ctx Context, key interface{}) time.Duration {
|
func Since(ctx context.Context, key interface{}) time.Duration {
|
||||||
if startedAt, ok := ctx.Value(key).(time.Time); ok {
|
if startedAt, ok := ctx.Value(key).(time.Time); ok {
|
||||||
return time.Since(startedAt)
|
return time.Since(startedAt)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +17,7 @@ func Since(ctx Context, key interface{}) time.Duration {
|
||||||
|
|
||||||
// GetStringValue returns a string value from the context. The empty string
|
// GetStringValue returns a string value from the context. The empty string
|
||||||
// will be returned if not found.
|
// will be returned if not found.
|
||||||
func GetStringValue(ctx Context, key interface{}) (value string) {
|
func GetStringValue(ctx context.Context, key interface{}) (value string) {
|
||||||
if valuev, ok := ctx.Value(key).(string); ok {
|
if valuev, ok := ctx.Value(key).(string); ok {
|
||||||
value = valuev
|
value = valuev
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type versionKey struct{}
|
||||||
|
|
||||||
|
func (versionKey) String() string { return "version" }
|
||||||
|
|
||||||
// WithVersion stores the application version in the context. The new context
|
// WithVersion stores the application version in the context. The new context
|
||||||
// gets a logger to ensure log messages are marked with the application
|
// gets a logger to ensure log messages are marked with the application
|
||||||
// version.
|
// version.
|
||||||
func WithVersion(ctx Context, version string) Context {
|
func WithVersion(ctx context.Context, version string) context.Context {
|
||||||
ctx = WithValue(ctx, "version", version)
|
ctx = context.WithValue(ctx, versionKey{}, version)
|
||||||
// push a new logger onto the stack
|
// push a new logger onto the stack
|
||||||
return WithLogger(ctx, GetLogger(ctx, "version"))
|
return WithLogger(ctx, GetLogger(ctx, versionKey{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVersion returns the application version from the context. An empty
|
// GetVersion returns the application version from the context. An empty
|
||||||
// string may returned if the version was not set on the context.
|
// string may returned if the version was not set on the context.
|
||||||
func GetVersion(ctx Context) string {
|
func GetVersion(ctx context.Context) string {
|
||||||
return GetStringValue(ctx, "version")
|
return GetStringValue(ctx, versionKey{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ to the 1.0 registry. Requests from newer clients will route to the 2.0 registry.
|
||||||
Removing intermediate container edb84c2b40cb
|
Removing intermediate container edb84c2b40cb
|
||||||
Successfully built 74acc70fa106
|
Successfully built 74acc70fa106
|
||||||
|
|
||||||
The commmand outputs its progress until it completes.
|
The command outputs its progress until it completes.
|
||||||
|
|
||||||
4. Start your configuration with compose.
|
4. Start your configuration with compose.
|
||||||
|
|
||||||
|
@ -123,22 +123,22 @@ to the 1.0 registry. Requests from newer clients will route to the 2.0 registry.
|
||||||
|
|
||||||
4. Use `curl` to list the image in the registry.
|
4. Use `curl` to list the image in the registry.
|
||||||
|
|
||||||
$ curl -v -X GET http://localhost:32777/v2/registry1/tags/list
|
$ curl -v -X GET http://localhost:5000/v2/registry_one/tags/list
|
||||||
* Hostname was NOT found in DNS cache
|
* Hostname was NOT found in DNS cache
|
||||||
* Trying 127.0.0.1...
|
* Trying 127.0.0.1...
|
||||||
* Connected to localhost (127.0.0.1) port 32777 (#0)
|
* Connected to localhost (127.0.0.1) port 32777 (#0)
|
||||||
> GET /v2/registry1/tags/list HTTP/1.1
|
> GET /v2/registry1/tags/list HTTP/1.1
|
||||||
> User-Agent: curl/7.36.0
|
> User-Agent: curl/7.36.0
|
||||||
> Host: localhost:32777
|
> Host: localhost:5000
|
||||||
> Accept: */*
|
> Accept: */*
|
||||||
>
|
>
|
||||||
< HTTP/1.1 200 OK
|
< HTTP/1.1 200 OK
|
||||||
< Content-Type: application/json; charset=utf-8
|
< Content-Type: application/json
|
||||||
< Docker-Distribution-Api-Version: registry/2.0
|
< Docker-Distribution-Api-Version: registry/2.0
|
||||||
< Date: Tue, 14 Apr 2015 22:34:13 GMT
|
< Date: Tue, 14 Apr 2015 22:34:13 GMT
|
||||||
< Content-Length: 39
|
< Content-Length: 39
|
||||||
<
|
<
|
||||||
{"name":"registry1","tags":["latest"]}
|
{"name":"registry_one","tags":["latest"]}
|
||||||
* Connection #0 to host localhost left intact
|
* Connection #0 to host localhost left intact
|
||||||
|
|
||||||
This example refers to the specific port assigned to the 2.0 registry. You saw
|
This example refers to the specific port assigned to the 2.0 registry. You saw
|
||||||
|
|
|
@ -4,3 +4,6 @@ proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_read_timeout 900;
|
proxy_read_timeout 900;
|
||||||
|
proxy_send_timeout 300;
|
||||||
|
proxy_request_buffering off; (see issue #2292 - https://github.com/moby/moby/issues/2292)
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
|
@ -35,7 +35,7 @@ the [release page](https://github.com/docker/golem/releases/tag/v0.1).
|
||||||
|
|
||||||
#### Running golem with docker
|
#### Running golem with docker
|
||||||
|
|
||||||
Additionally golem can be run as a docker image requiring no additonal
|
Additionally golem can be run as a docker image requiring no additional
|
||||||
installation.
|
installation.
|
||||||
|
|
||||||
`docker run --privileged -v "$GOPATH/src/github.com/docker/distribution/contrib/docker-integration:/test" -w /test distribution/golem golem -rundaemon .`
|
`docker run --privileged -v "$GOPATH/src/github.com/docker/distribution/contrib/docker-integration:/test" -w /test distribution/golem golem -rundaemon .`
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIC9TCCAd+gAwIBAgIQKQTGjKpSVBW78ef0fOcxRTALBgkqhkiG9w0BAQswJjER
|
MIIC+TCCAeGgAwIBAgIQJMzVQNYVNTbh36kZUytWiDANBgkqhkiG9w0BAQsFADAm
|
||||||
MA8GA1UEChMIUXVpY2tUTFMxETAPBgNVBAMTCFF1aWNrVExTMB4XDTE1MDgyMDIz
|
MREwDwYDVQQKEwhRdWlja1RMUzERMA8GA1UEAxMIUXVpY2tUTFMwHhcNMTgwNTIx
|
||||||
MjE0OVoXDTE4MDgwNDIzMjE0OVowJjERMA8GA1UEChMIUXVpY2tUTFMxETAPBgNV
|
MjI1OTA2WhcNMjgwODI2MjI1OTA2WjAmMREwDwYDVQQKEwhRdWlja1RMUzERMA8G
|
||||||
BAMTCFF1aWNrVExTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwoPM
|
A1UEAxMIUXVpY2tUTFMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCe
|
||||||
xiDZK6Fwy5r3waRkfJHhyZZH828Jyj+nz5UVkMyOM/xN6MgJ2w911hTj1wSXG2n3
|
8rEU8xHh6BMYVRz/KhFftKSxS4dxJi2LoNN4fxzY6EgHNfBACt2MhIWaUSHf2YkF
|
||||||
AohF3gTFNrDYh4j2qRZnixDrOM5GBm2/KJbyfBIYkrR45yLfjidO7MRnhaPZ5Fov
|
NsS/T7qZWq23NEuIJYUUwbJRAh/iQsEhCI56eV+aJX+DGd2SQQNKdx1Pt528LNws
|
||||||
l+RKwNBXP4Q2mUe7q9FM457Rm8hAcqXP04AJT20m1QSYQivDgxsDxuAQte3VEy1E
|
n8Ci8rEHTe6i2/U7n/DLqa32BWF3aShsVrchRgpizXezS7GLyFmhv0hi0zRKJgDG
|
||||||
0j0CwUKoFHT6MHOnDPEZbc4r1+ba34WBM1Sc5KXyV2JlbtU07J4hACYWVsD7vQCl
|
JebLeqe/BUtEOsS/Oa65NQTEO/5EZBzM74+4eRo5zyp9Uvw4edmOrXRXK1fK9gP3
|
||||||
VFlZNE4E35ahMDZ+ODLal9PAT8ARLdAtjvRWrT+h8qZ4Yfwt/sGF1K4CAkTP3H5p
|
Fq/jz9+8b5eUd9vl0e9z/xTqMdicYZOUHuUtxM3hXAkkxcaVJqqqDe6URbJHpbaN
|
||||||
uMkJG56zmqIEYeHMuwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAKQwDwYDVR0TAQH/
|
8Vt/p/csFXMWj3oSokvDAgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMB
|
||||||
BAUwAwEB/zALBgkqhkiG9w0BAQsDggEBALpieTckiPEeb3rTAWl7waDPLPOIhS5C
|
Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCC3NiX+2Qk3WB+TRNDPCtQ7Pw+
|
||||||
XHVfOm7cPmRn3pT2VuR8y74U7a1uOkYMgJnCWb8lSXhbqC89FatLnAhKqo4I9oD8
|
o31SSqfF8m3fevT4mdrJqFAF4qUpDwgV9/9EkU4UBoIq03S91Dk/No0jR3VAzzRA
|
||||||
2BXgYeIpP5/OWBcjzmsMnowrvokc0chAmAR0Ux6AP0eX9amC0lGMuTHdw3+is0AR
|
h3+ul/7u08JriS/ZgVediodi7H8xeCz3nvZfAwCP2ZmHzDGp39Uhc3L3WFZImZuV
|
||||||
lhoImOUPXvgMH7W2RimpSgnX0R5wKqfuGwMfbGa0xhWBZ+wekAKcU8b+pIHDyX0c
|
fCDeSWF3c5CjJbdUuCYYFy6LwSFLPoBXZaNBL19XP9btJtjbNTm77PZJ4cELTQ+U
|
||||||
EQcir2y8/lVjECXSAIlV6iasPQ3hm1sd0xq1hx4yrwYFvQb7yEhOXbK24HLr/20D
|
r5Ofw9D9mCCYrapmprw7Fw9wdE+iLL9EJCHAj7L8UYshF4+7O7Jv3ZatySMWPbjS
|
||||||
RRmEOuS8gg2XtUFv66z/VOw/nUleIg9GAuWDJaiu9frmIma4/tIY4qY=
|
nIa2+eKl/sfvRvLZWV9dUSObVsm/bpv8bsHIKp4bYl+IDb2aoSWnw4eZQHDJ
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
||||||
SWDxLrvS33UCtsCcpPggjehnxKaC
|
e38+Kzctl5iVbrB+JWY6roTQ26VLIdlS7A==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -1,29 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
|
||||||
KauB3KY1NiL+Ng5DCzNdkwDkWH78ZguI2w==
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,29 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
|
||||||
SpS/cacZYUsSLgB4cWbxElcc/p7CW1PbOA==
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,51 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -1,29 +1,19 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
5O3gRtj/UQjegI0XV4YO2TQ=
|
||||||
X70TONscsBL/kx5MK4xoAmb+EN6Yy8i+z9NkNJd0B+2MjXPMFBpgGb0UiPv2wEmZ
|
|
||||||
5PAKyjwTxNIm6L/nFhkmVqfsQHfjHukXES4C0ff6fj6fuDpBfl5nTlVmc9LpP+hT
|
|
||||||
5RAwW10qumucGxAWGNBWW+K66cf8O7n/0nQykxJxYjBx16ZB80H2uvqFDKDVFqze
|
|
||||||
co5M4euXQq9KiXPRlcC9rab2a7FGLHd0TyPkq6TvfsqpxcryyKS4rIAz3sQh/tl/
|
|
||||||
/qm1tBcZW2bce3UlF2Wb2dW9HqvIu1O84f6ptLqwgKcIdTbwgQZ0kbFoWE2kWJSV
|
|
||||||
w+eAFb7tz1LDTpF3NRlz+1K27pBQWRQgcqoIRoQXpC0LfQY9Mp70QIfUQdUh6tnO
|
|
||||||
8hmq5y623tfxiDwCxb/EOpwCmwK1Cp9cloZTDefVE1r6NkEJWeeHG79VljUGF1KT
|
|
||||||
NKzXWrrsFtge/hU9Pj+frcZO9qExxPCcsrdZcoK7Ll8s+pjulRvbnCnJkNpeOI3P
|
|
||||||
iz6+sdGmzKSKg2daRM67Zmy5tmlBEX/eV7kFqt+b3HsdUiLo3Ng2lyPLNNDfwUtB
|
|
||||||
EukgYGjVJoyqLjLXgsCxLJlk7X/ogVwf8SlAnQ7p6KuxGWm02vlUpEmJp+Hq
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,51 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -1,30 +1,19 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
gqxjLkTNOdSva6Fkk/z8BD2XSZ4L+nM3Mw==
|
||||||
p3tFsnWUJCiOh9ywsc2NcmJdleKDc4/spElFMUarHqcE1ua6EH15O5GEnHWKj8EY
|
|
||||||
PVQFrPvf30UkRGNPl8eC7afZtCNk9MLllIATAzBr5Z1i+psV7MmgBKpbZ4B0TnhR
|
|
||||||
GXNT60QaCJ9RfUuc2z7RHJNo9XTn3Q44X7TFj+P3jHOWzTf8y6Mz6saTy2bugIUy
|
|
||||||
AfRgRgq/bB8hRjrazg55FIlrMv7dr3J0cIuqmaHfsw7Q2ECMCXW8oQXMBzfuIT0n
|
|
||||||
sG4u0oVxdNx4OdHsAubGjjwNDhxJvN5j8+YFqZMu03i8LbyamTwsrZg2C3QrRUq8
|
|
||||||
SujQEEB+AmO0lpuJ24FsOOYVSYCpLy2ugrKOr2NUqbiBKZs8uBh6RGACfunMZlEw
|
|
||||||
4BntohiO7oZ5gjvhGZNUEqzMChw7knvVjZ+DkhFk9yE4qIL7VsJSUNI2ZJym/Xeq
|
|
||||||
jr/oT8CpP8/mFZspa6DFciPfhGLQqKcaZZohL7461pOYWY5C2vsJNR2ucBZzTFvD
|
|
||||||
BiN/rMnIGFrxUscCCje6RLmrsZ3Lb7bfhB3W6kwzLRfr/XEygAzx6S2mlOM34kqF
|
|
||||||
HFpKrg9TtLIpYLAKAIfuNbrLaNP1UKh7iLarhDz/qDcvRka/qJTzLD3eLeGXefAP
|
|
||||||
KjJ1S7s=
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,51 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -1,29 +1,18 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIE9DCCAt6gAwIBAgIQb58oJ+9SvWUCcYWA+L1oiTALBgkqhkiG9w0BAQswJjER
|
MIIC+DCCAeCgAwIBAgIQTCXTJncsLpgueaMqQF6AiTANBgkqhkiG9w0BAQsFADAm
|
||||||
MA8GA1UEChMIUXVpY2tUTFMxETAPBgNVBAMTCFF1aWNrVExTMB4XDTE1MDUyNjIw
|
MREwDwYDVQQKEwhRdWlja1RMUzERMA8GA1UEAxMIUXVpY2tUTFMwHhcNMTgwNTIx
|
||||||
NTUwMFoXDTE4MDUxMDIwNTUwMFowEzERMA8GA1UEChMIUXVpY2tUTFMwggIiMA0G
|
MjI1NzI4WhcNMjgwODI2MjI1NzI4WjATMREwDwYDVQQKEwhRdWlja1RMUzCCASIw
|
||||||
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDDmOL3EhBm4So3agPMmF0z1+/nPlrE
|
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL0fYn9wE7phMA6CFT6gv7mDpzSB
|
||||||
xoG7x0HYPk5CP3PF3TNVk3ArBPkMzge0/895a4ZEb9j+LUQEjOZa/ZwuLmSjfJSt
|
LkebCxj3LfU/isdgXvtXUn+BKIolvav7oJyTyz1R0NzX5uXxEERMBUW89KWvPLPK
|
||||||
9xTXI1ldp8KasyzQZjC33/bUj7FGxGzgbHyJrGGBoH2W5HdswH4WzhCnGTslyiDo
|
o3d47MWMcAgiYx2+FeGZo1cjq3IRVKyg3WRVw2rO0YNL3K1QCS93A+IdA/05muwt
|
||||||
VN4hklJ7gr+Geq3TPf8Eji+1L71MOrUyoNp7BaQBQT/gKxK0nV+ZuSk6eaiu+om7
|
346XJ2FV0tPmETn6t+So2e9ZXh+uJjcCHq4XpJAJznCwemzzRpDe7nG5sYZqq+Oz
|
||||||
slp3x4bc21o7eIMmNXggJP6p9fMDctnioKhAPcm+5ADiFYSjivLeUQ85VkMTpmdU
|
zBQ/bTC8rOdqW5woH/GhQHYHcKf1taPLmDLczVPQCqS3LAEK5EOUElfpQykfkZI4
|
||||||
yvq6ziK3Ls6erD+S3xLvcHYAaeu84qLd7qdPwkHMTQsDpO4vPMIwL8piMzZV+kwL
|
clOZBhJ0e5zNEBTB/XRd7uuUA57Ig58l7hbX0fUPHgS9MF1z9CXJ40BSm/sCAwEA
|
||||||
Bq+5xk5//FwnQH0pSo2Nr4vRn+DITZc3GKyGUJQoOUgAdfGNskTt8GXa4IsHn5iw
|
AaM1MDMwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1Ud
|
||||||
zr12vGaxb//GDm0RLHnh7NVbD8xxDHIJq+fJNFb7MdXa8v31PYebkWuaPhYt6HQC
|
EwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAHKH54KZdpvcLRIJK4yeSqwOigYp
|
||||||
I/D81zwcJIOGfzNITS2ifM5tvMaUXireo4pLC2v2aSY6RrPq1owlB6jGFwGwZSAF
|
0NHM9U8RlHjmf5Tp9lCtZpVrkfUtg9rXytekAXfd6GaNex7swTMNPnJBGgaQ2vA8
|
||||||
O6rxSqWO1gLfhJLzqcw/NjWnO7nCZEs/iKgAa22K2CtTt3dDMTvSBYKdkRe/FYQC
|
0jdtKfe6AcHTYQV1rs0qunlR8i26cNhYblKPJjYYA6FBzTTtybXhHYG9xvYpSVpo
|
||||||
MCa7MFJSaH85pYRzoDN4IuVpvROrtuQmlI47oZzb64uCPoA4A8AN+k8iysqITsgK
|
XcrsC81DYK6nMiQMRYuT7RO/rtI4Tzx+laYc0lYgBzf6pXUjXycgAuJ5+cWT8DDn
|
||||||
1m8ePPXhbu4YlwIDAQABozUwMzAOBgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYI
|
OxPXbfAxfzc6jYfsigwzdOCnuIomFogm8ad47ApTTTLFrVtqCNJAKCu7HufEbB2G
|
||||||
KwYBBQUHAwIwDAYDVR0TAQH/BAIwADALBgkqhkiG9w0BAQsDggIBALSgrCdEQd3I
|
OKWvl9NmTPYetS6MO5LqLAWcf/uRPn+lufHeTfBWIDD5zbJ2+ATP+mQQ2d0=
|
||||||
vb/FNkNZkAwdjfBD6j7ZtPBwvjEiiyNTx9hOLBGvbey7kr0HtW0KkLWsdRmCc+3z
|
|
||||||
ev9I5VjDOtpiqrvuAA1wRBaL3UzGyj/eFjPJpvkfJi8zjkIZ2y18QG3yJ6Eqy6dD
|
|
||||||
0aIQAHl9hkXMOVrf364gf0p7EoOGtSlfQ56yIGDPTFKKiy+Al0S42p17lhI4coz9
|
|
||||||
zGXE1/SiNeZgdsk4zHDqhzzBp8foZuSL1sGcIXHkG8RtqZ1WvCyIPYRyIjIKZcXd
|
|
||||||
JCEM//EbgDzQ7VE/jm+hIlYfPjM7fmUzsfii+bIrp/0HGEU3HN++LsA6eQOwWPa/
|
|
||||||
PrxKPP36EVXb72QK8C3lmz6y+CHhuuAm0C1b1qmYVEs4eRE21S8eB2l0KUlfOecf
|
|
||||||
xZ1LWp1agKt6fGqRgcsR3/qO27l8W7hlbFNPeOTgr6NQQkEMRW5OxbnZ58ULXqr3
|
|
||||||
gWh8Na3D4+3j53035UBBQUMmeeFfWCvtr5n0+6BTAi62Cwwu9QQQBM/2f9/9K+B7
|
|
||||||
cW0xPYtczm+VwJL6/rDtNN9xPWitxab1dkZp2XcHG3VWtYvE2R2EtEoKvvCLPggx
|
|
||||||
zcafsZfcD1wlvtQF7YjykGJnMa0SB0GBl9SQtvGc8PkP39yXHqXZhIoo3fp4qm9v
|
|
||||||
RfbdpOr8p/Ks34ZqQPukFwpM1s/6aicF
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,51 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -1,29 +1,19 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
6LPZM1wJIwuYHIP74eH0ctQP
|
||||||
wE560/AcVZcbKKAZOnZ/262a++8tparsQt+bXBJ2so6YUqsFDNdOLCI2aShjWDRe
|
|
||||||
TNhqmLIO3FNsLRKp96WHVz+jFoiECsoYfKn0jgqTqxx+7nWFqgBaNSlF5cbCgLCH
|
|
||||||
jQV1uQhzsw/Mh/32hXAidkv/nLeLf7FbKq08hgthtoP+XstlzZ5BxkPodjb8XWXG
|
|
||||||
DSS49SWX971GHa1apwMKfxVGSppxn18ZwEmW1BUfQBNxtMytqA9DK3+xuoUdXkB0
|
|
||||||
iJbm3Jc10JSRju8iyL121Xt6f8O33paVz/ndDJIWztUOjnItc89rxHsINPt5+cUt
|
|
||||||
jix8ohwmHGDrK7ZooXBvotvmGT/xhPr2eHUAG8JuSJ/Cr09UUOwUEigz4CfgJOHm
|
|
||||||
XukdzjOkb4r7lhNmVeGqrjRol1W0Wsc1NGH++J6xdkIeQ+i23kHwFHfQWV/J69tm
|
|
||||||
rOn2N+qijtmbIy9YfVcrFDtUtEAzXylZ2StCVQNofd0M7tXNdrUL8yAFwlrhWGJV
|
|
||||||
wsSP++1xH2Ie6Diupy8z6rbP383HmnmVPU/UecgLrlX2lEpt/UZkkX1Xm+6PhrrT
|
|
||||||
HDeeULvqtUP3PD8wS0C873Pl9GXOKISqf0HKEIDUAVZhQOsGFqiZH0388M4L
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,51 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
MIIJKgIBAAKCAgEAsF4L07p6z6ZK0OjBR3WSIzn1WSKfPN0eATbgu8qYbFDp8Oed
|
MIIEpQIBAAKCAQEA2vfYJRcVQJbVzag+geOrvpPyNRMf9zE+JHk1psnXLiAwIGSj
|
||||||
PxL0vuLLOjFwlaW9Ie6l/Jla+KPRkjJFnbzxd6ZMQVirblErQgEYZ29jP06pPZHe
|
ash75NuVb1RyXEf3XWCN+lIDEW01/6VfmTCYAweFTylwJz4yOncDH1pwVdJe/PNM
|
||||||
VuUFCtcONKL1YbXqwbHrQwaEzZ6AYyoVDkMF9PXogrDTT5GMWAJXMnxPrgvPDzYT
|
OD7GPQ0aKDo4GTePI+473aUzwDiqzSYFn9PTiIuRrAuZ1FdWJGgYr9PsN/wc36Gb
|
||||||
R7t3qGXf1I/Jtj66+u1IgPrSFgaZeg51qIL2qq6K+SzrsFmmsY185Gu/Ik2sIUjq
|
tSwbRU7cbEWBJJNiGEpO7okxMAOzNsgU868HEYJQ2F99eIs3K+I7gCIF1wC/a/RJ
|
||||||
gpQVYetDU+kZ3a07Ak3U/eCSRLylWUCJ/plCQB/7O4q76XiPm0rEaUkCQmxp9UkM
|
Rp0Q+ZIabklvUMq18pjSNaLWTXM1KrOK39uEwv8QdtWRgnTRCYfnQwFO6enPrSAi
|
||||||
s7EEq61SPmayFrSDq4mAVdR7T4byGQ1wSN3xepRQLAg5jel2/hazJ0oGTe4f3Myd
|
AeO3jyfMlqssMwuhEUymkb1cgFCVSfeWjSBXBwIDAQABAoIBAGQMCf4oZdV1FYs5
|
||||||
iiXDneTWrQ5u+hjU1d9t4zEdAC0OLud9AaVp71y645LKbL3la6/N+FkUp09ZR7yU
|
7BV86OPSxT/q1Rgkr7gKibEDWAYDPvoOAXywzarriYOsmfQADc3kZ/qPrkcwFxQP
|
||||||
o31BcUhZWgIWc6bxPc/4U0JQT6nwMhaRqXpSX1MeL8rPinu3DKtSnUvBPkS1Jone
|
g3aC9XGs5gQdctj7WgfMiOiycdFEpZH9uD2asQkEC4eF0kvzTrukBkZnTRXuzlud
|
||||||
IYCYH0j7z/W+ZlPeU+Kdfw/6JUYznI5Sem2uwWbRtRwlZEPqJpMrUrGkf4iziHaS
|
m8RDDMu+uXhadJbIsNtBlMYBllSdS+LFxXcAYm+IsvTYzmwg4+bnjvOwMHO9SMSb
|
||||||
JSJLPW0L/1spolg3aj2jLurv5lV8dKYjEujxkVixpkFxeL3pwq648xHdfghI/AoO
|
1dfgOLkg/A++/GTjD/kUyCV5dc4lv2I0i2pXJkD2V0Dr6Yra1U/MRKcOwTGC2q/8
|
||||||
o/mKvFT9Bsj5hAEyyGAbhvQE2gxnnSDfSvcdyyJ/DinM9AXREMw4F4KI6LcCAwEA
|
hZuKm9DgvGXvZsG0+yT5fsexGRwTxmByvfj+QMF3LCTDCknD4d/mmEEX0EEGPlW2
|
||||||
AQKCAgEAnrHg/oD7ZMEC7PuifoRCHMRYCf5nPkLQbtNMYG2pvT0JY6VlDo4l/2Te
|
I7OgKEECgYEA/LkdwnXy7ymis1Rgjumc3ydcLoCqV3ExaxXrvO50EkRpgRX/TLEk
|
||||||
7NvzrBPYHSI55RKwkq4FMwFdNtP+imTulJYOm1MaE2gc52WI7jv/eNE6OQIWCWz8
|
j98iVYyksiaJuMhqnxNttT6GwWJvwIXFPP9WpIGmzi4GKyqYGEX4WbyPoY9hjt/G
|
||||||
8Uv4dBVWyTcos8S31rTaXWBOVejlAUgMERy+5wfWOpLQlzLYF4m0pMFJk/AReUtB
|
muR67cTXg6ssiSssUCoQnWIHyuGDJfzRWqnoei0dIA2GobOwFJtXeV0CgYEA3c6u
|
||||||
nmhLXlsPsB22cag/RWZmzzcXk6tT/LzVe+R5ptLkdTsUuAxjjaBKVCDiMuDAZL1m
|
utbNtmbyp17Jffx01ee8Wprhnoz7Nh/dJMLngpIx3i8qQqpFB8TPNUTu+GLgGcol
|
||||||
dah3h8oKIMab8l0SABumxKqYAKkyvbSJQUhSUYAT5+3c0cfJ6q7WoMk8TqvnwfpQ
|
n9BDzZszoVhsxybn7Lgm/OjS/jQL4hosFoqztThkg28L8UD7QB0TyCucwgk2lgOe
|
||||||
2Klbcaa4G6+79H8e/a41RWmcMVTTpLKmwzx/iMLPswLnTFbWYCsLSsml3OpmXPhG
|
VxyX25kNSXzxdCYaKr1+6g2gtBAb0zPj2E+5t7MCgYEAimoA6J6dHWwaVkmiUOOW
|
||||||
CKdbIWMvNMBfahZmnCP2pNcZBVY1/k/lEw25ehtnWqA7HplawT6V3gk/Bzz+3e3R
|
LYprLHT/1sCCJnptEJ8xJ0gc2LxphWGH+txk+6H6GjCNQY1TCCkl7xx9xbDaMAGU
|
||||||
XEpioZF70ipdW5Pb3OG/tKSNDvRRjqLPk9UWlQzmedzu7XN28V/blw/CBVcMAcc0
|
E2Jt28++wjHm4wGDJ9g6uztRF1VmQ1BAgFkfEta6irzXuZDRxl4jl283gWCd6dJb
|
||||||
njwAledTuqv/wQ67dtbXdcxSPZbV/Rq7y3OmpgK6RWLIFzzpOPW5gULqUZfrnxtv
|
/2ILl87ZotKFqE6347Fo6WkCgYEAyDNyMMALIzTelkUO1wFUL3If5yPeuy4C3IJ8
|
||||||
StxVnlZXhFoymodFobTi7AYibsLaXLkunZWXEwFwdtLfFHznfHq/rHfBmna1lcKW
|
J18oeQkdq66klVF8RxvT7v/ONjGAlqaHuSzQ1jbcrifS3xp1wYsh3asELl+pziXT
|
||||||
MgWRqsbaoCsqHC1nc0E4llFkn3zqGYgMQNBeqNfX6cIPI/eQzPECggEBAOk0TP8N
|
X3FH7Sz+REep3tLJNMBKB6WdsuF//H09oOD1DEej342/nhd6DNPHRtiQEZZslwBC
|
||||||
edIFENOrzUtpH1fB3k15heeA84SeBhj8t/xrphR3o+IVO/GtMtq9hVLeYFVPwWCi
|
Cg9D0NMCgYEArNksPSQJSxXqxZsw17OTqQJnf3kNBI0SP9q6Wc8gN69r5YQcIHcr
|
||||||
Mmy4KhwNUOtFeCSX4MbpiXvoPEjL3QF+Sv95HsEWsT1iBQIN4aoV0ZSv48YsRczs
|
KgtfdiL4LawZFie6gcNu398ng7VYUzzkYR9j+G5qPetcqllQZeVc6cieUyR7Eul0
|
||||||
tLjr96hADLTMfpCwyRq9r8XVF/hnx7vqOoOC/J1kteRhjOWRnutFpdAMfkFgzUa9
|
WvtlUECCfweLFUsIhuHyEsGu1PrFYd98SlOzt24utguFss1539cEC3A=
|
||||||
1unmDHsDifcT+vpxief9Q9zK9xMYvYmwFkBUjOlhC7WchZC20nrwvM+A2mMBpeLB
|
|
||||||
WSRWsYeOqW8zcQNGdWuXXMKxsYHwv9tXbANVWxs1gz4x7BxcFoN5poIFrnT+eImY
|
|
||||||
EwhGrKR6jZsKF00CggEBAMGbdZU0+yvxL2tAul5RGAqv9xhdUV4eg8warTQ8/RWt
|
|
||||||
8Vef2wllBYnP48rXNDovb7ZNOjMBdjIWZ2zq2McMtHqpzP+zWQWaNT8/7Zi24JTL
|
|
||||||
y4G75kZdGgTPG2Y71seZoZGxfOu4gf7cLKOqxiHYrNDHEDl5Pi13tJD/8qf6hYm6
|
|
||||||
K3yALSv+QlM3mk+5oueKQ7Lj9rV81YomYSV5+K+WhszhvLmuxv0necOLKapeBWvL
|
|
||||||
GQ5038yAq3PFdu0HXzyA6L8YdusP1d3sqwQvLbi8KAMXJCeT6WZXGYgX2Rjfbuih
|
|
||||||
ZHUaE7Ac0EsJfMuOowSkS7oXuT81k64ngCoq5KZC5hMCggEBAKYkt9JiZG8HYuSb
|
|
||||||
GsjmHQllup5RvN+hVF0gRFHbAq2YeBtO3Xg+DpXxAjErIuhWPCWri6bwB6LDVmTj
|
|
||||||
68milaTke6TbTzLy0rg+Xbcppf766LlCFIYZ5l1/TE3j+4vGAC347sW/wkWY/7lj
|
|
||||||
4GmS43zsJmqhx6/XUJuOPJOZnZSCZr0vuhL6mOoZZDJUTXy62dx0PetvZsT/O9cM
|
|
||||||
P2fDWWTCLTEVlBqik4KMdsS4qjGsyzOeCzyZReNDDRO/nZTsRSqSSwARJhQom5Rr
|
|
||||||
RDVQXeyqbw93KAQhmshroBSB5Rc+4YiyCE3wPTo7NWL38XPi3lbF0VSd/rk/uNH5
|
|
||||||
6hcSCmUCggEAIPHjQFCTrRaNiyKolAQYozjuQyceAXYP11tyvcDjEB1ZRB/flemq
|
|
||||||
15iYmpukN4J67/qUPLmy8zL8xnvwB28SBw195MUQEPP8u5aVR7dW3/sN1jWzKaYO
|
|
||||||
F2Nmti7YjX6HD9Oz/iiXdlbhAbi9nmTQg3ZcPGt1OSd1gncLQ6pNrvIPFFB7X1EU
|
|
||||||
2DRN/eMI5X2Rp49DG/7yF2AQh+AJgVeL+LEw/CfRlKJzBeNYY7U8Fuuoh907eAEt
|
|
||||||
K7YeVpc6jYEiGeJ/2eAH9IuhTkT48saRyHTXoiR5QwDvR0lHmAPtS4irH4Igd4dv
|
|
||||||
qlUi90B+XPvYJwKCc08aojf2hzZlUiVwIQKCAQEAraCoWea8hLFchxmAiBt7joIg
|
|
||||||
nNK7a3LOHYxT1gB9H+PoVqTmzGVTeZpD8Jnis/UHmDhRYuUGqvFIefjAWbz0jJAN
|
|
||||||
t6RMAozENCG1PoeXHf1gt2wspv14kza+8jSdpzNrzZgPZdb7Wh1UEqUkiRYwn87f
|
|
||||||
C7DHknqCj9S2qq0DFXYz15JNPVrbvD+ZLBFJhTAjppS9TuYQVLf8JPYHpLRio/9A
|
|
||||||
dMsyOz1VA2RRYN0u/u4ccxiN45K3PbVMCeDPbWXNm8G75YKQ7LnIuehMB1qkZy6N
|
|
||||||
MOnNGp3l/ZkFK0JsW/pZqTQ2FqSkb0+ttTFApFI3qB04sc4s0uKPI9fa0OQtUw==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -1,30 +1,19 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
aSbIL/p+I6//AUMWJvcR7GbXy3xywgmaYw==
|
||||||
UL61ejMkTWQW8R4gKrcPHAOJAPKVfGEVOrQH3ZMyxV2HnWrJ7egrn65zOzmLbWSh
|
|
||||||
O7gdpL6YYjBr218fqJn/8HadXZa4k70JyympYOLojeWSLy3KP03U+y7AMcdE1uG6
|
|
||||||
6HJI54ZjBoW/nEyWmMh/mfMz8EN+Mgek48Z9AVaOswbtHtDIXN7XO0jbB3DbY5Yh
|
|
||||||
prVqVLYAz4sCchGTadj+aEChF5sJkKREDvAew/njC0WGS2TmMJ+V1uVhXV6354mr
|
|
||||||
edk79YvdwzwDgeYArkprahMtn9eu1aSTfUXsmM5OP5tR4gyFV1kUmTPY1yUd/yO+
|
|
||||||
638wV0mWtGbbf6j8dUKeUBCyt2qGg8J80OUeFdvdHMswtaUq951NApX44BinPkbK
|
|
||||||
moBVQByZ5OEcmMidFC9SqYSUwTQ7uNyWeguhCXav+l3x900YlKnUQgRUZntPwXjs
|
|
||||||
yc7MXv0j0E86Gme6G1O02zamwkRgr3qOTHu2oQOow/a24fM4HASayLR0Kegt0sh3
|
|
||||||
rzk0HRF1mGonf1Ecyyj/3LpHVsgYSckwtJoZLOqtDMn+CKtOCEByssQfD+E9Qe07
|
|
||||||
qMyvcwpXUpfqe3ZERbJ10m98Z88VeK/XGt9ptq7HY47n1KL6lx3oyXwZIw8pq928
|
|
||||||
89dcqL0=
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,51 +1,27 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
|
@ -7,6 +7,9 @@ storage:
|
||||||
rootdirectory: /tmp/registry-dev
|
rootdirectory: /tmp/registry-dev
|
||||||
http:
|
http:
|
||||||
addr: 0.0.0.0:5000
|
addr: 0.0.0.0:5000
|
||||||
|
compatibility:
|
||||||
|
schema1:
|
||||||
|
enabled: true
|
||||||
auth:
|
auth:
|
||||||
token:
|
token:
|
||||||
realm: "https://auth.localregistry:5559/token/"
|
realm: "https://auth.localregistry:5559/token/"
|
||||||
|
|
|
@ -10,6 +10,9 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certificate: "/etc/docker/registry/localregistry.cert"
|
certificate: "/etc/docker/registry/localregistry.cert"
|
||||||
key: "/etc/docker/registry/localregistry.key"
|
key: "/etc/docker/registry/localregistry.key"
|
||||||
|
compatibility:
|
||||||
|
schema1:
|
||||||
|
enabled: true
|
||||||
auth:
|
auth:
|
||||||
token:
|
token:
|
||||||
realm: "https://auth.localregistry:5559/token/"
|
realm: "https://auth.localregistry:5559/token/"
|
||||||
|
|
|
@ -10,6 +10,9 @@ http:
|
||||||
tls:
|
tls:
|
||||||
certificate: "/etc/docker/registry/localregistry.cert"
|
certificate: "/etc/docker/registry/localregistry.cert"
|
||||||
key: "/etc/docker/registry/localregistry.key"
|
key: "/etc/docker/registry/localregistry.key"
|
||||||
|
compatibility:
|
||||||
|
schema1:
|
||||||
|
enabled: true
|
||||||
auth:
|
auth:
|
||||||
token:
|
token:
|
||||||
realm: "https://auth.localregistry:5556/token/"
|
realm: "https://auth.localregistry:5556/token/"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
@ -9,13 +10,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
dcontext "github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/context"
|
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
"github.com/docker/distribution/registry/auth"
|
"github.com/docker/distribution/registry/auth"
|
||||||
_ "github.com/docker/distribution/registry/auth/htpasswd"
|
_ "github.com/docker/distribution/registry/auth/htpasswd"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -85,7 +86,7 @@ func main() {
|
||||||
// TODO: Make configurable
|
// TODO: Make configurable
|
||||||
issuer.Expiration = 15 * time.Minute
|
issuer.Expiration = 15 * time.Minute
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := dcontext.Background()
|
||||||
|
|
||||||
ts := &tokenServer{
|
ts := &tokenServer{
|
||||||
issuer: issuer,
|
issuer: issuer,
|
||||||
|
@ -115,23 +116,23 @@ func main() {
|
||||||
// request context from a base context.
|
// request context from a base context.
|
||||||
func handlerWithContext(ctx context.Context, handler func(context.Context, http.ResponseWriter, *http.Request)) http.Handler {
|
func handlerWithContext(ctx context.Context, handler func(context.Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := context.WithRequest(ctx, r)
|
ctx := dcontext.WithRequest(ctx, r)
|
||||||
logger := context.GetRequestLogger(ctx)
|
logger := dcontext.GetRequestLogger(ctx)
|
||||||
ctx = context.WithLogger(ctx, logger)
|
ctx = dcontext.WithLogger(ctx, logger)
|
||||||
|
|
||||||
handler(ctx, w, r)
|
handler(ctx, w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleError(ctx context.Context, err error, w http.ResponseWriter) {
|
func handleError(ctx context.Context, err error, w http.ResponseWriter) {
|
||||||
ctx, w = context.WithResponseWriter(ctx, w)
|
ctx, w = dcontext.WithResponseWriter(ctx, w)
|
||||||
|
|
||||||
if serveErr := errcode.ServeJSON(w, err); serveErr != nil {
|
if serveErr := errcode.ServeJSON(w, err); serveErr != nil {
|
||||||
context.GetResponseLogger(ctx).Errorf("error sending error response: %v", serveErr)
|
dcontext.GetResponseLogger(ctx).Errorf("error sending error response: %v", serveErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
context.GetResponseLogger(ctx).Info("application error")
|
dcontext.GetResponseLogger(ctx).Info("application error")
|
||||||
}
|
}
|
||||||
|
|
||||||
var refreshCharacters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
var refreshCharacters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
@ -173,13 +174,13 @@ func filterAccessList(ctx context.Context, scope string, requestedAccessList []a
|
||||||
for _, access := range requestedAccessList {
|
for _, access := range requestedAccessList {
|
||||||
if access.Type == "repository" {
|
if access.Type == "repository" {
|
||||||
if !strings.HasPrefix(access.Name, scope) {
|
if !strings.HasPrefix(access.Name, scope) {
|
||||||
context.GetLogger(ctx).Debugf("Resource scope not allowed: %s", access.Name)
|
dcontext.GetLogger(ctx).Debugf("Resource scope not allowed: %s", access.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if enforceRepoClass {
|
if enforceRepoClass {
|
||||||
if class, ok := repositoryClassCache[access.Name]; ok {
|
if class, ok := repositoryClassCache[access.Name]; ok {
|
||||||
if class != access.Class {
|
if class != access.Class {
|
||||||
context.GetLogger(ctx).Debugf("Different repository class: %q, previously %q", access.Class, class)
|
dcontext.GetLogger(ctx).Debugf("Different repository class: %q, previously %q", access.Class, class)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if strings.EqualFold(access.Action, "push") {
|
} else if strings.EqualFold(access.Action, "push") {
|
||||||
|
@ -188,12 +189,12 @@ func filterAccessList(ctx context.Context, scope string, requestedAccessList []a
|
||||||
}
|
}
|
||||||
} else if access.Type == "registry" {
|
} else if access.Type == "registry" {
|
||||||
if access.Name != "catalog" {
|
if access.Name != "catalog" {
|
||||||
context.GetLogger(ctx).Debugf("Unknown registry resource: %s", access.Name)
|
dcontext.GetLogger(ctx).Debugf("Unknown registry resource: %s", access.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO: Limit some actions to "admin" users
|
// TODO: Limit some actions to "admin" users
|
||||||
} else {
|
} else {
|
||||||
context.GetLogger(ctx).Debugf("Skipping unsupported resource type: %s", access.Type)
|
dcontext.GetLogger(ctx).Debugf("Skipping unsupported resource type: %s", access.Type)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
grantedAccessList = append(grantedAccessList, access)
|
grantedAccessList = append(grantedAccessList, access)
|
||||||
|
@ -216,7 +217,7 @@ func (grantedAccess) String() string { return "grantedAccess" }
|
||||||
// getToken handles authenticating the request and authorizing access to the
|
// getToken handles authenticating the request and authorizing access to the
|
||||||
// requested scopes.
|
// requested scopes.
|
||||||
func (ts *tokenServer) getToken(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
func (ts *tokenServer) getToken(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
context.GetLogger(ctx).Info("getToken")
|
dcontext.GetLogger(ctx).Info("getToken")
|
||||||
|
|
||||||
params := r.URL.Query()
|
params := r.URL.Query()
|
||||||
service := params.Get("service")
|
service := params.Get("service")
|
||||||
|
@ -242,30 +243,30 @@ func (ts *tokenServer) getToken(ctx context.Context, w http.ResponseWriter, r *h
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get response context.
|
// Get response context.
|
||||||
ctx, w = context.WithResponseWriter(ctx, w)
|
ctx, w = dcontext.WithResponseWriter(ctx, w)
|
||||||
|
|
||||||
challenge.SetHeaders(w)
|
challenge.SetHeaders(r, w)
|
||||||
handleError(ctx, errcode.ErrorCodeUnauthorized.WithDetail(challenge.Error()), w)
|
handleError(ctx, errcode.ErrorCodeUnauthorized.WithDetail(challenge.Error()), w)
|
||||||
|
|
||||||
context.GetResponseLogger(ctx).Info("get token authentication challenge")
|
dcontext.GetResponseLogger(ctx).Info("get token authentication challenge")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx = authorizedCtx
|
ctx = authorizedCtx
|
||||||
|
|
||||||
username := context.GetStringValue(ctx, "auth.user.name")
|
username := dcontext.GetStringValue(ctx, "auth.user.name")
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, acctSubject{}, username)
|
ctx = context.WithValue(ctx, acctSubject{}, username)
|
||||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, acctSubject{}))
|
ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, acctSubject{}))
|
||||||
|
|
||||||
context.GetLogger(ctx).Info("authenticated client")
|
dcontext.GetLogger(ctx).Info("authenticated client")
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestedAccess{}, requestedAccessList)
|
ctx = context.WithValue(ctx, requestedAccess{}, requestedAccessList)
|
||||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, requestedAccess{}))
|
ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, requestedAccess{}))
|
||||||
|
|
||||||
grantedAccessList := filterAccessList(ctx, username, requestedAccessList)
|
grantedAccessList := filterAccessList(ctx, username, requestedAccessList)
|
||||||
ctx = context.WithValue(ctx, grantedAccess{}, grantedAccessList)
|
ctx = context.WithValue(ctx, grantedAccess{}, grantedAccessList)
|
||||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, grantedAccess{}))
|
ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, grantedAccess{}))
|
||||||
|
|
||||||
token, err := ts.issuer.CreateJWT(username, service, grantedAccessList)
|
token, err := ts.issuer.CreateJWT(username, service, grantedAccessList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -273,7 +274,7 @@ func (ts *tokenServer) getToken(ctx context.Context, w http.ResponseWriter, r *h
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
context.GetLogger(ctx).Info("authorized client")
|
dcontext.GetLogger(ctx).Info("authorized client")
|
||||||
|
|
||||||
response := tokenResponse{
|
response := tokenResponse{
|
||||||
Token: token,
|
Token: token,
|
||||||
|
@ -288,12 +289,12 @@ func (ts *tokenServer) getToken(ctx context.Context, w http.ResponseWriter, r *h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, w = context.WithResponseWriter(ctx, w)
|
ctx, w = dcontext.WithResponseWriter(ctx, w)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
|
|
||||||
context.GetResponseLogger(ctx).Info("get token complete")
|
dcontext.GetResponseLogger(ctx).Info("get token complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
type postTokenResponse struct {
|
type postTokenResponse struct {
|
||||||
|
@ -378,16 +379,16 @@ func (ts *tokenServer) postToken(ctx context.Context, w http.ResponseWriter, r *
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, acctSubject{}, subject)
|
ctx = context.WithValue(ctx, acctSubject{}, subject)
|
||||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, acctSubject{}))
|
ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, acctSubject{}))
|
||||||
|
|
||||||
context.GetLogger(ctx).Info("authenticated client")
|
dcontext.GetLogger(ctx).Info("authenticated client")
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestedAccess{}, requestedAccessList)
|
ctx = context.WithValue(ctx, requestedAccess{}, requestedAccessList)
|
||||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, requestedAccess{}))
|
ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, requestedAccess{}))
|
||||||
|
|
||||||
grantedAccessList := filterAccessList(ctx, subject, requestedAccessList)
|
grantedAccessList := filterAccessList(ctx, subject, requestedAccessList)
|
||||||
ctx = context.WithValue(ctx, grantedAccess{}, grantedAccessList)
|
ctx = context.WithValue(ctx, grantedAccess{}, grantedAccessList)
|
||||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, grantedAccess{}))
|
ctx = dcontext.WithLogger(ctx, dcontext.GetLogger(ctx, grantedAccess{}))
|
||||||
|
|
||||||
token, err := ts.issuer.CreateJWT(subject, service, grantedAccessList)
|
token, err := ts.issuer.CreateJWT(subject, service, grantedAccessList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -395,7 +396,7 @@ func (ts *tokenServer) postToken(ctx context.Context, w http.ResponseWriter, r *
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
context.GetLogger(ctx).Info("authorized client")
|
dcontext.GetLogger(ctx).Info("authorized client")
|
||||||
|
|
||||||
response := postTokenResponse{
|
response := postTokenResponse{
|
||||||
Token: token,
|
Token: token,
|
||||||
|
@ -416,10 +417,10 @@ func (ts *tokenServer) postToken(ctx context.Context, w http.ResponseWriter, r *
|
||||||
response.RefreshToken = rToken
|
response.RefreshToken = rToken
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, w = context.WithResponseWriter(ctx, w)
|
ctx, w = dcontext.WithResponseWriter(ctx, w)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
|
|
||||||
context.GetResponseLogger(ctx).Info("post token complete")
|
dcontext.GetResponseLogger(ctx).Info("post token complete")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -11,7 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution/context"
|
dcontext "github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/registry/auth"
|
"github.com/docker/distribution/registry/auth"
|
||||||
"github.com/docker/distribution/registry/auth/token"
|
"github.com/docker/distribution/registry/auth/token"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
@ -27,7 +28,7 @@ func ResolveScopeSpecifiers(ctx context.Context, scopeSpecs []string) []auth.Acc
|
||||||
parts := strings.SplitN(scopeSpecifier, ":", 3)
|
parts := strings.SplitN(scopeSpecifier, ":", 3)
|
||||||
|
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
context.GetLogger(ctx).Infof("ignoring unsupported scope format %s", scopeSpecifier)
|
dcontext.GetLogger(ctx).Infof("ignoring unsupported scope format %s", scopeSpecifier)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
80
contrib/token-server/token_test.go
Normal file
80
contrib/token-server/token_test.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/registry/auth"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateJWTSuccessWithEmptyACL(t *testing.T) {
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pk, err := libtrust.FromCryptoPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tokenIssuer := TokenIssuer{
|
||||||
|
Expiration: time.Duration(100),
|
||||||
|
Issuer: "localhost",
|
||||||
|
SigningKey: pk,
|
||||||
|
}
|
||||||
|
|
||||||
|
grantedAccessList := make([]auth.Access, 0)
|
||||||
|
token, err := tokenIssuer.CreateJWT("test", "test", grantedAccessList)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := strings.Split(token, ".")
|
||||||
|
|
||||||
|
if len(token) == 0 {
|
||||||
|
t.Fatal("token not generated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := decodeJWT(tokens[1])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(json, "test") {
|
||||||
|
t.Fatal("Valid token was not generated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJWT(rawToken string) (string, error) {
|
||||||
|
data, err := joseBase64Decode(rawToken)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("Error in Decoding base64 String")
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func joseBase64Decode(s string) (string, error) {
|
||||||
|
switch len(s) % 4 {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
s += "=="
|
||||||
|
case 3:
|
||||||
|
s += "="
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return "", errors.New("Invalid base64 String")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data, err := base64.StdEncoding.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err //errors.New("Error in Decoding base64 String")
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# Given a subpackage and the containing package, figures out which packages
|
|
||||||
# need to be passed to `go test -coverpkg`: this includes all of the
|
|
||||||
# subpackage's dependencies within the containing package, as well as the
|
|
||||||
# subpackage itself.
|
|
||||||
DEPENDENCIES="$(go list -f $'{{range $f := .Deps}}{{$f}}\n{{end}}' ${1} | grep ${2} | grep -v github.com/docker/distribution/vendor)"
|
|
||||||
echo "${1} ${DEPENDENCIES}" | xargs echo -n | tr ' ' ','
|
|
|
@ -41,7 +41,7 @@ func TestLookup(t *testing.T) {
|
||||||
}
|
}
|
||||||
assertEqualDigests(t, dgst, digests[3])
|
assertEqualDigests(t, dgst, digests[3])
|
||||||
|
|
||||||
dgst, err = dset.Lookup("1234")
|
_, err = dset.Lookup("1234")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected ambiguous error looking up: 1234")
|
t.Fatal("Expected ambiguous error looking up: 1234")
|
||||||
}
|
}
|
||||||
|
@ -49,15 +49,15 @@ func TestLookup(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dgst, err = dset.Lookup("9876")
|
_, err = dset.Lookup("9876")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected ambiguous error looking up: 9876")
|
t.Fatal("Expected not found error looking up: 9876")
|
||||||
}
|
}
|
||||||
if err != ErrDigestNotFound {
|
if err != ErrDigestNotFound {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dgst, err = dset.Lookup("sha256:1234")
|
_, err = dset.Lookup("sha256:1234")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected ambiguous error looking up: sha256:1234")
|
t.Fatal("Expected ambiguous error looking up: sha256:1234")
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ func TestAddDuplication(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dset.entries) != 8 {
|
if len(dset.entries) != 8 {
|
||||||
t.Fatal("Duplicate digest insert allowed")
|
t.Fatal("Duplicate digest insert should not increase entries size")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dset.Add(digest.Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil {
|
if err := dset.Add(digest.Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||||
|
@ -126,7 +126,7 @@ func TestAddDuplication(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dset.entries) != 9 {
|
if len(dset.entries) != 9 {
|
||||||
t.Fatal("Insert with different algorithm not allowed")
|
t.Fatal("Insert with different algorithm should be allowed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
52
docs/architecture.md
Normal file
52
docs/architecture.md
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
published: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
## Design
|
||||||
|
**TODO(stevvooe):** Discuss the architecture of the registry, internally and externally, in a few different deployment scenarios.
|
||||||
|
|
||||||
|
### Eventual Consistency
|
||||||
|
|
||||||
|
> **NOTE:** This section belongs somewhere, perhaps in a design document. We
|
||||||
|
> are leaving this here so the information is not lost.
|
||||||
|
|
||||||
|
Running the registry on eventually consistent backends has been part of the
|
||||||
|
design from the beginning. This section covers some of the approaches to
|
||||||
|
dealing with this reality.
|
||||||
|
|
||||||
|
There are a few classes of issues that we need to worry about when
|
||||||
|
implementing something on top of the storage drivers:
|
||||||
|
|
||||||
|
1. Read-After-Write consistency (see this [article on
|
||||||
|
s3](http://shlomoswidler.com/2009/12/read-after-write-consistency-in-amazon.html)).
|
||||||
|
2. [Write-Write Conflicts](http://en.wikipedia.org/wiki/Write%E2%80%93write_conflict).
|
||||||
|
|
||||||
|
In reality, the registry must worry about these kinds of errors when doing the
|
||||||
|
following:
|
||||||
|
|
||||||
|
1. Accepting data into a temporary upload file may not have latest data block
|
||||||
|
yet (read-after-write).
|
||||||
|
2. Moving uploaded data into its blob location (write-write race).
|
||||||
|
3. Modifying the "current" manifest for given tag (write-write race).
|
||||||
|
4. A whole slew of operations around deletes (read-after-write, delete-write
|
||||||
|
races, garbage collection, etc.).
|
||||||
|
|
||||||
|
The backend path layout employs a few techniques to avoid these problems:
|
||||||
|
|
||||||
|
1. Large writes are done to private upload directories. This alleviates most
|
||||||
|
of the corruption potential under multiple writers by avoiding multiple
|
||||||
|
writers.
|
||||||
|
2. Constraints in storage driver implementations, such as support for writing
|
||||||
|
after the end of a file to extend it.
|
||||||
|
3. Digest verification to avoid data corruption.
|
||||||
|
4. Manifest files are stored by digest and cannot change.
|
||||||
|
5. All other non-content files (links, hashes, etc.) are written as an atomic
|
||||||
|
unit. Anything that requires additions and deletions is broken out into
|
||||||
|
separate "files". Last writer still wins.
|
||||||
|
|
||||||
|
Unfortunately, one must play this game when trying to build something like
|
||||||
|
this on top of eventually consistent storage systems. If we run into serious
|
||||||
|
problems, we can wrap the storagedrivers in a shared consistency layer but
|
||||||
|
that would increase complexity and hinder registry cluster performance.
|
|
@ -89,10 +89,6 @@ log:
|
||||||
to:
|
to:
|
||||||
- errors@example.com
|
- errors@example.com
|
||||||
loglevel: debug # deprecated: use "log"
|
loglevel: debug # deprecated: use "log"
|
||||||
plugins:
|
|
||||||
- /plugins/
|
|
||||||
- /plugin/driver1.so
|
|
||||||
- /plugin/auth1.so
|
|
||||||
storage:
|
storage:
|
||||||
filesystem:
|
filesystem:
|
||||||
rootdirectory: /var/lib/registry
|
rootdirectory: /var/lib/registry
|
||||||
|
@ -104,6 +100,17 @@ storage:
|
||||||
gcs:
|
gcs:
|
||||||
bucket: bucketname
|
bucket: bucketname
|
||||||
keyfile: /path/to/keyfile
|
keyfile: /path/to/keyfile
|
||||||
|
credentials:
|
||||||
|
type: service_account
|
||||||
|
project_id: project_id_string
|
||||||
|
private_key_id: private_key_id_string
|
||||||
|
private_key: private_key_string
|
||||||
|
client_email: client@example.com
|
||||||
|
client_id: client_id_string
|
||||||
|
auth_uri: http://example.com/auth_uri
|
||||||
|
token_uri: http://example.com/token_uri
|
||||||
|
auth_provider_x509_cert_url: http://example.com/provider_cert_url
|
||||||
|
client_x509_cert_url: http://example.com/client_cert_url
|
||||||
rootdirectory: /gcs/object/name/prefix
|
rootdirectory: /gcs/object/name/prefix
|
||||||
chunksize: 5242880
|
chunksize: 5242880
|
||||||
s3:
|
s3:
|
||||||
|
@ -140,7 +147,8 @@ storage:
|
||||||
endpoint: optional endpoints
|
endpoint: optional endpoints
|
||||||
internal: optional internal endpoint
|
internal: optional internal endpoint
|
||||||
bucket: OSS bucket
|
bucket: OSS bucket
|
||||||
encrypt: optional data encryption setting
|
encrypt: optional enable server-side encryption
|
||||||
|
encryptionkeyid: optional KMS key id for encryption
|
||||||
secure: optional ssl setting
|
secure: optional ssl setting
|
||||||
chunksize: optional size valye
|
chunksize: optional size valye
|
||||||
rootdirectory: optional root directory
|
rootdirectory: optional root directory
|
||||||
|
@ -164,6 +172,7 @@ auth:
|
||||||
realm: silly-realm
|
realm: silly-realm
|
||||||
service: silly-service
|
service: silly-service
|
||||||
token:
|
token:
|
||||||
|
autoredirect: true
|
||||||
realm: token-realm
|
realm: token-realm
|
||||||
service: token-service
|
service: token-service
|
||||||
issuer: registry-token-issuer
|
issuer: registry-token-issuer
|
||||||
|
@ -187,6 +196,10 @@ middleware:
|
||||||
privatekey: /path/to/pem
|
privatekey: /path/to/pem
|
||||||
keypairid: cloudfrontkeypairid
|
keypairid: cloudfrontkeypairid
|
||||||
duration: 3000s
|
duration: 3000s
|
||||||
|
ipfilteredby: awsregion
|
||||||
|
awsregion: us-east-1, use-east-2
|
||||||
|
updatefrequency: 12h
|
||||||
|
iprangesurl: https://ip-ranges.amazonaws.com/ip-ranges.json
|
||||||
storage:
|
storage:
|
||||||
- name: redirect
|
- name: redirect
|
||||||
options:
|
options:
|
||||||
|
@ -206,6 +219,7 @@ http:
|
||||||
host: https://myregistryaddress.org:5000
|
host: https://myregistryaddress.org:5000
|
||||||
secret: asecretforlocaldevelopment
|
secret: asecretforlocaldevelopment
|
||||||
relativeurls: false
|
relativeurls: false
|
||||||
|
draintimeout: 60s
|
||||||
tls:
|
tls:
|
||||||
certificate: /path/to/x509/public
|
certificate: /path/to/x509/public
|
||||||
key: /path/to/x509/private
|
key: /path/to/x509/private
|
||||||
|
@ -215,23 +229,34 @@ http:
|
||||||
letsencrypt:
|
letsencrypt:
|
||||||
cachefile: /path/to/cache-file
|
cachefile: /path/to/cache-file
|
||||||
email: emailused@letsencrypt.com
|
email: emailused@letsencrypt.com
|
||||||
|
hosts: [myregistryaddress.org]
|
||||||
debug:
|
debug:
|
||||||
addr: localhost:5001
|
addr: localhost:5001
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
path: /metrics
|
||||||
headers:
|
headers:
|
||||||
X-Content-Type-Options: [nosniff]
|
X-Content-Type-Options: [nosniff]
|
||||||
http2:
|
http2:
|
||||||
disabled: false
|
disabled: false
|
||||||
notifications:
|
notifications:
|
||||||
|
events:
|
||||||
|
includereferences: true
|
||||||
endpoints:
|
endpoints:
|
||||||
- name: alistener
|
- name: alistener
|
||||||
disabled: false
|
disabled: false
|
||||||
url: https://my.listener.com/event
|
url: https://my.listener.com/event
|
||||||
headers: <http.Header>
|
headers: <http.Header>
|
||||||
timeout: 500
|
timeout: 1s
|
||||||
threshold: 5
|
threshold: 10
|
||||||
backoff: 1000
|
backoff: 1s
|
||||||
ignoredmediatypes:
|
ignoredmediatypes:
|
||||||
- application/octet-stream
|
- application/octet-stream
|
||||||
|
ignore:
|
||||||
|
mediatypes:
|
||||||
|
- application/octet-stream
|
||||||
|
actions:
|
||||||
|
- pull
|
||||||
redis:
|
redis:
|
||||||
addr: localhost:6379
|
addr: localhost:6379
|
||||||
password: asecret
|
password: asecret
|
||||||
|
@ -271,6 +296,7 @@ proxy:
|
||||||
compatibility:
|
compatibility:
|
||||||
schema1:
|
schema1:
|
||||||
signingkeyfile: /etc/registry/key.json
|
signingkeyfile: /etc/registry/key.json
|
||||||
|
enabled: true
|
||||||
validation:
|
validation:
|
||||||
manifests:
|
manifests:
|
||||||
urls:
|
urls:
|
||||||
|
@ -363,21 +389,6 @@ loglevel: debug
|
||||||
Permitted values are `error`, `warn`, `info` and `debug`. The default is
|
Permitted values are `error`, `warn`, `info` and `debug`. The default is
|
||||||
`info`.
|
`info`.
|
||||||
|
|
||||||
## `plugins`
|
|
||||||
|
|
||||||
```none
|
|
||||||
plugins:
|
|
||||||
- /plugins/
|
|
||||||
- /plugin/driver1.so
|
|
||||||
- /plugin/auth1.so
|
|
||||||
```
|
|
||||||
|
|
||||||
> Requires golang >= 1.8
|
|
||||||
|
|
||||||
Directory with plugins or paths point to plugins. Plugin are loaded before trying to initialize
|
|
||||||
any driver or authcontroller. If a path is a directory, it is scanned for plugins loading all files
|
|
||||||
with a is common shared library extension on the platform (`.so`, `.dylib`, `dll`).
|
|
||||||
|
|
||||||
## `storage`
|
## `storage`
|
||||||
|
|
||||||
```none
|
```none
|
||||||
|
@ -391,6 +402,17 @@ storage:
|
||||||
gcs:
|
gcs:
|
||||||
bucket: bucketname
|
bucket: bucketname
|
||||||
keyfile: /path/to/keyfile
|
keyfile: /path/to/keyfile
|
||||||
|
credentials:
|
||||||
|
type: service_account
|
||||||
|
project_id: project_id_string
|
||||||
|
private_key_id: private_key_id_string
|
||||||
|
private_key: private_key_string
|
||||||
|
client_email: client@example.com
|
||||||
|
client_id: client_id_string
|
||||||
|
auth_uri: http://example.com/auth_uri
|
||||||
|
token_uri: http://example.com/token_uri
|
||||||
|
auth_provider_x509_cert_url: http://example.com/provider_cert_url
|
||||||
|
client_x509_cert_url: http://example.com/client_cert_url
|
||||||
rootdirectory: /gcs/object/name/prefix
|
rootdirectory: /gcs/object/name/prefix
|
||||||
s3:
|
s3:
|
||||||
accesskey: awsaccesskey
|
accesskey: awsaccesskey
|
||||||
|
@ -426,7 +448,8 @@ storage:
|
||||||
endpoint: optional endpoints
|
endpoint: optional endpoints
|
||||||
internal: optional internal endpoint
|
internal: optional internal endpoint
|
||||||
bucket: OSS bucket
|
bucket: OSS bucket
|
||||||
encrypt: optional data encryption setting
|
encrypt: optional enable server-side encryption
|
||||||
|
encryptionkeyid: optional KMS key id for encryption
|
||||||
secure: optional ssl setting
|
secure: optional ssl setting
|
||||||
chunksize: optional size valye
|
chunksize: optional size valye
|
||||||
rootdirectory: optional root directory
|
rootdirectory: optional root directory
|
||||||
|
@ -571,7 +594,8 @@ The `auth` option is **optional**. Possible auth providers include:
|
||||||
|
|
||||||
- [`silly`](#silly)
|
- [`silly`](#silly)
|
||||||
- [`token`](#token)
|
- [`token`](#token)
|
||||||
- [`htpasswd`](#token)
|
- [`htpasswd`](#htpasswd)
|
||||||
|
- [`none`]
|
||||||
|
|
||||||
You can configure only one authentication provider.
|
You can configure only one authentication provider.
|
||||||
|
|
||||||
|
@ -602,6 +626,7 @@ security.
|
||||||
| `service` | yes | The service being authenticated. |
|
| `service` | yes | The service being authenticated. |
|
||||||
| `issuer` | yes | The name of the token issuer. The issuer inserts this into the token so it must match the value configured for the issuer. |
|
| `issuer` | yes | The name of the token issuer. The issuer inserts this into the token so it must match the value configured for the issuer. |
|
||||||
| `rootcertbundle` | yes | The absolute path to the root certificate bundle. This bundle contains the public part of the certificates used to sign authentication tokens. |
|
| `rootcertbundle` | yes | The absolute path to the root certificate bundle. This bundle contains the public part of the certificates used to sign authentication tokens. |
|
||||||
|
| `autoredirect` | no | When set to `true`, `realm` will automatically be set using the Host header of the request as the domain and a path of `/auth/token/`|
|
||||||
|
|
||||||
|
|
||||||
For more information about Token based authentication configuration, see the
|
For more information about Token based authentication configuration, see the
|
||||||
|
@ -617,6 +642,9 @@ The only supported password format is
|
||||||
are ignored. The `htpasswd` file is loaded once, at startup. If the file is
|
are ignored. The `htpasswd` file is loaded once, at startup. If the file is
|
||||||
invalid, the registry will display an error and will not start.
|
invalid, the registry will display an error and will not start.
|
||||||
|
|
||||||
|
> **Warning**: If the `htpasswd` file is missing, the file will be created and provisioned with a default user and automatically generated password.
|
||||||
|
> The password will be printed to stdout.
|
||||||
|
|
||||||
> **Warning**: Only use the `htpasswd` authentication scheme with TLS
|
> **Warning**: Only use the `htpasswd` authentication scheme with TLS
|
||||||
> configured, since basic authentication sends passwords as part of the HTTP
|
> configured, since basic authentication sends passwords as part of the HTTP
|
||||||
> header.
|
> header.
|
||||||
|
@ -655,6 +683,10 @@ middleware:
|
||||||
privatekey: /path/to/pem
|
privatekey: /path/to/pem
|
||||||
keypairid: cloudfrontkeypairid
|
keypairid: cloudfrontkeypairid
|
||||||
duration: 3000s
|
duration: 3000s
|
||||||
|
ipfilteredby: awsregion
|
||||||
|
awsregion: us-east-1, use-east-2
|
||||||
|
updatefrequency: 12h
|
||||||
|
iprangesurl: https://ip-ranges.amazonaws.com/ip-ranges.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Each middleware entry has `name` and `options` entries. The `name` must
|
Each middleware entry has `name` and `options` entries. The `name` must
|
||||||
|
@ -673,7 +705,31 @@ interpretation of the options.
|
||||||
| `baseurl` | yes | The `SCHEME://HOST[/PATH]` at which Cloudfront is served. |
|
| `baseurl` | yes | The `SCHEME://HOST[/PATH]` at which Cloudfront is served. |
|
||||||
| `privatekey` | yes | The private key for Cloudfront, provided by AWS. |
|
| `privatekey` | yes | The private key for Cloudfront, provided by AWS. |
|
||||||
| `keypairid` | yes | The key pair ID provided by AWS. |
|
| `keypairid` | yes | The key pair ID provided by AWS. |
|
||||||
| `duration` | no | An integer and unit for the duration of the Cloudfront session. Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, or `h`. For example, `3000s` is valid, but `3000 s` is not. If you do not specify a `duration` or you specify an integer without a time unit, the duration defaults to `20m` (20 minutes).|
|
| `duration` | no | An integer and unit for the duration of the Cloudfront session. Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, or `h`. For example, `3000s` is valid, but `3000 s` is not. If you do not specify a `duration` or you specify an integer without a time unit, the duration defaults to `20m` (20 minutes). |
|
||||||
|
| `ipfilteredby` | no | A string with the following value `none`, `aws` or `awsregion`. |
|
||||||
|
| `awsregion` | no | A comma separated string of AWS regions, only available when `ipfilteredby` is `awsregion`. For example, `us-east-1, us-west-2` |
|
||||||
|
| `updatefrequency` | no | The frequency to update AWS IP regions, default: `12h` |
|
||||||
|
| `iprangesurl` | no | The URL contains the AWS IP ranges information, default: `https://ip-ranges.amazonaws.com/ip-ranges.json` |
|
||||||
|
|
||||||
|
|
||||||
|
Value of `ipfilteredby` can be:
|
||||||
|
|
||||||
|
| Value | Description |
|
||||||
|
|-------------|------------------------------------|
|
||||||
|
| `none` | default, do not filter by IP |
|
||||||
|
| `aws` | IP from AWS goes to S3 directly |
|
||||||
|
| `awsregion` | IP from certain AWS regions goes to S3 directly, use together with `awsregion`. |
|
||||||
|
|
||||||
|
### `alicdn`
|
||||||
|
|
||||||
|
`alicdn` storage middleware allows the registry to serve layers via a content delivery network provided by Alibaba Cloud. Alicdn requires the OSS storage driver.
|
||||||
|
|
||||||
|
| Parameter | Required | Description |
|
||||||
|
|--------------|----------|-------------------------------------------------------------------------|
|
||||||
|
| `baseurl` | yes | The `SCHEME://HOST` at which Alicdn is served. |
|
||||||
|
| `authtype` | yes | The URL authentication type for Alicdn, which should be `a`, `b` or `c`. See the [Authentication configuration](https://www.alibabacloud.com/help/doc-detail/85117.htm).|
|
||||||
|
| `privatekey` | yes | The URL authentication key for Alicdn. |
|
||||||
|
| `duration` | no | An integer and unit for the duration of the Alicdn session. Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, or `h`.|
|
||||||
|
|
||||||
### `redirect`
|
### `redirect`
|
||||||
|
|
||||||
|
@ -732,15 +788,18 @@ http:
|
||||||
host: https://myregistryaddress.org:5000
|
host: https://myregistryaddress.org:5000
|
||||||
secret: asecretforlocaldevelopment
|
secret: asecretforlocaldevelopment
|
||||||
relativeurls: false
|
relativeurls: false
|
||||||
|
draintimeout: 60s
|
||||||
tls:
|
tls:
|
||||||
certificate: /path/to/x509/public
|
certificate: /path/to/x509/public
|
||||||
key: /path/to/x509/private
|
key: /path/to/x509/private
|
||||||
clientcas:
|
clientcas:
|
||||||
- /path/to/ca.pem
|
- /path/to/ca.pem
|
||||||
- /path/to/another/ca.pem
|
- /path/to/another/ca.pem
|
||||||
|
minimumtls: tls1.0
|
||||||
letsencrypt:
|
letsencrypt:
|
||||||
cachefile: /path/to/cache-file
|
cachefile: /path/to/cache-file
|
||||||
email: emailused@letsencrypt.com
|
email: emailused@letsencrypt.com
|
||||||
|
hosts: [myregistryaddress.org]
|
||||||
debug:
|
debug:
|
||||||
addr: localhost:5001
|
addr: localhost:5001
|
||||||
headers:
|
headers:
|
||||||
|
@ -760,6 +819,7 @@ registry.
|
||||||
| `host` | no | A fully-qualified URL for an externally-reachable address for the registry. If present, it is used when creating generated URLs. Otherwise, these URLs are derived from client requests. |
|
| `host` | no | A fully-qualified URL for an externally-reachable address for the registry. If present, it is used when creating generated URLs. Otherwise, these URLs are derived from client requests. |
|
||||||
| `secret` | no | A random piece of data used to sign state that may be stored with the client to protect against tampering. For production environments you should generate a random piece of data using a cryptographically secure random generator. If you omit the secret, the registry will automatically generate a secret when it starts. **If you are building a cluster of registries behind a load balancer, you MUST ensure the secret is the same for all registries.**|
|
| `secret` | no | A random piece of data used to sign state that may be stored with the client to protect against tampering. For production environments you should generate a random piece of data using a cryptographically secure random generator. If you omit the secret, the registry will automatically generate a secret when it starts. **If you are building a cluster of registries behind a load balancer, you MUST ensure the secret is the same for all registries.**|
|
||||||
| `relativeurls`| no | If `true`, the registry returns relative URLs in Location headers. The client is responsible for resolving the correct URL. **This option is not compatible with Docker 1.7 and earlier.**|
|
| `relativeurls`| no | If `true`, the registry returns relative URLs in Location headers. The client is responsible for resolving the correct URL. **This option is not compatible with Docker 1.7 and earlier.**|
|
||||||
|
| `draintimeout`| no | Amount of time to wait for HTTP connections to drain before shutting down after registry receives SIGTERM signal|
|
||||||
|
|
||||||
|
|
||||||
### `tls`
|
### `tls`
|
||||||
|
@ -774,6 +834,7 @@ and proxy connections to the registry server.
|
||||||
| `certificate` | yes | Absolute path to the x509 certificate file. |
|
| `certificate` | yes | Absolute path to the x509 certificate file. |
|
||||||
| `key` | yes | Absolute path to the x509 private key file. |
|
| `key` | yes | Absolute path to the x509 private key file. |
|
||||||
| `clientcas` | no | An array of absolute paths to x509 CA files. |
|
| `clientcas` | no | An array of absolute paths to x509 CA files. |
|
||||||
|
| `minimumtls` | no | Minimum TLS version allowed (tls1.0, tls1.1, tls1.2). Defaults to tls1.0 |
|
||||||
|
|
||||||
### `letsencrypt`
|
### `letsencrypt`
|
||||||
|
|
||||||
|
@ -785,12 +846,17 @@ TLS certificates provided by
|
||||||
> accessible on port `443`. The registry defaults to listening on port `5000`.
|
> accessible on port `443`. The registry defaults to listening on port `5000`.
|
||||||
> If you run the registry as a container, consider adding the flag `-p 443:5000`
|
> If you run the registry as a container, consider adding the flag `-p 443:5000`
|
||||||
> to the `docker run` command or using a similar setting in a cloud
|
> to the `docker run` command or using a similar setting in a cloud
|
||||||
> configuration.
|
> configuration. You should also set the `hosts` option to the list of hostnames
|
||||||
|
> that are valid for this registry to avoid trying to get certificates for random
|
||||||
|
> hostnames due to malicious clients connecting with bogus SNI hostnames. Please
|
||||||
|
> ensure that you have the `ca-certificates` package installed in order to verify
|
||||||
|
> letsencrypt certificates.
|
||||||
|
|
||||||
| Parameter | Required | Description |
|
| Parameter | Required | Description |
|
||||||
|-----------|----------|-------------------------------------------------------|
|
|-----------|----------|-------------------------------------------------------|
|
||||||
| `cachefile` | yes | Absolute path to a file where the Let's Encrypt agent can cache data. |
|
| `cachefile` | yes | Absolute path to a file where the Let's Encrypt agent can cache data. |
|
||||||
| `email` | yes | The email address used to register with Let's Encrypt. |
|
| `email` | yes | The email address used to register with Let's Encrypt. |
|
||||||
|
| `hosts` | no | The hostnames allowed for Let's Encrypt certificates. |
|
||||||
|
|
||||||
### `debug`
|
### `debug`
|
||||||
|
|
||||||
|
@ -803,6 +869,19 @@ access to the debug endpoint is locked down in a production environment.
|
||||||
The `debug` section takes a single required `addr` parameter, which specifies
|
The `debug` section takes a single required `addr` parameter, which specifies
|
||||||
the `HOST:PORT` on which the debug server should accept connections.
|
the `HOST:PORT` on which the debug server should accept connections.
|
||||||
|
|
||||||
|
## `prometheus`
|
||||||
|
|
||||||
|
The `prometheus` option defines whether the prometheus metrics is enable, as well
|
||||||
|
as the path to access the metrics.
|
||||||
|
|
||||||
|
| Parameter | Required | Description |
|
||||||
|
|-----------|----------|-------------------------------------------------------|
|
||||||
|
| `enabled` | no | Set `true` to enable the prometheus server |
|
||||||
|
| `path` | no | The path to access the metrics, `/metrics` by default |
|
||||||
|
|
||||||
|
The url to access the metrics is `HOST:PORT/path`, where `HOST:PORT` is defined
|
||||||
|
in `addr` under `debug`.
|
||||||
|
|
||||||
### `headers`
|
### `headers`
|
||||||
|
|
||||||
The `headers` option is **optional** . Use it to specify headers that the HTTP
|
The `headers` option is **optional** . Use it to specify headers that the HTTP
|
||||||
|
@ -830,16 +909,23 @@ settings for the registry.
|
||||||
|
|
||||||
```none
|
```none
|
||||||
notifications:
|
notifications:
|
||||||
|
events:
|
||||||
|
includereferences: true
|
||||||
endpoints:
|
endpoints:
|
||||||
- name: alistener
|
- name: alistener
|
||||||
disabled: false
|
disabled: false
|
||||||
url: https://my.listener.com/event
|
url: https://my.listener.com/event
|
||||||
headers: <http.Header>
|
headers: <http.Header>
|
||||||
timeout: 500
|
timeout: 1s
|
||||||
threshold: 5
|
threshold: 10
|
||||||
backoff: 1000
|
backoff: 1s
|
||||||
ignoredmediatypes:
|
ignoredmediatypes:
|
||||||
- application/octet-stream
|
- application/octet-stream
|
||||||
|
ignore:
|
||||||
|
mediatypes:
|
||||||
|
- application/octet-stream
|
||||||
|
actions:
|
||||||
|
- pull
|
||||||
```
|
```
|
||||||
|
|
||||||
The notifications option is **optional** and currently may contain a single
|
The notifications option is **optional** and currently may contain a single
|
||||||
|
@ -860,6 +946,21 @@ accept event notifications.
|
||||||
| `threshold` | yes | An integer specifying how long to wait before backing off a failure. |
|
| `threshold` | yes | An integer specifying how long to wait before backing off a failure. |
|
||||||
| `backoff` | yes | How long the system backs off before retrying after a failure. A positive integer and an optional suffix indicating the unit of time, which may be `ns`, `us`, `ms`, `s`, `m`, or `h`. If you omit the unit of time, `ns` is used. |
|
| `backoff` | yes | How long the system backs off before retrying after a failure. A positive integer and an optional suffix indicating the unit of time, which may be `ns`, `us`, `ms`, `s`, `m`, or `h`. If you omit the unit of time, `ns` is used. |
|
||||||
| `ignoredmediatypes`|no| A list of target media types to ignore. Events with these target media types are not published to the endpoint. |
|
| `ignoredmediatypes`|no| A list of target media types to ignore. Events with these target media types are not published to the endpoint. |
|
||||||
|
| `ignore` |no| Events with these mediatypes or actions are not published to the endpoint. |
|
||||||
|
|
||||||
|
#### `ignore`
|
||||||
|
| Parameter | Required | Description |
|
||||||
|
|-----------|----------|-------------------------------------------------------|
|
||||||
|
| `mediatypes`|no| A list of target media types to ignore. Events with these target media types are not published to the endpoint. |
|
||||||
|
| `actions` |no| A list of actions to ignore. Events with these actions are not published to the endpoint. |
|
||||||
|
|
||||||
|
### `events`
|
||||||
|
|
||||||
|
The `events` structure configures the information provided in event notifications.
|
||||||
|
|
||||||
|
| Parameter | Required | Description |
|
||||||
|
|-----------|----------|-------------------------------------------------------|
|
||||||
|
| `includereferences` | no | If `true`, include reference information in manifest events. |
|
||||||
|
|
||||||
## `redis`
|
## `redis`
|
||||||
|
|
||||||
|
@ -966,7 +1067,7 @@ a file.
|
||||||
| Parameter | Required | Description |
|
| Parameter | Required | Description |
|
||||||
|-----------|----------|-------------------------------------------------------|
|
|-----------|----------|-------------------------------------------------------|
|
||||||
| `file` | yes | The path to check for existence of a file. |
|
| `file` | yes | The path to check for existence of a file. |
|
||||||
| `interval`| no | How long to wait before repeating the check. A positive integer and an optional suffix indicating the unit of time. The suffix is one of `ns`, `us`, `ms`, `s`, `m`, or `h`. Defaults to `10s` if the value is omitted. |
|
| `interval`| no | How long to wait before repeating the check. A positive integer and an optional suffix indicating the unit of time. The suffix is one of `ns`, `us`, `ms`, `s`, `m`, or `h`. Defaults to `10s` if the value is omitted. If you specify a value but omit the suffix, the value is interpreted as a number of nanoseconds. |
|
||||||
|
|
||||||
### `http`
|
### `http`
|
||||||
|
|
||||||
|
@ -1031,6 +1132,7 @@ username (such as `batman`) and the password for that username.
|
||||||
compatibility:
|
compatibility:
|
||||||
schema1:
|
schema1:
|
||||||
signingkeyfile: /etc/registry/key.json
|
signingkeyfile: /etc/registry/key.json
|
||||||
|
enabled: true
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the `compatibility` structure to configure handling of older and deprecated
|
Use the `compatibility` structure to configure handling of older and deprecated
|
||||||
|
@ -1041,6 +1143,7 @@ features. Each subsection defines such a feature with configurable behavior.
|
||||||
| Parameter | Required | Description |
|
| Parameter | Required | Description |
|
||||||
|-----------|----------|-------------------------------------------------------|
|
|-----------|----------|-------------------------------------------------------|
|
||||||
| `signingkeyfile` | no | The signing private key used to add signatures to `schema1` manifests. If no signing key is provided, a new ECDSA key is generated when the registry starts. |
|
| `signingkeyfile` | no | The signing private key used to add signatures to `schema1` manifests. If no signing key is provided, a new ECDSA key is generated when the registry starts. |
|
||||||
|
| `enabled` | no | If this is not set to true, `schema1` manifests cannot be pushed. |
|
||||||
|
|
||||||
## `validation`
|
## `validation`
|
||||||
|
|
||||||
|
@ -1128,7 +1231,7 @@ middleware:
|
||||||
baseurl: http://d111111abcdef8.cloudfront.net
|
baseurl: http://d111111abcdef8.cloudfront.net
|
||||||
privatekey: /path/to/asecret.pem
|
privatekey: /path/to/asecret.pem
|
||||||
keypairid: asecret
|
keypairid: asecret
|
||||||
duration: 60
|
duration: 60s
|
||||||
```
|
```
|
||||||
|
|
||||||
See the configuration reference for [Cloudfront](#cloudfront) for more
|
See the configuration reference for [Cloudfront](#cloudfront) for more
|
||||||
|
|
192
docs/spec/api.md
192
docs/spec/api.md
|
@ -1,7 +1,9 @@
|
||||||
---
|
---
|
||||||
title: "HTTP API V2"
|
title: "HTTP API V2"
|
||||||
description: "Specification for the Registry API."
|
description: "Specification for the Registry API."
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, api, advanced"]
|
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced
|
||||||
|
redirect_from:
|
||||||
|
- /reference/api/registry_api/
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry HTTP API V2
|
# Docker Registry HTTP API V2
|
||||||
|
@ -16,7 +18,7 @@ of this API, known as _Docker Registry HTTP API V2_.
|
||||||
|
|
||||||
While the V1 registry protocol is usable, there are several problems with the
|
While the V1 registry protocol is usable, there are several problems with the
|
||||||
architecture that have led to this new version. The main driver of this
|
architecture that have led to this new version. The main driver of this
|
||||||
specification is a set of changes to the docker the image format, covered in
|
specification is a set of changes to the Docker image format, covered in
|
||||||
[docker/docker#8093](https://github.com/docker/docker/issues/8093).
|
[docker/docker#8093](https://github.com/docker/docker/issues/8093).
|
||||||
The new, self-contained image manifest simplifies image definition and improves
|
The new, self-contained image manifest simplifies image definition and improves
|
||||||
security. This specification will build on that work, leveraging new properties
|
security. This specification will build on that work, leveraging new properties
|
||||||
|
@ -676,7 +678,7 @@ the upload will not be considered complete. The format for the final chunk
|
||||||
will be as follows:
|
will be as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
PUT /v2/<name>/blob/uploads/<uuid>?digest=<digest>
|
PUT /v2/<name>/blobs/uploads/<uuid>?digest=<digest>
|
||||||
Content-Length: <size of chunk>
|
Content-Length: <size of chunk>
|
||||||
Content-Range: <start of range>-<end of range>
|
Content-Range: <start of range>-<end of range>
|
||||||
Content-Type: application/octet-stream
|
Content-Type: application/octet-stream
|
||||||
|
@ -685,7 +687,7 @@ Content-Type: application/octet-stream
|
||||||
```
|
```
|
||||||
|
|
||||||
Optionally, if all chunks have already been uploaded, a `PUT` request with a
|
Optionally, if all chunks have already been uploaded, a `PUT` request with a
|
||||||
`digest` parameter and zero-length body may be sent to complete and validated
|
`digest` parameter and zero-length body may be sent to complete and validate
|
||||||
the upload. Multiple "digest" parameters may be provided with different
|
the upload. Multiple "digest" parameters may be provided with different
|
||||||
digests. The server may verify none or all of them but _must_ notify the
|
digests. The server may verify none or all of them but _must_ notify the
|
||||||
client if the content is rejected.
|
client if the content is rejected.
|
||||||
|
@ -1206,7 +1208,7 @@ The registry does not implement the V2 API.
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1244,7 +1246,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1316,7 +1318,7 @@ The following parameters should be specified on the request:
|
||||||
```
|
```
|
||||||
200 OK
|
200 OK
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": <name>,
|
"name": <name>,
|
||||||
|
@ -1344,7 +1346,7 @@ The following headers will be returned with the response:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1382,7 +1384,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1419,7 +1421,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1456,7 +1458,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1514,7 +1516,7 @@ The following parameters should be specified on the request:
|
||||||
200 OK
|
200 OK
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Link: <<url>?n=<last n value>&last=<last entry from response>>; rel="next"
|
Link: <<url>?n=<last n value>&last=<last entry from response>>; rel="next"
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": <name>,
|
"name": <name>,
|
||||||
|
@ -1543,7 +1545,7 @@ The following headers will be returned with the response:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1581,7 +1583,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1618,7 +1620,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1655,7 +1657,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1759,7 +1761,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1792,7 +1794,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1830,7 +1832,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1867,7 +1869,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -1904,7 +1906,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2005,7 +2007,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2041,7 +2043,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2079,7 +2081,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2116,7 +2118,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2153,7 +2155,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2189,7 +2191,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [{
|
"errors:" [{
|
||||||
|
@ -2277,7 +2279,7 @@ The following parameters should be specified on the request:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2310,7 +2312,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2348,7 +2350,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2385,7 +2387,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2422,7 +2424,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2458,7 +2460,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2583,7 +2585,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2614,7 +2616,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2647,7 +2649,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2685,7 +2687,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2722,7 +2724,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2759,7 +2761,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2843,7 +2845,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2874,7 +2876,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2917,7 +2919,7 @@ The range specification cannot be satisfied for the requested content. This can
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2955,7 +2957,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -2992,7 +2994,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3029,7 +3031,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3132,7 +3134,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3163,7 +3165,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
405 Method Not Allowed
|
405 Method Not Allowed
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3195,7 +3197,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3233,7 +3235,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3270,7 +3272,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3307,7 +3309,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3445,7 +3447,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3483,7 +3485,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3520,7 +3522,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3557,7 +3559,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3662,7 +3664,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3700,7 +3702,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3737,7 +3739,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3774,7 +3776,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3897,7 +3899,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3935,7 +3937,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -3972,7 +3974,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4009,7 +4011,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4102,7 +4104,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4134,7 +4136,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4166,7 +4168,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4204,7 +4206,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4241,7 +4243,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4278,7 +4280,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4370,7 +4372,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4402,7 +4404,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4434,7 +4436,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4472,7 +4474,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4509,7 +4511,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4546,7 +4548,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4636,7 +4638,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4668,7 +4670,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4710,7 +4712,7 @@ The `Content-Range` specification cannot be accepted, either because it does not
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4748,7 +4750,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4785,7 +4787,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4822,7 +4824,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4916,7 +4918,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4949,7 +4951,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -4981,7 +4983,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5019,7 +5021,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5056,7 +5058,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5093,7 +5095,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5177,7 +5179,7 @@ The following headers will be returned with the response:
|
||||||
|
|
||||||
```
|
```
|
||||||
400 Bad Request
|
400 Bad Request
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5208,7 +5210,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
|
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5240,7 +5242,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
401 Unauthorized
|
401 Unauthorized
|
||||||
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
WWW-Authenticate: <scheme> realm="<realm>", ..."
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5278,7 +5280,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
404 Not Found
|
404 Not Found
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5315,7 +5317,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
403 Forbidden
|
403 Forbidden
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5352,7 +5354,7 @@ The error codes that may be included in the response body are enumerated below:
|
||||||
```
|
```
|
||||||
429 Too Many Requests
|
429 Too Many Requests
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"errors:" [
|
"errors:" [
|
||||||
|
@ -5414,7 +5416,7 @@ Request an unabridged list of repositories available. The implementation may im
|
||||||
```
|
```
|
||||||
200 OK
|
200 OK
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
@ -5459,7 +5461,7 @@ The following parameters should be specified on the request:
|
||||||
200 OK
|
200 OK
|
||||||
Content-Length: <length>
|
Content-Length: <length>
|
||||||
Link: <<url>?n=<last n value>&last=<last entry from response>>; rel="next"
|
Link: <<url>?n=<last n value>&last=<last entry from response>>; rel="next"
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
---
|
---
|
||||||
title: "HTTP API V2"
|
title: "HTTP API V2"
|
||||||
description: "Specification for the Registry API."
|
description: "Specification for the Registry API."
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, api, advanced"]
|
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced
|
||||||
|
redirect_from:
|
||||||
|
- /reference/api/registry_api/
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry HTTP API V2
|
# Docker Registry HTTP API V2
|
||||||
|
@ -16,7 +18,7 @@ of this API, known as _Docker Registry HTTP API V2_.
|
||||||
|
|
||||||
While the V1 registry protocol is usable, there are several problems with the
|
While the V1 registry protocol is usable, there are several problems with the
|
||||||
architecture that have led to this new version. The main driver of this
|
architecture that have led to this new version. The main driver of this
|
||||||
specification is a set of changes to the docker the image format, covered in
|
specification is a set of changes to the Docker image format, covered in
|
||||||
[docker/docker#8093](https://github.com/docker/docker/issues/8093).
|
[docker/docker#8093](https://github.com/docker/docker/issues/8093).
|
||||||
The new, self-contained image manifest simplifies image definition and improves
|
The new, self-contained image manifest simplifies image definition and improves
|
||||||
security. This specification will build on that work, leveraging new properties
|
security. This specification will build on that work, leveraging new properties
|
||||||
|
@ -676,7 +678,7 @@ the upload will not be considered complete. The format for the final chunk
|
||||||
will be as follows:
|
will be as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
PUT /v2/<name>/blob/uploads/<uuid>?digest=<digest>
|
PUT /v2/<name>/blobs/uploads/<uuid>?digest=<digest>
|
||||||
Content-Length: <size of chunk>
|
Content-Length: <size of chunk>
|
||||||
Content-Range: <start of range>-<end of range>
|
Content-Range: <start of range>-<end of range>
|
||||||
Content-Type: application/octet-stream
|
Content-Type: application/octet-stream
|
||||||
|
@ -685,7 +687,7 @@ Content-Type: application/octet-stream
|
||||||
```
|
```
|
||||||
|
|
||||||
Optionally, if all chunks have already been uploaded, a `PUT` request with a
|
Optionally, if all chunks have already been uploaded, a `PUT` request with a
|
||||||
`digest` parameter and zero-length body may be sent to complete and validated
|
`digest` parameter and zero-length body may be sent to complete and validate
|
||||||
the upload. Multiple "digest" parameters may be provided with different
|
the upload. Multiple "digest" parameters may be provided with different
|
||||||
digests. The server may verify none or all of them but _must_ notify the
|
digests. The server may verify none or all of them but _must_ notify the
|
||||||
client if the content is rejected.
|
client if the content is rejected.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Docker Registry Token Authentication"
|
title: "Docker Registry Token Authentication"
|
||||||
description: "Docker Registry v2 authentication schema"
|
description: "Docker Registry v2 authentication schema"
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, authentication, advanced"]
|
keywords: registry, on-prem, images, tags, repository, distribution, authentication, advanced
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry v2 authentication
|
# Docker Registry v2 authentication
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Token Authentication Implementation"
|
title: "Token Authentication Implementation"
|
||||||
description: "Describe the reference implementation of the Docker Registry v2 authentication schema"
|
description: "Describe the reference implementation of the Docker Registry v2 authentication schema"
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, JWT authentication, advanced"]
|
keywords: registry, on-prem, images, tags, repository, distribution, JWT authentication, advanced
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry v2 Bearer token specification
|
# Docker Registry v2 Bearer token specification
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Oauth2 Token Authentication"
|
title: "Oauth2 Token Authentication"
|
||||||
description: "Specifies the Docker Registry v2 authentication"
|
description: "Specifies the Docker Registry v2 authentication"
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, oauth2, advanced"]
|
keywords: registry, on-prem, images, tags, repository, distribution, oauth2, advanced
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry v2 authentication using OAuth2
|
# Docker Registry v2 authentication using OAuth2
|
||||||
|
@ -39,7 +39,7 @@ Content-Type: application/x-www-form-urlencoded
|
||||||
<dd>
|
<dd>
|
||||||
(REQUIRED) Type of grant used to get token. When getting a refresh token
|
(REQUIRED) Type of grant used to get token. When getting a refresh token
|
||||||
using credentials this type should be set to "password" and have the
|
using credentials this type should be set to "password" and have the
|
||||||
accompanying username and password paramters. Type "authorization_code"
|
accompanying username and password parameters. Type "authorization_code"
|
||||||
is reserved for future use for authenticating to an authorization server
|
is reserved for future use for authenticating to an authorization server
|
||||||
without having to send credentials directly from the client. When
|
without having to send credentials directly from the client. When
|
||||||
requesting an access token with a refresh token this should be set to
|
requesting an access token with a refresh token this should be set to
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Token Scope Documentation"
|
title: "Token Scope Documentation"
|
||||||
description: "Describes the scope and access fields used for registry authorization tokens"
|
description: "Describes the scope and access fields used for registry authorization tokens"
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, advanced, access, scope"]
|
keywords: registry, on-prem, images, tags, repository, distribution, advanced, access, scope
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry Token Scope and Access
|
# Docker Registry Token Scope and Access
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
---
|
---
|
||||||
title: "Token Authentication Specification"
|
title: "Token Authentication Specification"
|
||||||
description: "Specifies the Docker Registry v2 authentication"
|
description: "Specifies the Docker Registry v2 authentication"
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, Bearer authentication, advanced"]
|
keywords: registry, on-prem, images, tags, repository, distribution, Bearer authentication, advanced
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry v2 authentication via central service
|
# Docker Registry v2 authentication via central service
|
||||||
|
|
||||||
This document outlines the v2 Docker registry authentication scheme:
|
This document outlines the v2 Docker registry authentication scheme:
|
||||||
|
|
||||||
![v2 registry auth](../../images/v2-registry-auth.png)
|
![v2 registry auth](../images/v2-registry-auth.png)
|
||||||
|
|
||||||
1. Attempt to begin a push/pull operation with the registry.
|
1. Attempt to begin a push/pull operation with the registry.
|
||||||
2. If the registry requires authorization it will return a `401 Unauthorized`
|
2. If the registry requires authorization it will return a `401 Unauthorized`
|
||||||
|
@ -60,7 +60,7 @@ return this response:
|
||||||
|
|
||||||
```
|
```
|
||||||
HTTP/1.1 401 Unauthorized
|
HTTP/1.1 401 Unauthorized
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json
|
||||||
Docker-Distribution-Api-Version: registry/2.0
|
Docker-Distribution-Api-Version: registry/2.0
|
||||||
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"
|
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"
|
||||||
Date: Thu, 10 Sep 2015 19:32:31 GMT
|
Date: Thu, 10 Sep 2015 19:32:31 GMT
|
||||||
|
|
41
docs/spec/deprecated-schema-v1.md
Normal file
41
docs/spec/deprecated-schema-v1.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
title: Update deprecated schema image manifest version 2, v1 images
|
||||||
|
description: Update deprecated schema v1 iamges
|
||||||
|
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced, manifest
|
||||||
|
---
|
||||||
|
|
||||||
|
## Image manifest version 2, schema 1
|
||||||
|
With the release of image manifest version 2, schema 2, image manifest version
|
||||||
|
2, schema 1 has been deprecated. This could lead to compatibility and
|
||||||
|
vulnerability issues in images that haven't been updated to image manifest
|
||||||
|
version 2, schema 2.
|
||||||
|
|
||||||
|
This page contains information on how to update from image manifest version 2,
|
||||||
|
schema 1. However, these instructions will not ensure your new image will run
|
||||||
|
successfully. There may be several other issues to troubleshoot that are
|
||||||
|
associated with the deprecated image manifest that will block your image from
|
||||||
|
running succesfully. A list of possible methods to help update your image is
|
||||||
|
also included below.
|
||||||
|
|
||||||
|
### Update to image manifest version 2, schema 2
|
||||||
|
|
||||||
|
One way to upgrade an image from image manifest version 2, schema 1 to
|
||||||
|
schema 2 is to `docker pull` the image and then `docker push` the image with a
|
||||||
|
current version of Docker. Doing so will automatically convert the image to use
|
||||||
|
the latest image manifest specification.
|
||||||
|
|
||||||
|
Converting an image to image manifest version 2, schema 2 converts the
|
||||||
|
manifest format, but does not update the contents within the image. Images
|
||||||
|
using manifest version 2, schema 1 may contain unpatched vulnerabilities. We
|
||||||
|
recommend looking for an alternative image or rebuilding it.
|
||||||
|
|
||||||
|
|
||||||
|
### Update FROM statement
|
||||||
|
|
||||||
|
You can rebuild the image by updating the `FROM` statement in your
|
||||||
|
`Dockerfile`. If your image manifest is out-of-date, there is a chance the
|
||||||
|
image pulled from your `FROM` statement in your `Dockerfile` is also
|
||||||
|
out-of-date. See the [Dockerfile reference](https://docs.docker.com/engine/reference/builder/#from)
|
||||||
|
and the [Dockerfile best practices guide](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
|
||||||
|
for more information on how to update the `FROM` statement in your
|
||||||
|
`Dockerfile`.
|
BIN
docs/spec/images/v2-registry-auth.png
Normal file
BIN
docs/spec/images/v2-registry-auth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -1,12 +1,12 @@
|
||||||
---
|
---
|
||||||
title: "Reference Overview"
|
title: "Reference Overview"
|
||||||
description: "Explains registry JSON objects"
|
description: "Explains registry JSON objects"
|
||||||
keywords: ["registry, service, images, repository, json"]
|
keywords: registry, service, images, repository, json
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docker Registry Reference
|
# Docker Registry Reference
|
||||||
|
|
||||||
* [HTTP API V2](api.md)
|
* [HTTP API V2](api.md)
|
||||||
* [Storage Driver](../storage-drivers/index.md)
|
* [Storage Driver](https://docs.docker.com/registry/storage-drivers/)
|
||||||
* [Token Authentication Specification](auth/token.md)
|
* [Token Authentication Specification](auth/token.md)
|
||||||
* [Token Authentication Implementation](auth/jwt.md)
|
* [Token Authentication Implementation](auth/jwt.md)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Image Manifest V 2, Schema 1 "
|
title: "Image Manifest V 2, Schema 1 "
|
||||||
description: "image manifest for the Registry."
|
description: "image manifest for the Registry."
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, api, advanced, manifest"]
|
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced, manifest
|
||||||
---
|
---
|
||||||
|
|
||||||
# Image Manifest Version 2, Schema 1
|
# Image Manifest Version 2, Schema 1
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Image Manifest V 2, Schema 2 "
|
title: "Image Manifest V 2, Schema 2 "
|
||||||
description: "image manifest for the Registry."
|
description: "image manifest for the Registry."
|
||||||
keywords: ["registry, on-prem, images, tags, repository, distribution, api, advanced, manifest"]
|
keywords: registry, on-prem, images, tags, repository, distribution, api, advanced, manifest
|
||||||
---
|
---
|
||||||
|
|
||||||
# Image Manifest Version 2, Schema 2
|
# Image Manifest Version 2, Schema 2
|
||||||
|
@ -60,8 +60,8 @@ image manifest based on the Content-Type returned in the HTTP response.
|
||||||
- **`mediaType`** *string*
|
- **`mediaType`** *string*
|
||||||
|
|
||||||
The MIME type of the referenced object. This will generally be
|
The MIME type of the referenced object. This will generally be
|
||||||
`application/vnd.docker.image.manifest.v2+json`, but it could also
|
`application/vnd.docker.distribution.manifest.v2+json`, but it could also
|
||||||
be `application/vnd.docker.image.manifest.v1+json` if the manifest
|
be `application/vnd.docker.distribution.manifest.v1+json` if the manifest
|
||||||
list references a legacy schema-1 manifest.
|
list references a legacy schema-1 manifest.
|
||||||
|
|
||||||
- **`size`** *int*
|
- **`size`** *int*
|
||||||
|
@ -123,7 +123,7 @@ image manifest based on the Content-Type returned in the HTTP response.
|
||||||
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
|
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
|
||||||
"manifests": [
|
"manifests": [
|
||||||
{
|
{
|
||||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||||
"size": 7143,
|
"size": 7143,
|
||||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||||||
"platform": {
|
"platform": {
|
||||||
|
@ -132,7 +132,7 @@ image manifest based on the Content-Type returned in the HTTP response.
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||||
"size": 7682,
|
"size": 7682,
|
||||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||||
"platform": {
|
"platform": {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Reference"
|
title: "Reference"
|
||||||
description: "Explains registry JSON objects"
|
description: "Explains registry JSON objects"
|
||||||
keywords: ["registry, service, images, repository, json"]
|
keywords: registry, service, images, repository, json
|
||||||
type: "menu"
|
type: "menu"
|
||||||
identifier: "smn_registry_ref"
|
identifier: "smn_registry_ref"
|
||||||
---
|
---
|
||||||
|
|
|
@ -20,6 +20,10 @@ var ErrManifestNotModified = errors.New("manifest not modified")
|
||||||
// performed
|
// performed
|
||||||
var ErrUnsupported = errors.New("operation unsupported")
|
var ErrUnsupported = errors.New("operation unsupported")
|
||||||
|
|
||||||
|
// ErrSchemaV1Unsupported is returned when a client tries to upload a schema v1
|
||||||
|
// manifest but the registry is configured to reject it
|
||||||
|
var ErrSchemaV1Unsupported = errors.New("manifest schema v1 unsupported")
|
||||||
|
|
||||||
// ErrTagUnknown is returned if the given tag is not known by the tag service
|
// ErrTagUnknown is returned if the given tag is not known by the tag service
|
||||||
type ErrTagUnknown struct {
|
type ErrTagUnknown struct {
|
||||||
Tag string
|
Tag string
|
||||||
|
@ -77,7 +81,7 @@ func (err ErrManifestUnknownRevision) Error() string {
|
||||||
type ErrManifestUnverified struct{}
|
type ErrManifestUnverified struct{}
|
||||||
|
|
||||||
func (ErrManifestUnverified) Error() string {
|
func (ErrManifestUnverified) Error() string {
|
||||||
return fmt.Sprintf("unverified manifest")
|
return "unverified manifest"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrManifestVerification provides a type to collect errors encountered
|
// ErrManifestVerification provides a type to collect errors encountered
|
||||||
|
|
47
go.mod
Normal file
47
go.mod
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
module github.com/docker/distribution
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible
|
||||||
|
github.com/Azure/go-autorest v10.8.1+incompatible // indirect
|
||||||
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d
|
||||||
|
github.com/aws/aws-sdk-go v1.34.9
|
||||||
|
github.com/bitly/go-simplejson v0.5.0 // indirect
|
||||||
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
||||||
|
github.com/bshuster-repo/logrus-logstash-hook v0.4.1
|
||||||
|
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
|
||||||
|
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b // indirect
|
||||||
|
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 // indirect
|
||||||
|
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba
|
||||||
|
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c // indirect
|
||||||
|
github.com/dnaeon/go-vcr v1.0.1 // indirect
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c
|
||||||
|
github.com/docker/go-metrics v0.0.1
|
||||||
|
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
|
||||||
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
|
||||||
|
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33
|
||||||
|
github.com/gorilla/mux v1.7.2
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
|
github.com/marstr/guid v1.1.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2
|
||||||
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f // indirect
|
||||||
|
github.com/ncw/swift v1.0.47
|
||||||
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
|
github.com/opencontainers/image-spec v1.0.1
|
||||||
|
github.com/satori/go.uuid v1.2.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.6.0
|
||||||
|
github.com/spf13/cobra v0.0.3
|
||||||
|
github.com/spf13/pflag v1.0.3 // indirect
|
||||||
|
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 // indirect
|
||||||
|
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
|
||||||
|
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||||
|
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff
|
||||||
|
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8
|
||||||
|
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
|
||||||
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
|
)
|
176
go.sum
Normal file
176
go.sum
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I=
|
||||||
|
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
|
github.com/Azure/go-autorest v10.8.1+incompatible h1:u0jVQf+a6k6x8A+sT60l6EY9XZu+kHdnZVPAYqpVRo0=
|
||||||
|
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||||
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/aws/aws-sdk-go v1.34.9 h1:cUGBW9CVdi0mS7K1hDzxIqTpfeWhpoQiguq81M1tjK0=
|
||||||
|
github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||||
|
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||||
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||||
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
|
github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA=
|
||||||
|
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||||
|
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
|
||||||
|
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||||
|
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
|
||||||
|
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||||
|
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||||
|
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4=
|
||||||
|
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||||
|
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c h1:KJAnOBuY9cTKVqB5cfbynpvFgeHRTREkRk8C977oFu4=
|
||||||
|
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
|
||||||
|
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||||
|
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||||
|
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||||
|
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
|
||||||
|
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||||
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko=
|
||||||
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo=
|
||||||
|
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||||
|
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
|
||||||
|
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
|
||||||
|
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI=
|
||||||
|
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ=
|
||||||
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/ncw/swift v1.0.47 h1:4DQRPj35Y41WogBxyhOXlrI37nzGlyEcsforeudyYPQ=
|
||||||
|
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||||
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
|
||||||
|
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
|
||||||
|
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||||
|
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||||
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
|
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||||
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
|
||||||
|
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||||
|
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
|
||||||
|
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||||
|
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
|
||||||
|
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
||||||
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
|
||||||
|
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff h1:mk5zS3XLqVUzdF/CQCZ5ERujSF/8JFo+Wpkp/5I93NA=
|
||||||
|
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 h1:Cpp2P6TPjujNoC5M2KHY6g7wfyLYfIWRZaSdIKfDasA=
|
||||||
|
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
||||||
|
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a h1:zo0EaRwJM6T5UQ+QEt2dDSgEmbFJ4pZr/Rzsjpu7zgI=
|
||||||
|
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789 h1:NMiUjDZiD6qDVeBOzpImftxXzQHCp2Y2QLdmaqU9MRk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -14,7 +14,7 @@ var (
|
||||||
// DownHandler registers a manual_http_status that always returns an Error
|
// DownHandler registers a manual_http_status that always returns an Error
|
||||||
func DownHandler(w http.ResponseWriter, r *http.Request) {
|
func DownHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
updater.Update(errors.New("Manual Check"))
|
updater.Update(errors.New("manual Check"))
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ func RegisterFunc(name string, check func() error) {
|
||||||
// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
|
// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
|
||||||
// from an arbitrary func() error.
|
// from an arbitrary func() error.
|
||||||
func (registry *Registry) RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
|
func (registry *Registry) RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
|
||||||
registry.Register(name, PeriodicChecker(CheckFunc(check), period))
|
registry.Register(name, PeriodicChecker(check, period))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
|
// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
|
||||||
|
@ -227,7 +227,7 @@ func RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
|
||||||
// RegisterPeriodicThresholdFunc allows the convenience of registering a
|
// RegisterPeriodicThresholdFunc allows the convenience of registering a
|
||||||
// PeriodicChecker from an arbitrary func() error.
|
// PeriodicChecker from an arbitrary func() error.
|
||||||
func (registry *Registry) RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
|
func (registry *Registry) RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
|
||||||
registry.Register(name, PeriodicThresholdChecker(CheckFunc(check), period, threshold))
|
registry.Register(name, PeriodicThresholdChecker(check, period, threshold))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterPeriodicThresholdFunc allows the convenience of registering a
|
// RegisterPeriodicThresholdFunc allows the convenience of registering a
|
||||||
|
@ -291,7 +291,7 @@ func statusResponse(w http.ResponseWriter, r *http.Request, status int, checks m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Header().Set("Content-Length", fmt.Sprint(len(p)))
|
w.Header().Set("Content-Length", fmt.Sprint(len(p)))
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
if _, err := w.Write(p); err != nil {
|
if _, err := w.Write(p); err != nil {
|
||||||
|
|
|
@ -25,8 +25,8 @@ func TestReturns200IfThereAreNoChecks(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestReturns500IfThereAreErrorChecks ensures that the result code of the
|
// TestReturns503IfThereAreErrorChecks ensures that the result code of the
|
||||||
// health endpoint is 500 if there are health checks with errors
|
// health endpoint is 503 if there are health checks with errors.
|
||||||
func TestReturns503IfThereAreErrorChecks(t *testing.T) {
|
func TestReturns503IfThereAreErrorChecks(t *testing.T) {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,13 @@ import (
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/manifest"
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MediaTypeManifestList specifies the mediaType for manifest lists.
|
const (
|
||||||
const MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json"
|
// MediaTypeManifestList specifies the mediaType for manifest lists.
|
||||||
|
MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json"
|
||||||
|
)
|
||||||
|
|
||||||
// SchemaVersion provides a pre-initialized version structure for this
|
// SchemaVersion provides a pre-initialized version structure for this
|
||||||
// packages version of the manifest.
|
// packages version of the manifest.
|
||||||
|
@ -20,6 +23,13 @@ var SchemaVersion = manifest.Versioned{
|
||||||
MediaType: MediaTypeManifestList,
|
MediaType: MediaTypeManifestList,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OCISchemaVersion provides a pre-initialized version structure for this
|
||||||
|
// packages OCIschema version of the manifest.
|
||||||
|
var OCISchemaVersion = manifest.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: v1.MediaTypeImageIndex,
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
|
manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
|
||||||
m := new(DeserializedManifestList)
|
m := new(DeserializedManifestList)
|
||||||
|
@ -28,6 +38,13 @@ func init() {
|
||||||
return nil, distribution.Descriptor{}, err
|
return nil, distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.MediaType != MediaTypeManifestList {
|
||||||
|
err = fmt.Errorf("mediaType in manifest list should be '%s' not '%s'",
|
||||||
|
MediaTypeManifestList, m.MediaType)
|
||||||
|
|
||||||
|
return nil, distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
dgst := digest.FromBytes(b)
|
dgst := digest.FromBytes(b)
|
||||||
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err
|
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err
|
||||||
}
|
}
|
||||||
|
@ -35,6 +52,28 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Unable to register manifest: %s", err))
|
panic(fmt.Sprintf("Unable to register manifest: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageIndexFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
|
||||||
|
m := new(DeserializedManifestList)
|
||||||
|
err := m.UnmarshalJSON(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.MediaType != "" && m.MediaType != v1.MediaTypeImageIndex {
|
||||||
|
err = fmt.Errorf("if present, mediaType in image index should be '%s' not '%s'",
|
||||||
|
v1.MediaTypeImageIndex, m.MediaType)
|
||||||
|
|
||||||
|
return nil, distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst := digest.FromBytes(b)
|
||||||
|
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageIndex}, err
|
||||||
|
}
|
||||||
|
err = distribution.RegisterManifestSchema(v1.MediaTypeImageIndex, imageIndexFunc)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to register OCI Image Index: %s", err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlatformSpec specifies a platform where a particular image manifest is
|
// PlatformSpec specifies a platform where a particular image manifest is
|
||||||
|
@ -81,7 +120,7 @@ type ManifestList struct {
|
||||||
Manifests []ManifestDescriptor `json:"manifests"`
|
Manifests []ManifestDescriptor `json:"manifests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// References returnes the distribution descriptors for the referenced image
|
// References returns the distribution descriptors for the referenced image
|
||||||
// manifests.
|
// manifests.
|
||||||
func (m ManifestList) References() []distribution.Descriptor {
|
func (m ManifestList) References() []distribution.Descriptor {
|
||||||
dependencies := make([]distribution.Descriptor, len(m.Manifests))
|
dependencies := make([]distribution.Descriptor, len(m.Manifests))
|
||||||
|
@ -105,11 +144,26 @@ type DeserializedManifestList struct {
|
||||||
// DeserializedManifestList which contains the resulting manifest list
|
// DeserializedManifestList which contains the resulting manifest list
|
||||||
// and its JSON representation.
|
// and its JSON representation.
|
||||||
func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
|
func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
|
||||||
m := ManifestList{
|
var mediaType string
|
||||||
Versioned: SchemaVersion,
|
if len(descriptors) > 0 && descriptors[0].Descriptor.MediaType == v1.MediaTypeImageManifest {
|
||||||
|
mediaType = v1.MediaTypeImageIndex
|
||||||
|
} else {
|
||||||
|
mediaType = MediaTypeManifestList
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Manifests = make([]ManifestDescriptor, len(descriptors), len(descriptors))
|
return FromDescriptorsWithMediaType(descriptors, mediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromDescriptorsWithMediaType is for testing purposes, it's useful to be able to specify the media type explicitly
|
||||||
|
func FromDescriptorsWithMediaType(descriptors []ManifestDescriptor, mediaType string) (*DeserializedManifestList, error) {
|
||||||
|
m := ManifestList{
|
||||||
|
Versioned: manifest.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: mediaType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Manifests = make([]ManifestDescriptor, len(descriptors))
|
||||||
copy(m.Manifests, descriptors)
|
copy(m.Manifests, descriptors)
|
||||||
|
|
||||||
deserialized := DeserializedManifestList{
|
deserialized := DeserializedManifestList{
|
||||||
|
@ -123,7 +177,7 @@ func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestLis
|
||||||
|
|
||||||
// UnmarshalJSON populates a new ManifestList struct from JSON data.
|
// UnmarshalJSON populates a new ManifestList struct from JSON data.
|
||||||
func (m *DeserializedManifestList) UnmarshalJSON(b []byte) error {
|
func (m *DeserializedManifestList) UnmarshalJSON(b []byte) error {
|
||||||
m.canonical = make([]byte, len(b), len(b))
|
m.canonical = make([]byte, len(b))
|
||||||
// store manifest list in canonical
|
// store manifest list in canonical
|
||||||
copy(m.canonical, b)
|
copy(m.canonical, b)
|
||||||
|
|
||||||
|
@ -151,5 +205,12 @@ func (m *DeserializedManifestList) MarshalJSON() ([]byte, error) {
|
||||||
// Payload returns the raw content of the manifest list. The contents can be
|
// Payload returns the raw content of the manifest list. The contents can be
|
||||||
// used to calculate the content identifier.
|
// used to calculate the content identifier.
|
||||||
func (m DeserializedManifestList) Payload() (string, []byte, error) {
|
func (m DeserializedManifestList) Payload() (string, []byte, error) {
|
||||||
return m.MediaType, m.canonical, nil
|
var mediaType string
|
||||||
|
if m.MediaType == "" {
|
||||||
|
mediaType = v1.MediaTypeImageIndex
|
||||||
|
} else {
|
||||||
|
mediaType = m.MediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
return mediaType, m.canonical, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var expectedManifestListSerialization = []byte(`{
|
var expectedManifestListSerialization = []byte(`{
|
||||||
|
@ -37,7 +38,7 @@ var expectedManifestListSerialization = []byte(`{
|
||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
func TestManifestList(t *testing.T) {
|
func makeTestManifestList(t *testing.T, mediaType string) ([]ManifestDescriptor, *DeserializedManifestList) {
|
||||||
manifestDescriptors := []ManifestDescriptor{
|
manifestDescriptors := []ManifestDescriptor{
|
||||||
{
|
{
|
||||||
Descriptor: distribution.Descriptor{
|
Descriptor: distribution.Descriptor{
|
||||||
|
@ -64,12 +65,17 @@ func TestManifestList(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialized, err := FromDescriptors(manifestDescriptors)
|
deserialized, err := FromDescriptorsWithMediaType(manifestDescriptors, mediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating DeserializedManifestList: %v", err)
|
t.Fatalf("error creating DeserializedManifestList: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaType, canonical, err := deserialized.Payload()
|
return manifestDescriptors, deserialized
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManifestList(t *testing.T) {
|
||||||
|
manifestDescriptors, deserialized := makeTestManifestList(t, MediaTypeManifestList)
|
||||||
|
mediaType, canonical, _ := deserialized.Payload()
|
||||||
|
|
||||||
if mediaType != MediaTypeManifestList {
|
if mediaType != MediaTypeManifestList {
|
||||||
t.Fatalf("unexpected media type: %s", mediaType)
|
t.Fatalf("unexpected media type: %s", mediaType)
|
||||||
|
@ -109,3 +115,191 @@ func TestManifestList(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (mikebrow): add annotations on the manifest list (index) and support for
|
||||||
|
// empty platform structs (move to Platform *Platform `json:"platform,omitempty"`
|
||||||
|
// from current Platform PlatformSpec `json:"platform"`) in the manifest descriptor.
|
||||||
|
// Requires changes to docker/distribution/manifest/manifestlist.ManifestList and .ManifestDescriptor
|
||||||
|
// and associated serialization APIs in manifestlist.go. Or split the OCI index and
|
||||||
|
// docker manifest list implementations, which would require a lot of refactoring.
|
||||||
|
var expectedOCIImageIndexSerialization = []byte(`{
|
||||||
|
"schemaVersion": 2,
|
||||||
|
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||||
|
"manifests": [
|
||||||
|
{
|
||||||
|
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
"size": 985,
|
||||||
|
"digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
"platform": {
|
||||||
|
"architecture": "amd64",
|
||||||
|
"os": "linux",
|
||||||
|
"features": [
|
||||||
|
"sse4"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
"size": 985,
|
||||||
|
"digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
"annotations": {
|
||||||
|
"platform": "none"
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"architecture": "",
|
||||||
|
"os": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
"size": 2392,
|
||||||
|
"digest": "sha256:6346340964309634683409684360934680934608934608934608934068934608",
|
||||||
|
"annotations": {
|
||||||
|
"what": "for"
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"architecture": "sun4m",
|
||||||
|
"os": "sunos"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
func makeTestOCIImageIndex(t *testing.T, mediaType string) ([]ManifestDescriptor, *DeserializedManifestList) {
|
||||||
|
manifestDescriptors := []ManifestDescriptor{
|
||||||
|
{
|
||||||
|
Descriptor: distribution.Descriptor{
|
||||||
|
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
Size: 985,
|
||||||
|
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
},
|
||||||
|
Platform: PlatformSpec{
|
||||||
|
Architecture: "amd64",
|
||||||
|
OS: "linux",
|
||||||
|
Features: []string{"sse4"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Descriptor: distribution.Descriptor{
|
||||||
|
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
Size: 985,
|
||||||
|
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
Annotations: map[string]string{"platform": "none"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Descriptor: distribution.Descriptor{
|
||||||
|
Digest: "sha256:6346340964309634683409684360934680934608934608934608934068934608",
|
||||||
|
Size: 2392,
|
||||||
|
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
Annotations: map[string]string{"what": "for"},
|
||||||
|
},
|
||||||
|
Platform: PlatformSpec{
|
||||||
|
Architecture: "sun4m",
|
||||||
|
OS: "sunos",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialized, err := FromDescriptorsWithMediaType(manifestDescriptors, mediaType)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating DeserializedManifestList: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifestDescriptors, deserialized
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOCIImageIndex(t *testing.T) {
|
||||||
|
manifestDescriptors, deserialized := makeTestOCIImageIndex(t, v1.MediaTypeImageIndex)
|
||||||
|
|
||||||
|
mediaType, canonical, _ := deserialized.Payload()
|
||||||
|
|
||||||
|
if mediaType != v1.MediaTypeImageIndex {
|
||||||
|
t.Fatalf("unexpected media type: %s", mediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the canonical field is the same as json.MarshalIndent
|
||||||
|
// with these parameters.
|
||||||
|
p, err := json.MarshalIndent(&deserialized.ManifestList, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error marshaling manifest list: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(p, canonical) {
|
||||||
|
t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the canonical field has the expected value.
|
||||||
|
if !bytes.Equal(expectedOCIImageIndexSerialization, canonical) {
|
||||||
|
t.Fatalf("manifest bytes not equal to expected: %q != %q", string(canonical), string(expectedOCIImageIndexSerialization))
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmarshalled DeserializedManifestList
|
||||||
|
if err := json.Unmarshal(deserialized.canonical, &unmarshalled); err != nil {
|
||||||
|
t.Fatalf("error unmarshaling manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(&unmarshalled, deserialized) {
|
||||||
|
t.Fatalf("manifests are different after unmarshaling: %v != %v", unmarshalled, *deserialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
references := deserialized.References()
|
||||||
|
if len(references) != 3 {
|
||||||
|
t.Fatalf("unexpected number of references: %d", len(references))
|
||||||
|
}
|
||||||
|
for i := range references {
|
||||||
|
if !reflect.DeepEqual(references[i], manifestDescriptors[i].Descriptor) {
|
||||||
|
t.Fatalf("unexpected value %d returned by References: %v", i, references[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mediaTypeTest(t *testing.T, contentType string, mediaType string, shouldError bool) {
|
||||||
|
var m *DeserializedManifestList
|
||||||
|
if contentType == MediaTypeManifestList {
|
||||||
|
_, m = makeTestManifestList(t, mediaType)
|
||||||
|
} else {
|
||||||
|
_, m = makeTestOCIImageIndex(t, mediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, canonical, err := m.Payload()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error getting payload, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalled, descriptor, err := distribution.UnmarshalManifest(
|
||||||
|
contentType,
|
||||||
|
canonical)
|
||||||
|
|
||||||
|
if shouldError {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("bad content type should have produced error")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error unmarshaling manifest, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
asManifest := unmarshalled.(*DeserializedManifestList)
|
||||||
|
if asManifest.MediaType != mediaType {
|
||||||
|
t.Fatalf("Bad media type '%v' as unmarshalled", asManifest.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if descriptor.MediaType != contentType {
|
||||||
|
t.Fatalf("Bad media type '%v' for descriptor", descriptor.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalledMediaType, _, _ := unmarshalled.Payload()
|
||||||
|
if unmarshalledMediaType != contentType {
|
||||||
|
t.Fatalf("Bad media type '%v' for payload", unmarshalledMediaType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediaTypes(t *testing.T) {
|
||||||
|
mediaTypeTest(t, MediaTypeManifestList, "", true)
|
||||||
|
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList, false)
|
||||||
|
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList+"XXX", true)
|
||||||
|
mediaTypeTest(t, v1.MediaTypeImageIndex, "", false)
|
||||||
|
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex, false)
|
||||||
|
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex+"XXX", true)
|
||||||
|
}
|
||||||
|
|
107
manifest/ocischema/builder.go
Normal file
107
manifest/ocischema/builder.go
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package ocischema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Builder is a type for constructing manifests.
|
||||||
|
type Builder struct {
|
||||||
|
// bs is a BlobService used to publish the configuration blob.
|
||||||
|
bs distribution.BlobService
|
||||||
|
|
||||||
|
// configJSON references
|
||||||
|
configJSON []byte
|
||||||
|
|
||||||
|
// layers is a list of layer descriptors that gets built by successive
|
||||||
|
// calls to AppendReference.
|
||||||
|
layers []distribution.Descriptor
|
||||||
|
|
||||||
|
// Annotations contains arbitrary metadata relating to the targeted content.
|
||||||
|
annotations map[string]string
|
||||||
|
|
||||||
|
// For testing purposes
|
||||||
|
mediaType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewManifestBuilder is used to build new manifests for the current schema
|
||||||
|
// version. It takes a BlobService so it can publish the configuration blob
|
||||||
|
// as part of the Build process, and annotations.
|
||||||
|
func NewManifestBuilder(bs distribution.BlobService, configJSON []byte, annotations map[string]string) distribution.ManifestBuilder {
|
||||||
|
mb := &Builder{
|
||||||
|
bs: bs,
|
||||||
|
configJSON: make([]byte, len(configJSON)),
|
||||||
|
annotations: annotations,
|
||||||
|
mediaType: v1.MediaTypeImageManifest,
|
||||||
|
}
|
||||||
|
copy(mb.configJSON, configJSON)
|
||||||
|
|
||||||
|
return mb
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMediaType assigns the passed mediatype or error if the mediatype is not a
|
||||||
|
// valid media type for oci image manifests currently: "" or "application/vnd.oci.image.manifest.v1+json"
|
||||||
|
func (mb *Builder) SetMediaType(mediaType string) error {
|
||||||
|
if mediaType != "" && mediaType != v1.MediaTypeImageManifest {
|
||||||
|
return errors.New("invalid media type for OCI image manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
mb.mediaType = mediaType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build produces a final manifest from the given references.
|
||||||
|
func (mb *Builder) Build(ctx context.Context) (distribution.Manifest, error) {
|
||||||
|
m := Manifest{
|
||||||
|
Versioned: manifest.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: mb.mediaType,
|
||||||
|
},
|
||||||
|
Layers: make([]distribution.Descriptor, len(mb.layers)),
|
||||||
|
Annotations: mb.annotations,
|
||||||
|
}
|
||||||
|
copy(m.Layers, mb.layers)
|
||||||
|
|
||||||
|
configDigest := digest.FromBytes(mb.configJSON)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
m.Config, err = mb.bs.Stat(ctx, configDigest)
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
// Override MediaType, since Put always replaces the specified media
|
||||||
|
// type with application/octet-stream in the descriptor it returns.
|
||||||
|
m.Config.MediaType = v1.MediaTypeImageConfig
|
||||||
|
return FromStruct(m)
|
||||||
|
case distribution.ErrBlobUnknown:
|
||||||
|
// nop
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add config to the blob store
|
||||||
|
m.Config, err = mb.bs.Put(ctx, v1.MediaTypeImageConfig, mb.configJSON)
|
||||||
|
// Override MediaType, since Put always replaces the specified media
|
||||||
|
// type with application/octet-stream in the descriptor it returns.
|
||||||
|
m.Config.MediaType = v1.MediaTypeImageConfig
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return FromStruct(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendReference adds a reference to the current ManifestBuilder.
|
||||||
|
func (mb *Builder) AppendReference(d distribution.Describable) error {
|
||||||
|
mb.layers = append(mb.layers, d.Descriptor())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// References returns the current references added to this builder.
|
||||||
|
func (mb *Builder) References() []distribution.Descriptor {
|
||||||
|
return mb.layers
|
||||||
|
}
|
173
manifest/ocischema/builder_test.go
Normal file
173
manifest/ocischema/builder_test.go
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
package ocischema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockBlobService struct {
|
||||||
|
descriptors map[digest.Digest]distribution.Descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
if descriptor, ok := bs.descriptors[dgst]; ok {
|
||||||
|
return descriptor, nil
|
||||||
|
}
|
||||||
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *mockBlobService) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *mockBlobService) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
|
||||||
|
d := distribution.Descriptor{
|
||||||
|
Digest: digest.FromBytes(p),
|
||||||
|
Size: int64(len(p)),
|
||||||
|
MediaType: "application/octet-stream",
|
||||||
|
}
|
||||||
|
bs.descriptors[d.Digest] = d
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *mockBlobService) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *mockBlobService) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilder(t *testing.T) {
|
||||||
|
imgJSON := []byte(`{
|
||||||
|
"created": "2015-10-31T22:22:56.015925234Z",
|
||||||
|
"author": "Alyssa P. Hacker <alyspdev@example.com>",
|
||||||
|
"architecture": "amd64",
|
||||||
|
"os": "linux",
|
||||||
|
"config": {
|
||||||
|
"User": "alice",
|
||||||
|
"ExposedPorts": {
|
||||||
|
"8080/tcp": {}
|
||||||
|
},
|
||||||
|
"Env": [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||||
|
"FOO=oci_is_a",
|
||||||
|
"BAR=well_written_spec"
|
||||||
|
],
|
||||||
|
"Entrypoint": [
|
||||||
|
"/bin/my-app-binary"
|
||||||
|
],
|
||||||
|
"Cmd": [
|
||||||
|
"--foreground",
|
||||||
|
"--config",
|
||||||
|
"/etc/my-app.d/default.cfg"
|
||||||
|
],
|
||||||
|
"Volumes": {
|
||||||
|
"/var/job-result-data": {},
|
||||||
|
"/var/log/my-app-logs": {}
|
||||||
|
},
|
||||||
|
"WorkingDir": "/home/alice",
|
||||||
|
"Labels": {
|
||||||
|
"com.example.project.git.url": "https://example.com/project.git",
|
||||||
|
"com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rootfs": {
|
||||||
|
"diff_ids": [
|
||||||
|
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
|
||||||
|
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||||
|
],
|
||||||
|
"type": "layers"
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
"hot": "potato"
|
||||||
|
}
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"created": "2015-10-31T22:22:54.690851953Z",
|
||||||
|
"created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2015-10-31T22:22:55.613815829Z",
|
||||||
|
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
|
||||||
|
"empty_layer": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
configDigest := digest.FromBytes(imgJSON)
|
||||||
|
|
||||||
|
descriptors := []distribution.Descriptor{
|
||||||
|
{
|
||||||
|
Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
|
||||||
|
Size: 5312,
|
||||||
|
MediaType: v1.MediaTypeImageLayerGzip,
|
||||||
|
Annotations: map[string]string{"apple": "orange", "lettuce": "wrap"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
|
||||||
|
Size: 235231,
|
||||||
|
MediaType: v1.MediaTypeImageLayerGzip,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Digest: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
|
||||||
|
Size: 639152,
|
||||||
|
MediaType: v1.MediaTypeImageLayerGzip,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
annotations := map[string]string{"hot": "potato"}
|
||||||
|
|
||||||
|
bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)}
|
||||||
|
builder := NewManifestBuilder(bs, imgJSON, annotations)
|
||||||
|
|
||||||
|
for _, d := range descriptors {
|
||||||
|
if err := builder.AppendReference(d); err != nil {
|
||||||
|
t.Fatalf("AppendReference returned error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
built, err := builder.Build(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Build returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the config was put in the blob store
|
||||||
|
_, err = bs.Stat(context.Background(), configDigest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("config was not put in the blob store")
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest := built.(*DeserializedManifest).Manifest
|
||||||
|
if manifest.Annotations["hot"] != "potato" {
|
||||||
|
t.Fatalf("unexpected annotation in manifest: %s", manifest.Annotations["hot"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if manifest.Versioned.SchemaVersion != 2 {
|
||||||
|
t.Fatal("SchemaVersion != 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
target := manifest.Target()
|
||||||
|
if target.Digest != configDigest {
|
||||||
|
t.Fatalf("unexpected digest in target: %s", target.Digest.String())
|
||||||
|
}
|
||||||
|
if target.MediaType != v1.MediaTypeImageConfig {
|
||||||
|
t.Fatalf("unexpected media type in target: %s", target.MediaType)
|
||||||
|
}
|
||||||
|
if target.Size != 1632 {
|
||||||
|
t.Fatalf("unexpected size in target: %d", target.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
references := manifest.References()
|
||||||
|
expected := append([]distribution.Descriptor{manifest.Target()}, descriptors...)
|
||||||
|
if !reflect.DeepEqual(references, expected) {
|
||||||
|
t.Fatal("References() does not match the descriptors added")
|
||||||
|
}
|
||||||
|
}
|
124
manifest/ocischema/manifest.go
Normal file
124
manifest/ocischema/manifest.go
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
package ocischema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// SchemaVersion provides a pre-initialized version structure for this
|
||||||
|
// packages version of the manifest.
|
||||||
|
SchemaVersion = manifest.Versioned{
|
||||||
|
SchemaVersion: 2, // historical value here.. does not pertain to OCI or docker version
|
||||||
|
MediaType: v1.MediaTypeImageManifest,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
|
||||||
|
m := new(DeserializedManifest)
|
||||||
|
err := m.UnmarshalJSON(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst := digest.FromBytes(b)
|
||||||
|
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageManifest}, err
|
||||||
|
}
|
||||||
|
err := distribution.RegisterManifestSchema(v1.MediaTypeImageManifest, ocischemaFunc)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to register manifest: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest defines a ocischema manifest.
|
||||||
|
type Manifest struct {
|
||||||
|
manifest.Versioned
|
||||||
|
|
||||||
|
// Config references the image configuration as a blob.
|
||||||
|
Config distribution.Descriptor `json:"config"`
|
||||||
|
|
||||||
|
// Layers lists descriptors for the layers referenced by the
|
||||||
|
// configuration.
|
||||||
|
Layers []distribution.Descriptor `json:"layers"`
|
||||||
|
|
||||||
|
// Annotations contains arbitrary metadata for the image manifest.
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// References returns the descriptors of this manifests references.
|
||||||
|
func (m Manifest) References() []distribution.Descriptor {
|
||||||
|
references := make([]distribution.Descriptor, 0, 1+len(m.Layers))
|
||||||
|
references = append(references, m.Config)
|
||||||
|
references = append(references, m.Layers...)
|
||||||
|
return references
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target returns the target of this manifest.
|
||||||
|
func (m Manifest) Target() distribution.Descriptor {
|
||||||
|
return m.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeserializedManifest wraps Manifest with a copy of the original JSON.
|
||||||
|
// It satisfies the distribution.Manifest interface.
|
||||||
|
type DeserializedManifest struct {
|
||||||
|
Manifest
|
||||||
|
|
||||||
|
// canonical is the canonical byte representation of the Manifest.
|
||||||
|
canonical []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStruct takes a Manifest structure, marshals it to JSON, and returns a
|
||||||
|
// DeserializedManifest which contains the manifest and its JSON representation.
|
||||||
|
func FromStruct(m Manifest) (*DeserializedManifest, error) {
|
||||||
|
var deserialized DeserializedManifest
|
||||||
|
deserialized.Manifest = m
|
||||||
|
|
||||||
|
var err error
|
||||||
|
deserialized.canonical, err = json.MarshalIndent(&m, "", " ")
|
||||||
|
return &deserialized, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON populates a new Manifest struct from JSON data.
|
||||||
|
func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
|
||||||
|
m.canonical = make([]byte, len(b))
|
||||||
|
// store manifest in canonical
|
||||||
|
copy(m.canonical, b)
|
||||||
|
|
||||||
|
// Unmarshal canonical JSON into Manifest object
|
||||||
|
var manifest Manifest
|
||||||
|
if err := json.Unmarshal(m.canonical, &manifest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if manifest.MediaType != "" && manifest.MediaType != v1.MediaTypeImageManifest {
|
||||||
|
return fmt.Errorf("if present, mediaType in manifest should be '%s' not '%s'",
|
||||||
|
v1.MediaTypeImageManifest, manifest.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Manifest = manifest
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the contents of canonical. If canonical is empty,
|
||||||
|
// marshals the inner contents.
|
||||||
|
func (m *DeserializedManifest) MarshalJSON() ([]byte, error) {
|
||||||
|
if len(m.canonical) > 0 {
|
||||||
|
return m.canonical, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("JSON representation not initialized in DeserializedManifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload returns the raw content of the manifest. The contents can be used to
|
||||||
|
// calculate the content identifier.
|
||||||
|
func (m DeserializedManifest) Payload() (string, []byte, error) {
|
||||||
|
return v1.MediaTypeImageManifest, m.canonical, nil
|
||||||
|
}
|
184
manifest/ocischema/manifest_test.go
Normal file
184
manifest/ocischema/manifest_test.go
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
package ocischema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var expectedManifestSerialization = []byte(`{
|
||||||
|
"schemaVersion": 2,
|
||||||
|
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
"config": {
|
||||||
|
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||||
|
"size": 985,
|
||||||
|
"digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
"annotations": {
|
||||||
|
"apple": "orange"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||||
|
"size": 153263,
|
||||||
|
"digest": "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
|
||||||
|
"annotations": {
|
||||||
|
"lettuce": "wrap"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"annotations": {
|
||||||
|
"hot": "potato"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
func makeTestManifest(mediaType string) Manifest {
|
||||||
|
return Manifest{
|
||||||
|
Versioned: manifest.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: mediaType,
|
||||||
|
},
|
||||||
|
Config: distribution.Descriptor{
|
||||||
|
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
|
Size: 985,
|
||||||
|
MediaType: v1.MediaTypeImageConfig,
|
||||||
|
Annotations: map[string]string{"apple": "orange"},
|
||||||
|
},
|
||||||
|
Layers: []distribution.Descriptor{
|
||||||
|
{
|
||||||
|
Digest: "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
|
||||||
|
Size: 153263,
|
||||||
|
MediaType: v1.MediaTypeImageLayerGzip,
|
||||||
|
Annotations: map[string]string{"lettuce": "wrap"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{"hot": "potato"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManifest(t *testing.T) {
|
||||||
|
manifest := makeTestManifest(v1.MediaTypeImageManifest)
|
||||||
|
|
||||||
|
deserialized, err := FromStruct(manifest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating DeserializedManifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaType, canonical, _ := deserialized.Payload()
|
||||||
|
|
||||||
|
if mediaType != v1.MediaTypeImageManifest {
|
||||||
|
t.Fatalf("unexpected media type: %s", mediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the canonical field is the same as json.MarshalIndent
|
||||||
|
// with these parameters.
|
||||||
|
p, err := json.MarshalIndent(&manifest, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error marshaling manifest: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(p, canonical) {
|
||||||
|
t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that canonical field matches expected value.
|
||||||
|
if !bytes.Equal(expectedManifestSerialization, canonical) {
|
||||||
|
t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(expectedManifestSerialization))
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmarshalled DeserializedManifest
|
||||||
|
if err := json.Unmarshal(deserialized.canonical, &unmarshalled); err != nil {
|
||||||
|
t.Fatalf("error unmarshaling manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(&unmarshalled, deserialized) {
|
||||||
|
t.Fatalf("manifests are different after unmarshaling: %v != %v", unmarshalled, *deserialized)
|
||||||
|
}
|
||||||
|
if deserialized.Annotations["hot"] != "potato" {
|
||||||
|
t.Fatalf("unexpected annotation in manifest: %s", deserialized.Annotations["hot"])
|
||||||
|
}
|
||||||
|
|
||||||
|
target := deserialized.Target()
|
||||||
|
if target.Digest != "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b" {
|
||||||
|
t.Fatalf("unexpected digest in target: %s", target.Digest.String())
|
||||||
|
}
|
||||||
|
if target.MediaType != v1.MediaTypeImageConfig {
|
||||||
|
t.Fatalf("unexpected media type in target: %s", target.MediaType)
|
||||||
|
}
|
||||||
|
if target.Size != 985 {
|
||||||
|
t.Fatalf("unexpected size in target: %d", target.Size)
|
||||||
|
}
|
||||||
|
if target.Annotations["apple"] != "orange" {
|
||||||
|
t.Fatalf("unexpected annotation in target: %s", target.Annotations["apple"])
|
||||||
|
}
|
||||||
|
|
||||||
|
references := deserialized.References()
|
||||||
|
if len(references) != 2 {
|
||||||
|
t.Fatalf("unexpected number of references: %d", len(references))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(references[0], target) {
|
||||||
|
t.Fatalf("first reference should be target: %v != %v", references[0], target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the second reference
|
||||||
|
if references[1].Digest != "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b" {
|
||||||
|
t.Fatalf("unexpected digest in reference: %s", references[0].Digest.String())
|
||||||
|
}
|
||||||
|
if references[1].MediaType != v1.MediaTypeImageLayerGzip {
|
||||||
|
t.Fatalf("unexpected media type in reference: %s", references[0].MediaType)
|
||||||
|
}
|
||||||
|
if references[1].Size != 153263 {
|
||||||
|
t.Fatalf("unexpected size in reference: %d", references[0].Size)
|
||||||
|
}
|
||||||
|
if references[1].Annotations["lettuce"] != "wrap" {
|
||||||
|
t.Fatalf("unexpected annotation in reference: %s", references[1].Annotations["lettuce"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mediaTypeTest(t *testing.T, mediaType string, shouldError bool) {
|
||||||
|
manifest := makeTestManifest(mediaType)
|
||||||
|
|
||||||
|
deserialized, err := FromStruct(manifest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating DeserializedManifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalled, descriptor, err := distribution.UnmarshalManifest(
|
||||||
|
v1.MediaTypeImageManifest,
|
||||||
|
deserialized.canonical)
|
||||||
|
|
||||||
|
if shouldError {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("bad content type should have produced error")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error unmarshaling manifest, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
asManifest := unmarshalled.(*DeserializedManifest)
|
||||||
|
if asManifest.MediaType != mediaType {
|
||||||
|
t.Fatalf("Bad media type '%v' as unmarshalled", asManifest.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if descriptor.MediaType != v1.MediaTypeImageManifest {
|
||||||
|
t.Fatalf("Bad media type '%v' for descriptor", descriptor.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalledMediaType, _, _ := unmarshalled.Payload()
|
||||||
|
if unmarshalledMediaType != v1.MediaTypeImageManifest {
|
||||||
|
t.Fatalf("Bad media type '%v' for payload", unmarshalledMediaType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediaTypes(t *testing.T) {
|
||||||
|
mediaTypeTest(t, "", false)
|
||||||
|
mediaTypeTest(t, v1.MediaTypeImageManifest, false)
|
||||||
|
mediaTypeTest(t, v1.MediaTypeImageManifest+"XXX", true)
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package schema1
|
package schema1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -8,7 +9,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/context"
|
|
||||||
"github.com/docker/distribution/manifest"
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
|
|
@ -3,12 +3,13 @@ package schema1
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/context"
|
dcontext "github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
@ -58,7 +59,7 @@ func TestEmptyTar(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewReader returned error: %v", err)
|
t.Fatalf("NewReader returned error: %v", err)
|
||||||
}
|
}
|
||||||
n, err := gzipReader.Read(decompressed[:])
|
n, _ := gzipReader.Read(decompressed[:])
|
||||||
if n != 1024 {
|
if n != 1024 {
|
||||||
t.Fatalf("read returned %d bytes; expected 1024", n)
|
t.Fatalf("read returned %d bytes; expected 1024", n)
|
||||||
}
|
}
|
||||||
|
@ -214,13 +215,13 @@ func TestConfigBuilder(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signed, err := builder.Build(context.Background())
|
signed, err := builder.Build(dcontext.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Build returned error: %v", err)
|
t.Fatalf("Build returned error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the gzipped empty layer tar was put in the blob store
|
// Check that the gzipped empty layer tar was put in the blob store
|
||||||
_, err = bs.Stat(context.Background(), digestSHA256GzippedEmptyTar)
|
_, err = bs.Stat(dcontext.Background(), digestSHA256GzippedEmptyTar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("gzipped empty tar was not put in the blob store")
|
t.Fatal("gzipped empty tar was not put in the blob store")
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ type SignedManifest struct {
|
||||||
|
|
||||||
// UnmarshalJSON populates a new SignedManifest struct from JSON data.
|
// UnmarshalJSON populates a new SignedManifest struct from JSON data.
|
||||||
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||||
sm.all = make([]byte, len(b), len(b))
|
sm.all = make([]byte, len(b))
|
||||||
// store manifest and signatures in all
|
// store manifest and signatures in all
|
||||||
copy(sm.all, b)
|
copy(sm.all, b)
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sm.Canonical stores the canonical manifest JSON
|
// sm.Canonical stores the canonical manifest JSON
|
||||||
sm.Canonical = make([]byte, len(bytes), len(bytes))
|
sm.Canonical = make([]byte, len(bytes))
|
||||||
copy(sm.Canonical, bytes)
|
copy(sm.Canonical, bytes)
|
||||||
|
|
||||||
// Unmarshal canonical JSON into Manifest object
|
// Unmarshal canonical JSON into Manifest object
|
||||||
|
@ -138,7 +138,7 @@ func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// References returnes the descriptors of this manifests references
|
// References returns the descriptors of this manifests references
|
||||||
func (sm SignedManifest) References() []distribution.Descriptor {
|
func (sm SignedManifest) References() []distribution.Descriptor {
|
||||||
dependencies := make([]distribution.Descriptor, len(sm.FSLayers))
|
dependencies := make([]distribution.Descriptor, len(sm.FSLayers))
|
||||||
for i, fsLayer := range sm.FSLayers {
|
for i, fsLayer := range sm.FSLayers {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package schema1
|
package schema1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"errors"
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/context"
|
|
||||||
"github.com/docker/distribution/manifest"
|
"github.com/docker/distribution/manifest"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
@ -58,7 +58,7 @@ func (mb *referenceManifestBuilder) Build(ctx context.Context) (distribution.Man
|
||||||
func (mb *referenceManifestBuilder) AppendReference(d distribution.Describable) error {
|
func (mb *referenceManifestBuilder) AppendReference(d distribution.Describable) error {
|
||||||
r, ok := d.(Reference)
|
r, ok := d.(Reference)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Unable to add non-reference type to v1 builder")
|
return fmt.Errorf("unable to add non-reference type to v1 builder")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entries need to be prepended
|
// Entries need to be prepended
|
||||||
|
|
|
@ -3,8 +3,8 @@ package schema1
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Verify verifies the signature of the signed manifest returning the public
|
// Verify verifies the signature of the signed manifest returning the public
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package schema2
|
package schema2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/context"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package schema2
|
package schema2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/context"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ type Manifest struct {
|
||||||
Layers []distribution.Descriptor `json:"layers"`
|
Layers []distribution.Descriptor `json:"layers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// References returnes the descriptors of this manifests references.
|
// References returns the descriptors of this manifests references.
|
||||||
func (m Manifest) References() []distribution.Descriptor {
|
func (m Manifest) References() []distribution.Descriptor {
|
||||||
references := make([]distribution.Descriptor, 0, 1+len(m.Layers))
|
references := make([]distribution.Descriptor, 0, 1+len(m.Layers))
|
||||||
references = append(references, m.Config)
|
references = append(references, m.Config)
|
||||||
|
@ -79,7 +79,7 @@ func (m Manifest) References() []distribution.Descriptor {
|
||||||
return references
|
return references
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target returns the target of this signed manifest.
|
// Target returns the target of this manifest.
|
||||||
func (m Manifest) Target() distribution.Descriptor {
|
func (m Manifest) Target() distribution.Descriptor {
|
||||||
return m.Config
|
return m.Config
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ func FromStruct(m Manifest) (*DeserializedManifest, error) {
|
||||||
|
|
||||||
// UnmarshalJSON populates a new Manifest struct from JSON data.
|
// UnmarshalJSON populates a new Manifest struct from JSON data.
|
||||||
func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
|
func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
|
||||||
m.canonical = make([]byte, len(b), len(b))
|
m.canonical = make([]byte, len(b))
|
||||||
// store manifest in canonical
|
// store manifest in canonical
|
||||||
copy(m.canonical, b)
|
copy(m.canonical, b)
|
||||||
|
|
||||||
|
@ -116,6 +116,12 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if manifest.MediaType != MediaTypeManifest {
|
||||||
|
return fmt.Errorf("mediaType in manifest should be '%s' not '%s'",
|
||||||
|
MediaTypeManifest, manifest.MediaType)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
m.Manifest = manifest
|
m.Manifest = manifest
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var expectedManifestSerialization = []byte(`{
|
var expectedManifestSerialization = []byte(`{
|
||||||
|
@ -26,9 +27,12 @@ var expectedManifestSerialization = []byte(`{
|
||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
func TestManifest(t *testing.T) {
|
func makeTestManifest(mediaType string) Manifest {
|
||||||
manifest := Manifest{
|
return Manifest{
|
||||||
Versioned: SchemaVersion,
|
Versioned: manifest.Versioned{
|
||||||
|
SchemaVersion: 2,
|
||||||
|
MediaType: mediaType,
|
||||||
|
},
|
||||||
Config: distribution.Descriptor{
|
Config: distribution.Descriptor{
|
||||||
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
|
||||||
Size: 985,
|
Size: 985,
|
||||||
|
@ -42,13 +46,17 @@ func TestManifest(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManifest(t *testing.T) {
|
||||||
|
manifest := makeTestManifest(MediaTypeManifest)
|
||||||
|
|
||||||
deserialized, err := FromStruct(manifest)
|
deserialized, err := FromStruct(manifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error creating DeserializedManifest: %v", err)
|
t.Fatalf("error creating DeserializedManifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaType, canonical, err := deserialized.Payload()
|
mediaType, canonical, _ := deserialized.Payload()
|
||||||
|
|
||||||
if mediaType != MediaTypeManifest {
|
if mediaType != MediaTypeManifest {
|
||||||
t.Fatalf("unexpected media type: %s", mediaType)
|
t.Fatalf("unexpected media type: %s", mediaType)
|
||||||
|
@ -109,3 +117,46 @@ func TestManifest(t *testing.T) {
|
||||||
t.Fatalf("unexpected size in reference: %d", references[0].Size)
|
t.Fatalf("unexpected size in reference: %d", references[0].Size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mediaTypeTest(t *testing.T, mediaType string, shouldError bool) {
|
||||||
|
manifest := makeTestManifest(mediaType)
|
||||||
|
|
||||||
|
deserialized, err := FromStruct(manifest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating DeserializedManifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalled, descriptor, err := distribution.UnmarshalManifest(
|
||||||
|
MediaTypeManifest,
|
||||||
|
deserialized.canonical)
|
||||||
|
|
||||||
|
if shouldError {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("bad content type should have produced error")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error unmarshaling manifest, %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
asManifest := unmarshalled.(*DeserializedManifest)
|
||||||
|
if asManifest.MediaType != mediaType {
|
||||||
|
t.Fatalf("Bad media type '%v' as unmarshalled", asManifest.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if descriptor.MediaType != MediaTypeManifest {
|
||||||
|
t.Fatalf("Bad media type '%v' for descriptor", descriptor.MediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalledMediaType, _, _ := unmarshalled.Payload()
|
||||||
|
if unmarshalledMediaType != MediaTypeManifest {
|
||||||
|
t.Fatalf("Bad media type '%v' for payload", unmarshalledMediaType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediaTypes(t *testing.T) {
|
||||||
|
mediaTypeTest(t, "", true)
|
||||||
|
mediaTypeTest(t, MediaTypeManifest, false)
|
||||||
|
mediaTypeTest(t, MediaTypeManifest+"XXX", true)
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package distribution
|
package distribution
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime"
|
"mime"
|
||||||
|
|
||||||
"github.com/docker/distribution/context"
|
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ func ManifestMediaTypes() (mediaTypes []string) {
|
||||||
// UnmarshalFunc implements manifest unmarshalling a given MediaType
|
// UnmarshalFunc implements manifest unmarshalling a given MediaType
|
||||||
type UnmarshalFunc func([]byte) (Manifest, Descriptor, error)
|
type UnmarshalFunc func([]byte) (Manifest, Descriptor, error)
|
||||||
|
|
||||||
var mappings = make(map[string]UnmarshalFunc, 0)
|
var mappings = make(map[string]UnmarshalFunc)
|
||||||
|
|
||||||
// UnmarshalManifest looks up manifest unmarshal functions based on
|
// UnmarshalManifest looks up manifest unmarshal functions based on
|
||||||
// MediaType
|
// MediaType
|
||||||
|
|
16
metrics/prometheus.go
Normal file
16
metrics/prometheus.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import "github.com/docker/go-metrics"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NamespacePrefix is the namespace of prometheus metrics
|
||||||
|
NamespacePrefix = "registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// StorageNamespace is the prometheus namespace of blob/cache related operations
|
||||||
|
StorageNamespace = metrics.NewNamespace(NamespacePrefix, "storage", nil)
|
||||||
|
|
||||||
|
// NotificationsNamespace is the prometheus namespace of notification related metrics
|
||||||
|
NotificationsNamespace = metrics.NewNamespace(NamespacePrefix, "notifications", nil)
|
||||||
|
)
|
|
@ -8,15 +8,17 @@ import (
|
||||||
"github.com/docker/distribution/context"
|
"github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/distribution/uuid"
|
"github.com/docker/distribution/uuid"
|
||||||
|
events "github.com/docker/go-events"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type bridge struct {
|
type bridge struct {
|
||||||
ub URLBuilder
|
ub URLBuilder
|
||||||
|
includeReferences bool
|
||||||
actor ActorRecord
|
actor ActorRecord
|
||||||
source SourceRecord
|
source SourceRecord
|
||||||
request RequestRecord
|
request RequestRecord
|
||||||
sink Sink
|
sink events.Sink
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Listener = &bridge{}
|
var _ Listener = &bridge{}
|
||||||
|
@ -31,9 +33,10 @@ type URLBuilder interface {
|
||||||
// using the actor and source. Any urls populated in the events created by
|
// using the actor and source. Any urls populated in the events created by
|
||||||
// this bridge will be created using the URLBuilder.
|
// this bridge will be created using the URLBuilder.
|
||||||
// TODO(stevvooe): Update this to simply take a context.Context object.
|
// TODO(stevvooe): Update this to simply take a context.Context object.
|
||||||
func NewBridge(ub URLBuilder, source SourceRecord, actor ActorRecord, request RequestRecord, sink Sink) Listener {
|
func NewBridge(ub URLBuilder, source SourceRecord, actor ActorRecord, request RequestRecord, sink events.Sink, includeReferences bool) Listener {
|
||||||
return &bridge{
|
return &bridge{
|
||||||
ub: ub,
|
ub: ub,
|
||||||
|
includeReferences: includeReferences,
|
||||||
actor: actor,
|
actor: actor,
|
||||||
source: source,
|
source: source,
|
||||||
request: request,
|
request: request,
|
||||||
|
@ -108,13 +111,19 @@ func (b *bridge) BlobDeleted(repo reference.Named, dgst digest.Digest) error {
|
||||||
return b.createBlobDeleteEventAndWrite(EventActionDelete, repo, dgst)
|
return b.createBlobDeleteEventAndWrite(EventActionDelete, repo, dgst)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) createManifestEventAndWrite(action string, repo reference.Named, sm distribution.Manifest) error {
|
func (b *bridge) TagDeleted(repo reference.Named, tag string) error {
|
||||||
manifestEvent, err := b.createManifestEvent(action, repo, sm)
|
event := b.createEvent(EventActionDelete)
|
||||||
if err != nil {
|
event.Target.Repository = repo.Name()
|
||||||
return err
|
event.Target.Tag = tag
|
||||||
}
|
|
||||||
|
|
||||||
return b.sink.Write(*manifestEvent)
|
return b.sink.Write(*event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) RepoDeleted(repo reference.Named) error {
|
||||||
|
event := b.createEvent(EventActionDelete)
|
||||||
|
event.Target.Repository = repo.Name()
|
||||||
|
|
||||||
|
return b.sink.Write(*event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) createManifestDeleteEventAndWrite(action string, repo reference.Named, dgst digest.Digest) error {
|
func (b *bridge) createManifestDeleteEventAndWrite(action string, repo reference.Named, dgst digest.Digest) error {
|
||||||
|
@ -135,7 +144,7 @@ func (b *bridge) createManifestEvent(action string, repo reference.Named, sm dis
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have the canonical manifest descriptor here
|
// Ensure we have the canonical manifest descriptor here
|
||||||
_, desc, err := distribution.UnmarshalManifest(mt, p)
|
manifest, desc, err := distribution.UnmarshalManifest(mt, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -144,6 +153,9 @@ func (b *bridge) createManifestEvent(action string, repo reference.Named, sm dis
|
||||||
event.Target.Length = desc.Size
|
event.Target.Length = desc.Size
|
||||||
event.Target.Size = desc.Size
|
event.Target.Size = desc.Size
|
||||||
event.Target.Digest = desc.Digest
|
event.Target.Digest = desc.Digest
|
||||||
|
if b.includeReferences {
|
||||||
|
event.Target.References = append(event.Target.References, manifest.References()...)
|
||||||
|
}
|
||||||
|
|
||||||
ref, err := reference.WithDigest(repo, event.Target.Digest)
|
ref, err := reference.WithDigest(repo, event.Target.Digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue