mirror of
https://github.com/openfaas/faas.git
synced 2025-06-18 03:56:37 +00:00
Update vendoring via vndr
This commit is contained in:
37
gateway/vendor/github.com/docker/distribution/.gitignore
generated
vendored
37
gateway/vendor/github.com/docker/distribution/.gitignore
generated
vendored
@ -1,37 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# never checkin from the bin file (for now)
|
||||
bin/*
|
||||
|
||||
# Test key files
|
||||
*.pem
|
||||
|
||||
# Cover profiles
|
||||
*.out
|
||||
|
||||
# Editor/IDE specific files.
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
18
gateway/vendor/github.com/docker/distribution/.mailmap
generated
vendored
18
gateway/vendor/github.com/docker/distribution/.mailmap
generated
vendored
@ -1,18 +0,0 @@
|
||||
Stephen J Day <stephen.day@docker.com> Stephen Day <stevvooe@users.noreply.github.com>
|
||||
Stephen J Day <stephen.day@docker.com> Stephen Day <stevvooe@gmail.com>
|
||||
Olivier Gambier <olivier@docker.com> Olivier Gambier <dmp42@users.noreply.github.com>
|
||||
Brian Bland <brian.bland@docker.com> Brian Bland <r4nd0m1n4t0r@gmail.com>
|
||||
Brian Bland <brian.bland@docker.com> Brian Bland <brian.t.bland@gmail.com>
|
||||
Josh Hawn <josh.hawn@docker.com> Josh Hawn <jlhawn@berkeley.edu>
|
||||
Richard Scothern <richard.scothern@docker.com> Richard <richard.scothern@gmail.com>
|
||||
Richard Scothern <richard.scothern@docker.com> Richard Scothern <richard.scothern@gmail.com>
|
||||
Andrew Meredith <andymeredith@gmail.com> Andrew Meredith <kendru@users.noreply.github.com>
|
||||
harche <p.harshal@gmail.com> harche <harche@users.noreply.github.com>
|
||||
Jessie Frazelle <jessie@docker.com> <jfrazelle@users.noreply.github.com>
|
||||
Sharif Nassar <sharif@mrwacky.com> Sharif Nassar <mrwacky42@users.noreply.github.com>
|
||||
Sven Dowideit <SvenDowideit@home.org.au> Sven Dowideit <SvenDowideit@users.noreply.github.com>
|
||||
Vincent Giersch <vincent.giersch@ovh.net> Vincent Giersch <vincent@giersch.fr>
|
||||
davidli <wenquan.li@hp.com> davidli <wenquan.li@hpe.com>
|
||||
Omer Cohen <git@omer.io> Omer Cohen <git@omerc.net>
|
||||
Eric Yang <windfarer@gmail.com> Eric Yang <Windfarer@users.noreply.github.com>
|
||||
Nikita Tarasov <nikita@mygento.ru> Nikita <luckyraul@users.noreply.github.com>
|
182
gateway/vendor/github.com/docker/distribution/AUTHORS
generated
vendored
182
gateway/vendor/github.com/docker/distribution/AUTHORS
generated
vendored
@ -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>
|
117
gateway/vendor/github.com/docker/distribution/BUILDING.md
generated
vendored
117
gateway/vendor/github.com/docker/distribution/BUILDING.md
generated
vendored
@ -1,117 +0,0 @@
|
||||
|
||||
# Building the registry source
|
||||
|
||||
## Use-case
|
||||
|
||||
This is useful if you intend to actively work on the registry.
|
||||
|
||||
### Alternatives
|
||||
|
||||
Most people should use the [official Registry docker image](https://hub.docker.com/r/library/registry/).
|
||||
|
||||
People looking for advanced operational use cases might consider rolling their own image with a custom Dockerfile inheriting `FROM registry:2`.
|
||||
|
||||
OS X users who want to run natively can do so following [the instructions here](https://github.com/docker/docker.github.io/blob/master/registry/recipes/osx-setup-guide.md).
|
||||
|
||||
### Gotchas
|
||||
|
||||
You are expected to know your way around with go & git.
|
||||
|
||||
If you are a casual user with no development experience, and no preliminary knowledge of go, building from source is probably not a good solution for you.
|
||||
|
||||
## Build the development environment
|
||||
|
||||
The first prerequisite of properly building distribution targets is to have a Go
|
||||
development environment setup. Please follow [How to Write Go Code](https://golang.org/doc/code.html)
|
||||
for proper setup. If done correctly, you should have a GOROOT and GOPATH set in the
|
||||
environment.
|
||||
|
||||
If a Go development environment is setup, one can use `go get` to install the
|
||||
`registry` command from the current latest:
|
||||
|
||||
go get github.com/docker/distribution/cmd/registry
|
||||
|
||||
The above will install the source repository into the `GOPATH`.
|
||||
|
||||
Now create the directory for the registry data (this might require you to set permissions properly)
|
||||
|
||||
mkdir -p /var/lib/registry
|
||||
|
||||
... or alternatively `export REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/somewhere` if you want to store data into another location.
|
||||
|
||||
The `registry`
|
||||
binary can then be run with the following:
|
||||
|
||||
$ $GOPATH/bin/registry --version
|
||||
$GOPATH/bin/registry github.com/docker/distribution v2.0.0-alpha.1+unknown
|
||||
|
||||
> __NOTE:__ While you do not need to use `go get` to checkout the distribution
|
||||
> project, for these build instructions to work, the project must be checked
|
||||
> out in the correct location in the `GOPATH`. This should almost always be
|
||||
> `$GOPATH/src/github.com/docker/distribution`.
|
||||
|
||||
The registry can be run with the default config using the following
|
||||
incantation:
|
||||
|
||||
$ $GOPATH/bin/registry serve $GOPATH/src/github.com/docker/distribution/cmd/registry/config-example.yml
|
||||
INFO[0000] endpoint local-5003 disabled, skipping app.id=34bbec38-a91a-494a-9a3f-b72f9010081f version=v2.0.0-alpha.1+unknown
|
||||
INFO[0000] endpoint local-8083 disabled, skipping app.id=34bbec38-a91a-494a-9a3f-b72f9010081f version=v2.0.0-alpha.1+unknown
|
||||
INFO[0000] listening on :5000 app.id=34bbec38-a91a-494a-9a3f-b72f9010081f version=v2.0.0-alpha.1+unknown
|
||||
INFO[0000] debug server listening localhost:5001
|
||||
|
||||
If it is working, one should see the above log messages.
|
||||
|
||||
### Repeatable Builds
|
||||
|
||||
For the full development experience, one should `cd` into
|
||||
`$GOPATH/src/github.com/docker/distribution`. From there, the regular `go`
|
||||
commands, such as `go test`, should work per package (please see
|
||||
[Developing](#developing) if they don't work).
|
||||
|
||||
A `Makefile` has been provided as a convenience to support repeatable builds.
|
||||
Please install the following into `GOPATH` for it to work:
|
||||
|
||||
go get github.com/golang/lint/golint
|
||||
|
||||
Once these commands are available in the `GOPATH`, run `make` to get a full
|
||||
build:
|
||||
|
||||
$ make
|
||||
+ clean
|
||||
+ fmt
|
||||
+ vet
|
||||
+ lint
|
||||
+ build
|
||||
github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar
|
||||
github.com/Sirupsen/logrus
|
||||
github.com/docker/libtrust
|
||||
...
|
||||
github.com/yvasiyarov/gorelic
|
||||
github.com/docker/distribution/registry/handlers
|
||||
github.com/docker/distribution/cmd/registry
|
||||
+ test
|
||||
...
|
||||
ok github.com/docker/distribution/digest 7.875s
|
||||
ok github.com/docker/distribution/manifest 0.028s
|
||||
ok github.com/docker/distribution/notifications 17.322s
|
||||
? github.com/docker/distribution/registry [no test files]
|
||||
ok github.com/docker/distribution/registry/api/v2 0.101s
|
||||
? github.com/docker/distribution/registry/auth [no test files]
|
||||
ok github.com/docker/distribution/registry/auth/silly 0.011s
|
||||
...
|
||||
+ /Users/sday/go/src/github.com/docker/distribution/bin/registry
|
||||
+ /Users/sday/go/src/github.com/docker/distribution/bin/registry-api-descriptor-template
|
||||
+ binaries
|
||||
|
||||
The above provides a repeatable build using the contents of the vendor
|
||||
directory. This includes formatting, vetting, linting, building,
|
||||
testing and generating tagged binaries. We can verify this worked by running
|
||||
the registry binary generated in the "./bin" directory:
|
||||
|
||||
$ ./bin/registry -version
|
||||
./bin/registry github.com/docker/distribution v2.0.0-alpha.2-80-g16d8b2c.m
|
||||
|
||||
### Optional build tags
|
||||
|
||||
Optional [build tags](http://golang.org/pkg/go/build/) can be provided using
|
||||
the environment variable `DOCKER_BUILDTAGS`.
|
108
gateway/vendor/github.com/docker/distribution/CHANGELOG.md
generated
vendored
108
gateway/vendor/github.com/docker/distribution/CHANGELOG.md
generated
vendored
@ -1,108 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## 2.6.0 (2017-01-18)
|
||||
|
||||
#### Storage
|
||||
- S3: fixed bug in delete due to read-after-write inconsistency
|
||||
- S3: allow EC2 IAM roles to be used when authorizing region endpoints
|
||||
- S3: add Object ACL Support
|
||||
- S3: fix delete method's notion of subpaths
|
||||
- S3: use multipart upload API in `Move` method for performance
|
||||
- S3: add v2 signature signing for legacy S3 clones
|
||||
- Swift: add simple heuristic to detect incomplete DLOs during read ops
|
||||
- Swift: support different user and tenant domains
|
||||
- Swift: bulk deletes in chunks
|
||||
- Aliyun OSS: fix delete method's notion of subpaths
|
||||
- Aliyun OSS: optimize data copy after upload finishes
|
||||
- Azure: close leaking response body
|
||||
- Fix storage drivers dropping non-EOF errors when listing repositories
|
||||
- Compare path properly when listing repositories in catalog
|
||||
- Add a foreign layer URL host whitelist
|
||||
- Improve catalog enumerate runtime
|
||||
|
||||
#### Registry
|
||||
- Export `storage.CreateOptions` in top-level package
|
||||
- Enable notifications to endpoints that use self-signed certificates
|
||||
- Properly validate multi-URL foreign layers
|
||||
- Add control over validation of URLs in pushed manifests
|
||||
- Proxy mode: fix socket leak when pull is cancelled
|
||||
- Tag service: properly handle error responses on HEAD request
|
||||
- Support for custom authentication URL in proxying registry
|
||||
- Add configuration option to disable access logging
|
||||
- Add notification filtering by target media type
|
||||
- Manifest: `References()` returns all children
|
||||
- Honor `X-Forwarded-Port` and Forwarded headers
|
||||
- Reference: Preserve tag and digest in With* functions
|
||||
- Add policy configuration for enforcing repository classes
|
||||
|
||||
#### Client
|
||||
- Changes the client Tags `All()` method to follow links
|
||||
- Allow registry clients to connect via HTTP2
|
||||
- Better handling of OAuth errors in client
|
||||
|
||||
#### Spec
|
||||
- Manifest: clarify relationship between urls and foreign layers
|
||||
- Authorization: add support for repository classes
|
||||
|
||||
#### Manifest
|
||||
- Override media type returned from `Stat()` for existing manifests
|
||||
- Add plugin mediatype to distribution manifest
|
||||
|
||||
#### Docs
|
||||
- Document `TOOMANYREQUESTS` error code
|
||||
- Document required Let's Encrypt port
|
||||
- Improve documentation around implementation of OAuth2
|
||||
- Improve documentation for configuration
|
||||
|
||||
#### Auth
|
||||
- Add support for registry type in scope
|
||||
- Add support for using v2 ping challenges for v1
|
||||
- Add leeway to JWT `nbf` and `exp` checking
|
||||
- htpasswd: dynamically parse htpasswd file
|
||||
- Fix missing auth headers with PATCH HTTP request when pushing to default port
|
||||
|
||||
#### Dockerfile
|
||||
- Update to go1.7
|
||||
- Reorder Dockerfile steps for better layer caching
|
||||
|
||||
#### Notes
|
||||
|
||||
Documentation has moved to the documentation repository at
|
||||
`github.com/docker/docker.github.io/tree/master/registry`
|
||||
|
||||
The registry is go 1.7 compliant, and passes newer, more restrictive `lint` and `vet` ing.
|
||||
|
||||
|
||||
## 2.5.0 (2016-06-14)
|
||||
|
||||
#### Storage
|
||||
- Ensure uploads directory is cleaned after upload is committed
|
||||
- Add ability to cap concurrent operations in filesystem driver
|
||||
- S3: Add 'us-gov-west-1' to the valid region list
|
||||
- Swift: Handle ceph not returning Last-Modified header for HEAD requests
|
||||
- Add redirect middleware
|
||||
|
||||
#### Registry
|
||||
- Add support for blobAccessController middleware
|
||||
- Add support for layers from foreign sources
|
||||
- Remove signature store
|
||||
- Add support for Let's Encrypt
|
||||
- Correct yaml key names in configuration
|
||||
|
||||
#### Client
|
||||
- Add option to get content digest from manifest get
|
||||
|
||||
#### Spec
|
||||
- Update the auth spec scope grammar to reflect the fact that hostnames are optionally supported
|
||||
- Clarify API documentation around catalog fetch behavior
|
||||
|
||||
#### API
|
||||
- Support returning HTTP 429 (Too Many Requests)
|
||||
|
||||
#### Documentation
|
||||
- Update auth documentation examples to show "expires in" as int
|
||||
|
||||
#### Docker Image
|
||||
- Use Alpine Linux as base image
|
||||
|
||||
|
140
gateway/vendor/github.com/docker/distribution/CONTRIBUTING.md
generated
vendored
140
gateway/vendor/github.com/docker/distribution/CONTRIBUTING.md
generated
vendored
@ -1,140 +0,0 @@
|
||||
# Contributing to the registry
|
||||
|
||||
## Before reporting an issue...
|
||||
|
||||
### If your problem is with...
|
||||
|
||||
- automated builds
|
||||
- your account on the [Docker Hub](https://hub.docker.com/)
|
||||
- any other [Docker Hub](https://hub.docker.com/) issue
|
||||
|
||||
Then please do not report your issue here - you should instead report it to [https://support.docker.com](https://support.docker.com)
|
||||
|
||||
### If you...
|
||||
|
||||
- need help setting up your registry
|
||||
- can't figure out something
|
||||
- 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:
|
||||
|
||||
- irc: #docker-distribution on freenode
|
||||
- mailing-list: <distribution@dockerproject.org> or https://groups.google.com/a/dockerproject.org/forum/#!forum/distribution
|
||||
|
||||
## Reporting an issue properly
|
||||
|
||||
By following these simple rules you will get better and faster feedback on your issue.
|
||||
|
||||
- search the bugtracker for an already reported issue
|
||||
|
||||
### 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 refrain from adding "same thing here" or "+1" comments
|
||||
- you don't need to comment on an issue to get notified of updates: just hit the "subscribe" button
|
||||
- comment if you have some new, technical and relevant information to add to the case
|
||||
- __DO NOT__ comment on closed issues or merged PRs. If you think you have a related problem, open up a new issue and reference the PR or issue.
|
||||
|
||||
### If you have not found an existing issue that describes your problem:
|
||||
|
||||
1. create a new issue, with a succinct title that describes your issue:
|
||||
- bad title: "It doesn't work with my docker"
|
||||
- good title: "Private registry push fail: 400 error with E_INVALID_DIGEST"
|
||||
2. copy the output of:
|
||||
- `docker version`
|
||||
- `docker info`
|
||||
- `docker exec <registry-container> registry -version`
|
||||
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)
|
||||
5. reproduce your problem and get your docker daemon logs showing the error
|
||||
6. if relevant, copy your registry logs that show the error
|
||||
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
|
||||
|
||||
## Contributing a patch for a known bug, or a small correction
|
||||
|
||||
You should follow the basic GitHub workflow:
|
||||
|
||||
1. fork
|
||||
2. commit a change
|
||||
3. make sure the tests pass
|
||||
4. PR
|
||||
|
||||
Additionally, you must [sign your commits](https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work). It's very simple:
|
||||
|
||||
- configure your name with git: `git config user.name "Real Name" && git config user.email mail@example.com`
|
||||
- sign your commits using `-s`: `git commit -s -m "My commit"`
|
||||
|
||||
Some simple rules to ensure quick merge:
|
||||
|
||||
- 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
|
||||
- if you need to amend your PR following comments, please squash instead of adding more commits
|
||||
|
||||
## Contributing new features
|
||||
|
||||
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.
|
||||
|
||||
If this is a major new feature, you should then submit a proposal that describes your technical solution and reasoning.
|
||||
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).
|
||||
|
||||
Your PR will be reviewed by the community, then ultimately by the project maintainers, before being merged.
|
||||
|
||||
It's mandatory to:
|
||||
|
||||
- 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)
|
||||
- address maintainers' comments and modify your submission accordingly
|
||||
- write tests for any new code
|
||||
|
||||
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.
|
||||
|
||||
Have a look at a great, successful contribution: the [Swift driver PR](https://github.com/docker/distribution/pull/493)
|
||||
|
||||
## Coding Style
|
||||
|
||||
Unless explicitly stated, we follow all coding guidelines from the Go
|
||||
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
|
||||
guidelines. We are not looking for a massive PR that fixes this, since that
|
||||
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.
|
18
gateway/vendor/github.com/docker/distribution/Dockerfile
generated
vendored
18
gateway/vendor/github.com/docker/distribution/Dockerfile
generated
vendored
@ -1,18 +0,0 @@
|
||||
FROM golang:1.7-alpine
|
||||
|
||||
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
||||
ENV DOCKER_BUILDTAGS include_oss include_gcs
|
||||
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache make git
|
||||
|
||||
WORKDIR $DISTRIBUTION_DIR
|
||||
COPY . $DISTRIBUTION_DIR
|
||||
COPY cmd/registry/config-dev.yml /etc/docker/registry/config.yml
|
||||
|
||||
RUN make PREFIX=/go clean binaries
|
||||
|
||||
VOLUME ["/var/lib/registry"]
|
||||
EXPOSE 5000
|
||||
ENTRYPOINT ["registry"]
|
||||
CMD ["serve", "/etc/docker/registry/config.yml"]
|
58
gateway/vendor/github.com/docker/distribution/MAINTAINERS
generated
vendored
58
gateway/vendor/github.com/docker/distribution/MAINTAINERS
generated
vendored
@ -1,58 +0,0 @@
|
||||
# Distribution maintainers file
|
||||
#
|
||||
# This file describes who runs the docker/distribution project and how.
|
||||
# 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.
|
||||
# To extract its contents programmatically, use any TOML-compliant parser.
|
||||
#
|
||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||
#
|
||||
[Org]
|
||||
[Org."Core maintainers"]
|
||||
people = [
|
||||
"aaronlehmann",
|
||||
"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"
|
99
gateway/vendor/github.com/docker/distribution/Makefile
generated
vendored
99
gateway/vendor/github.com/docker/distribution/Makefile
generated
vendored
@ -1,99 +0,0 @@
|
||||
# Set an output prefix, which is the local directory if not specified
|
||||
PREFIX?=$(shell pwd)
|
||||
|
||||
|
||||
# Used to populate version variable in main package.
|
||||
VERSION=$(shell git describe --match 'v[0-9]*' --dirty='.m' --always)
|
||||
|
||||
# Allow turning off function inlining and variable registerization
|
||||
ifeq (${DISABLE_OPTIMIZATION},true)
|
||||
GO_GCFLAGS=-gcflags "-N -l"
|
||||
VERSION:="$(VERSION)-noopt"
|
||||
endif
|
||||
|
||||
GO_LDFLAGS=-ldflags "-X `go list ./version`.Version=$(VERSION)"
|
||||
|
||||
.PHONY: all build binaries clean dep-restore dep-save dep-validate fmt lint test test-full vet
|
||||
.DEFAULT: all
|
||||
all: fmt vet lint build test binaries
|
||||
|
||||
AUTHORS: .mailmap .git/HEAD
|
||||
git log --format='%aN <%aE>' | sort -fu > $@
|
||||
|
||||
# This only needs to be generated by hand when cutting full releases.
|
||||
version/version.go:
|
||||
./version/version.sh > $@
|
||||
|
||||
# Required for go 1.5 to build
|
||||
GO15VENDOREXPERIMENT := 1
|
||||
|
||||
# Go files
|
||||
GOFILES=$(shell find . -type f -name '*.go')
|
||||
|
||||
# Package list
|
||||
PKGS=$(shell go list -tags "${DOCKER_BUILDTAGS}" ./... | grep -v ^github.com/docker/distribution/vendor/)
|
||||
|
||||
# Resolving binary dependencies for specific targets
|
||||
GOLINT=$(shell which golint || echo '')
|
||||
VNDR=$(shell which vndr || echo '')
|
||||
|
||||
${PREFIX}/bin/registry: $(GOFILES)
|
||||
@echo "+ $@"
|
||||
@go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/registry
|
||||
|
||||
${PREFIX}/bin/digest: $(GOFILES)
|
||||
@echo "+ $@"
|
||||
@go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/digest
|
||||
|
||||
${PREFIX}/bin/registry-api-descriptor-template: $(GOFILES)
|
||||
@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
|
||||
./bin/registry-api-descriptor-template $< > $@
|
||||
|
||||
vet:
|
||||
@echo "+ $@"
|
||||
@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:
|
||||
@echo "+ $@"
|
||||
@go build -tags "${DOCKER_BUILDTAGS}" -v ${GO_LDFLAGS} $(PKGS)
|
||||
|
||||
test:
|
||||
@echo "+ $@"
|
||||
@go test -test.short -tags "${DOCKER_BUILDTAGS}" $(PKGS)
|
||||
|
||||
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
|
||||
@mv .vendor.bak vendor
|
44
gateway/vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md
generated
vendored
44
gateway/vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md
generated
vendored
@ -1,44 +0,0 @@
|
||||
## Registry Release Checklist
|
||||
|
||||
10. Compile release notes detailing features and since the last release.
|
||||
|
||||
Update the `CHANGELOG.md` file and create a PR to master with the updates.
|
||||
Once that PR has been approved by maintainers the change may be cherry-picked
|
||||
to the release branch (new release branches may be forked from this commit).
|
||||
|
||||
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, use previous tags as a guide for formatting consistently. Run
|
||||
`git tag -s vx.y.z[-rcn]` to create tag and `git -v vx.y.z[-rcn]` to verify tag,
|
||||
check comment and correct commit hash.
|
||||
|
||||
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.
|
||||
|
267
gateway/vendor/github.com/docker/distribution/ROADMAP.md
generated
vendored
267
gateway/vendor/github.com/docker/distribution/ROADMAP.md
generated
vendored
@ -1,267 +0,0 @@
|
||||
# Roadmap
|
||||
|
||||
The Distribution Project consists of several components, some of which are
|
||||
still being defined. This document defines the high-level goals of the
|
||||
project, identifies the current components, and defines the release-
|
||||
relationship to the Docker Platform.
|
||||
|
||||
* [Distribution Goals](#distribution-goals)
|
||||
* [Distribution Components](#distribution-components)
|
||||
* [Project Planning](#project-planning): release-relationship to the Docker Platform.
|
||||
|
||||
This road map is a living document, providing an overview of the goals and
|
||||
considerations made in respect of the future of the project.
|
||||
|
||||
## Distribution Goals
|
||||
|
||||
- Replace the existing [docker registry](github.com/docker/docker-registry)
|
||||
implementation as the primary implementation.
|
||||
- Replace the existing push and pull code in the docker engine with the
|
||||
distribution package.
|
||||
- Define a strong data model for distributing docker images
|
||||
- Provide a flexible distribution tool kit for use in the docker platform
|
||||
- Unlock new distribution models
|
||||
|
||||
## Distribution Components
|
||||
|
||||
Components of the Distribution Project are managed via github [milestones](https://github.com/docker/distribution/milestones). Upcoming
|
||||
features and bugfixes for a component will be added to the relevant milestone. If a feature or
|
||||
bugfix is not part of a milestone, it is currently unscheduled for
|
||||
implementation.
|
||||
|
||||
* [Registry](#registry)
|
||||
* [Distribution Package](#distribution-package)
|
||||
|
||||
***
|
||||
|
||||
### Registry
|
||||
|
||||
The new Docker registry is the main portion of the distribution repository.
|
||||
Registry 2.0 is the first release of the next-generation registry. This was
|
||||
primarily focused on implementing the [new registry
|
||||
API](https://github.com/docker/distribution/blob/master/docs/spec/api.md),
|
||||
with a focus on security and performance.
|
||||
|
||||
Following from the Distribution project goals above, we have a set of goals
|
||||
for registry v2 that we would like to follow in the design. New features
|
||||
should be compared against these goals.
|
||||
|
||||
#### Data Storage and Distribution First
|
||||
|
||||
The registry's first goal is to provide a reliable, consistent storage
|
||||
location for Docker images. The registry should only provide the minimal
|
||||
amount of indexing required to fetch image data and no more.
|
||||
|
||||
This means we should be selective in new features and API additions, including
|
||||
those that may require expensive, ever growing indexes. Requests should be
|
||||
servable in "constant time".
|
||||
|
||||
#### Content Addressability
|
||||
|
||||
All data objects used in the registry API should be content addressable.
|
||||
Content identifiers should be secure and verifiable. This provides a secure,
|
||||
reliable base from which to build more advanced content distribution systems.
|
||||
|
||||
#### Content Agnostic
|
||||
|
||||
In the past, changes to the image format would require large changes in Docker
|
||||
and the Registry. By decoupling the distribution and image format, we can
|
||||
allow the formats to progress without having to coordinate between the two.
|
||||
This means that we should be focused on decoupling Docker from the registry
|
||||
just as much as decoupling the registry from Docker. Such an approach will
|
||||
allow us to unlock new distribution models that haven't been possible before.
|
||||
|
||||
We can take this further by saying that the new registry should be content
|
||||
agnostic. The registry provides a model of names, tags, manifests and content
|
||||
addresses and that model can be used to work with content.
|
||||
|
||||
#### Simplicity
|
||||
|
||||
The new registry should be closer to a microservice component than its
|
||||
predecessor. This means it should have a narrower API and a low number of
|
||||
service dependencies. It should be easy to deploy.
|
||||
|
||||
This means that other solutions should be explored before changing the API or
|
||||
adding extra dependencies. If functionality is required, can it be added as an
|
||||
extension or companion service.
|
||||
|
||||
#### Extensibility
|
||||
|
||||
The registry should provide extension points to add functionality. By keeping
|
||||
the scope narrow, but providing the ability to add functionality.
|
||||
|
||||
Features like search, indexing, synchronization and registry explorers fall
|
||||
into this category. No such feature should be added unless we've found it
|
||||
impossible to do through an extension.
|
||||
|
||||
#### Active Feature Discussions
|
||||
|
||||
The following are feature discussions that are currently active.
|
||||
|
||||
If you don't see your favorite, unimplemented feature, feel free to contact us
|
||||
via IRC or the mailing list and we can talk about adding it. The goal here is
|
||||
to make sure that new features go through a rigid design process before
|
||||
landing in the registry.
|
||||
|
||||
##### Proxying to other Registries
|
||||
|
||||
A _pull-through caching_ mode exists for the registry, but is restricted from
|
||||
within the docker client to only mirror the official Docker Hub. This functionality
|
||||
can be expanded when image provenance has been specified and implemented in the
|
||||
distribution project.
|
||||
|
||||
##### Metadata storage
|
||||
|
||||
Metadata for the registry is currently stored with the manifest and layer data on
|
||||
the storage backend. While this is a big win for simplicity and reliably maintaining
|
||||
state, it comes with the cost of consistency and high latency. The mutable registry
|
||||
metadata operations should be abstracted behind an API which will allow ACID compliant
|
||||
storage systems to handle metadata.
|
||||
|
||||
##### Peer to Peer transfer
|
||||
|
||||
Discussion has started here: https://docs.google.com/document/d/1rYDpSpJiQWmCQy8Cuiaa3NH-Co33oK_SC9HeXYo87QA/edit
|
||||
|
||||
##### Indexing, Search and Discovery
|
||||
|
||||
The original registry provided some implementation of search for use with
|
||||
private registries. Support has been elided from V2 since we'd like to both
|
||||
decouple search functionality from the registry. The makes the registry
|
||||
simpler to deploy, especially in use cases where search is not needed, and
|
||||
let's us decouple the image format from the registry.
|
||||
|
||||
There are explorations into using the catalog API and notification system to
|
||||
build external indexes. The current line of thought is that we will define a
|
||||
common search API to index and query docker images. Such a system could be run
|
||||
as a companion to a registry or set of registries to power discovery.
|
||||
|
||||
The main issue with search and discovery is that there are so many ways to
|
||||
accomplish it. There are two aspects to this project. The first is deciding on
|
||||
how it will be done, including an API definition that can work with changing
|
||||
data formats. The second is the process of integrating with `docker search`.
|
||||
We expect that someone attempts to address the problem with the existing tools
|
||||
and propose it as a standard search API or uses it to inform a standardization
|
||||
process. Once this has been explored, we integrate with the docker client.
|
||||
|
||||
Please see the following for more detail:
|
||||
|
||||
- https://github.com/docker/distribution/issues/206
|
||||
|
||||
##### Deletes
|
||||
|
||||
> __NOTE:__ Deletes are a much asked for feature. Before requesting this
|
||||
feature or participating in discussion, we ask that you read this section in
|
||||
full and understand the problems behind deletes.
|
||||
|
||||
While, at first glance, implementing deleting seems simple, there are a number
|
||||
mitigating factors that make many solutions not ideal or even pathological in
|
||||
the context of a registry. The following paragraph discuss the background and
|
||||
approaches that could be applied to arrive at a solution.
|
||||
|
||||
The goal of deletes in any system is to remove unused or unneeded data. Only
|
||||
data requested for deletion should be removed and no other data. Removing
|
||||
unintended data is worse than _not_ removing data that was requested for
|
||||
removal but ideally, both are supported. Generally, according to this rule, we
|
||||
err on holding data longer than needed, ensuring that it is only removed when
|
||||
we can be certain that it can be removed. With the current behavior, we opt to
|
||||
hold onto the data forever, ensuring that data cannot be incorrectly removed.
|
||||
|
||||
To understand the problems with implementing deletes, one must understand the
|
||||
data model. All registry data is stored in a filesystem layout, implemented on
|
||||
a "storage driver", effectively a _virtual file system_ (VFS). The storage
|
||||
system must assume that this VFS layer will be eventually consistent and has
|
||||
poor read- after-write consistency, since this is the lower common denominator
|
||||
among the storage drivers. This is mitigated by writing values in reverse-
|
||||
dependent order, but makes wider transactional operations unsafe.
|
||||
|
||||
Layered on the VFS model is a content-addressable _directed, acyclic graph_
|
||||
(DAG) made up of blobs. Manifests reference layers. Tags reference manifests.
|
||||
Since the same data can be referenced by multiple manifests, we only store
|
||||
data once, even if it is in different repositories. Thus, we have a set of
|
||||
blobs, referenced by tags and manifests. If we want to delete a blob we need
|
||||
to be certain that it is no longer referenced by another manifest or tag. When
|
||||
we delete a manifest, we also can try to delete the referenced blobs. Deciding
|
||||
whether or not a blob has an active reference is the crux of the problem.
|
||||
|
||||
Conceptually, deleting a manifest and its resources is quite simple. Just find
|
||||
all the manifests, enumerate the referenced blobs and delete the blobs not in
|
||||
that set. An astute observer will recognize this as a garbage collection
|
||||
problem. As with garbage collection in programming languages, this is very
|
||||
simple when one always has a consistent view. When one adds parallelism and an
|
||||
inconsistent view of data, it becomes very challenging.
|
||||
|
||||
A simple example can demonstrate this. Let's say we are deleting a manifest
|
||||
_A_ in one process. We scan the manifest and decide that all the blobs are
|
||||
ready for deletion. Concurrently, we have another process accepting a new
|
||||
manifest _B_ referencing one or more blobs from the manifest _A_. Manifest _B_
|
||||
is accepted and all the blobs are considered present, so the operation
|
||||
proceeds. The original process then deletes the referenced blobs, assuming
|
||||
they were unreferenced. The manifest _B_, which we thought had all of its data
|
||||
present, can no longer be served by the registry, since the dependent data has
|
||||
been deleted.
|
||||
|
||||
Deleting data from the registry safely requires some way to coordinate this
|
||||
operation. The following approaches are being considered:
|
||||
|
||||
- _Reference Counting_ - Maintain a count of references to each blob. This is
|
||||
challenging for a number of reasons: 1. maintaining a consistent consensus
|
||||
of reference counts across a set of Registries and 2. Building the initial
|
||||
list of reference counts for an existing registry. These challenges can be
|
||||
met with a consensus protocol like Paxos or Raft in the first case and a
|
||||
necessary but simple scan in the second..
|
||||
- _Lock the World GC_ - Halt all writes to the data store. Walk the data store
|
||||
and find all blob references. Delete all unreferenced blobs. This approach
|
||||
is very simple but requires disabling writes for a period of time while the
|
||||
service reads all data. This is slow and expensive but very accurate and
|
||||
effective.
|
||||
- _Generational GC_ - Do something similar to above but instead of blocking
|
||||
writes, writes are sent to another storage backend while reads are broadcast
|
||||
to the new and old backends. GC is then performed on the read-only portion.
|
||||
Because writes land in the new backend, the data in the read-only section
|
||||
can be safely deleted. The main drawbacks of this approach are complexity
|
||||
and coordination.
|
||||
- _Centralized Oracle_ - Using a centralized, transactional database, we can
|
||||
know exactly which data is referenced at any given time. This avoids
|
||||
coordination problem by managing this data in a single location. We trade
|
||||
off metadata scalability for simplicity and performance. This is a very good
|
||||
option for most registry deployments. This would create a bottleneck for
|
||||
registry metadata. However, metadata is generally not the main bottleneck
|
||||
when serving images.
|
||||
|
||||
Please let us know if other solutions exist that we have yet to enumerate.
|
||||
Note that for any approach, implementation is a massive consideration. For
|
||||
example, a mark-sweep based solution may seem simple but the amount of work in
|
||||
coordination offset the extra work it might take to build a _Centralized
|
||||
Oracle_. We'll accept proposals for any solution but please coordinate with us
|
||||
before dropping code.
|
||||
|
||||
At this time, we have traded off simplicity and ease of deployment for disk
|
||||
space. Simplicity and ease of deployment tend to reduce developer involvement,
|
||||
which is currently the most expensive resource in software engineering. Taking
|
||||
on any solution for deletes will greatly effect these factors, trading off
|
||||
very cheap disk space for a complex deployment and operational story.
|
||||
|
||||
Please see the following issues for more detail:
|
||||
|
||||
- https://github.com/docker/distribution/issues/422
|
||||
- https://github.com/docker/distribution/issues/461
|
||||
- https://github.com/docker/distribution/issues/462
|
||||
|
||||
### Distribution Package
|
||||
|
||||
At its core, the Distribution Project is a set of Go packages that make up
|
||||
Distribution Components. At this time, most of these packages make up the
|
||||
Registry implementation.
|
||||
|
||||
The package itself is considered unstable. If you're using it, please take care to vendor the dependent version.
|
||||
|
||||
For feature additions, please see the Registry section. In the future, we may break out a
|
||||
separate Roadmap for distribution-specific features that apply to more than
|
||||
just the registry.
|
||||
|
||||
***
|
||||
|
||||
### Project Planning
|
||||
|
||||
An [Open-Source Planning Process](https://github.com/docker/distribution/wiki/Open-Source-Planning-Process) is used to define the Roadmap. [Project Pages](https://github.com/docker/distribution/wiki) define the goals for each Milestone and identify current progress.
|
||||
|
92
gateway/vendor/github.com/docker/distribution/circle.yml
generated
vendored
92
gateway/vendor/github.com/docker/distribution/circle.yml
generated
vendored
@ -1,92 +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.7 --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 origin/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/" | 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
|
||||
|
||||
# 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
|
97
gateway/vendor/github.com/docker/distribution/cmd/digest/main.go
generated
vendored
97
gateway/vendor/github.com/docker/distribution/cmd/digest/main.go
generated
vendored
@ -1,97 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/docker/distribution/version"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
algorithm = digest.Canonical
|
||||
showVersion bool
|
||||
)
|
||||
|
||||
type job struct {
|
||||
name string
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.Var(&algorithm, "a", "select the digest algorithm (shorthand)")
|
||||
flag.Var(&algorithm, "algorithm", "select the digest algorithm")
|
||||
flag.BoolVar(&showVersion, "version", false, "show the version and exit")
|
||||
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix(os.Args[0] + ": ")
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s [files...]\n", os.Args[0])
|
||||
fmt.Fprint(os.Stderr, `
|
||||
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
|
||||
be calculated.
|
||||
|
||||
`)
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func unsupported() {
|
||||
log.Fatalf("unsupported digest algorithm: %v", algorithm)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var jobs []job
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if showVersion {
|
||||
version.PrintVersion()
|
||||
return
|
||||
}
|
||||
|
||||
var fail bool // if we fail on one item, foul the exit code
|
||||
if flag.NArg() > 0 {
|
||||
for _, path := range flag.Args() {
|
||||
fp, err := os.Open(path)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s: %v", path, err)
|
||||
fail = true
|
||||
continue
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
jobs = append(jobs, job{name: path, reader: fp})
|
||||
}
|
||||
} else {
|
||||
// just read stdin
|
||||
jobs = append(jobs, job{name: "-", reader: os.Stdin})
|
||||
}
|
||||
|
||||
digestFn := algorithm.FromReader
|
||||
|
||||
if !algorithm.Available() {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
for _, job := range jobs {
|
||||
dgst, err := digestFn(job.reader)
|
||||
if err != nil {
|
||||
log.Printf("%s: %v", job.name, err)
|
||||
fail = true
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%v\t%s\n", dgst, job.name)
|
||||
}
|
||||
|
||||
if fail {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
131
gateway/vendor/github.com/docker/distribution/cmd/registry-api-descriptor-template/main.go
generated
vendored
131
gateway/vendor/github.com/docker/distribution/cmd/registry-api-descriptor-template/main.go
generated
vendored
@ -1,131 +0,0 @@
|
||||
// registry-api-descriptor-template uses the APIDescriptor defined in the
|
||||
// api/v2 package to execute templates passed to the command line.
|
||||
//
|
||||
// For example, to generate a new API specification, one would execute the
|
||||
// following command from the repo root:
|
||||
//
|
||||
// $ registry-api-descriptor-template docs/spec/api.md.tmpl > docs/spec/api.md
|
||||
//
|
||||
// The templates are passed in the api/v2.APIDescriptor object. Please see the
|
||||
// package documentation for fields available on that object. The template
|
||||
// syntax is from Go's standard library text/template package. For information
|
||||
// on Go's template syntax, please see golang.org/pkg/text/template.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
)
|
||||
|
||||
var spaceRegex = regexp.MustCompile(`\n\s*`)
|
||||
|
||||
func main() {
|
||||
|
||||
if len(os.Args) != 2 {
|
||||
log.Fatalln("please specify a template to execute.")
|
||||
}
|
||||
|
||||
path := os.Args[1]
|
||||
filename := filepath.Base(path)
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"removenewlines": func(s string) string {
|
||||
return spaceRegex.ReplaceAllString(s, " ")
|
||||
},
|
||||
"statustext": http.StatusText,
|
||||
"prettygorilla": prettyGorillaMuxPath,
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New(filename).Funcs(funcMap).ParseFiles(path))
|
||||
|
||||
data := struct {
|
||||
RouteDescriptors []v2.RouteDescriptor
|
||||
ErrorDescriptors []errcode.ErrorDescriptor
|
||||
}{
|
||||
RouteDescriptors: v2.APIDescriptor.RouteDescriptors,
|
||||
ErrorDescriptors: append(errcode.GetErrorCodeGroup("registry.api.v2"),
|
||||
// The following are part of the specification but provided by errcode default.
|
||||
errcode.ErrorCodeUnauthorized.Descriptor(),
|
||||
errcode.ErrorCodeDenied.Descriptor(),
|
||||
errcode.ErrorCodeUnsupported.Descriptor()),
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(os.Stdout, data); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
// prettyGorillaMuxPath removes the regular expressions from a gorilla/mux
|
||||
// route string, making it suitable for documentation.
|
||||
func prettyGorillaMuxPath(s string) string {
|
||||
// Stateful parser that removes regular expressions from gorilla
|
||||
// routes. It correctly handles balanced bracket pairs.
|
||||
|
||||
var output string
|
||||
var label string
|
||||
var level int
|
||||
|
||||
start:
|
||||
if s[0] == '{' {
|
||||
s = s[1:]
|
||||
level++
|
||||
goto capture
|
||||
}
|
||||
|
||||
output += string(s[0])
|
||||
s = s[1:]
|
||||
|
||||
goto end
|
||||
capture:
|
||||
switch s[0] {
|
||||
case '{':
|
||||
level++
|
||||
case '}':
|
||||
level--
|
||||
|
||||
if level == 0 {
|
||||
s = s[1:]
|
||||
goto label
|
||||
}
|
||||
case ':':
|
||||
s = s[1:]
|
||||
goto skip
|
||||
default:
|
||||
label += string(s[0])
|
||||
}
|
||||
s = s[1:]
|
||||
goto capture
|
||||
skip:
|
||||
switch s[0] {
|
||||
case '{':
|
||||
level++
|
||||
case '}':
|
||||
level--
|
||||
}
|
||||
s = s[1:]
|
||||
|
||||
if level == 0 {
|
||||
goto label
|
||||
}
|
||||
|
||||
goto skip
|
||||
label:
|
||||
if label != "" {
|
||||
output += "<" + label + ">"
|
||||
label = ""
|
||||
}
|
||||
end:
|
||||
if s != "" {
|
||||
goto start
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
}
|
55
gateway/vendor/github.com/docker/distribution/cmd/registry/config-cache.yml
generated
vendored
55
gateway/vendor/github.com/docker/distribution/cmd/registry/config-cache.yml
generated
vendored
@ -1,55 +0,0 @@
|
||||
version: 0.1
|
||||
log:
|
||||
level: debug
|
||||
fields:
|
||||
service: registry
|
||||
environment: development
|
||||
storage:
|
||||
cache:
|
||||
blobdescriptor: redis
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry-cache
|
||||
maintenance:
|
||||
uploadpurging:
|
||||
enabled: false
|
||||
http:
|
||||
addr: :5000
|
||||
secret: asecretforlocaldevelopment
|
||||
debug:
|
||||
addr: localhost:5001
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
pool:
|
||||
maxidle: 16
|
||||
maxactive: 64
|
||||
idletimeout: 300s
|
||||
dialtimeout: 10ms
|
||||
readtimeout: 10ms
|
||||
writetimeout: 10ms
|
||||
notifications:
|
||||
endpoints:
|
||||
- name: local-8082
|
||||
url: http://localhost:5003/callback
|
||||
headers:
|
||||
Authorization: [Bearer <an example token>]
|
||||
timeout: 1s
|
||||
threshold: 10
|
||||
backoff: 1s
|
||||
disabled: true
|
||||
- name: local-8083
|
||||
url: http://localhost:8083/callback
|
||||
timeout: 1s
|
||||
threshold: 10
|
||||
backoff: 1s
|
||||
disabled: true
|
||||
proxy:
|
||||
remoteurl: https://registry-1.docker.io
|
||||
username: username
|
||||
password: password
|
||||
health:
|
||||
storagedriver:
|
||||
enabled: true
|
||||
interval: 10s
|
||||
threshold: 3
|
66
gateway/vendor/github.com/docker/distribution/cmd/registry/config-dev.yml
generated
vendored
66
gateway/vendor/github.com/docker/distribution/cmd/registry/config-dev.yml
generated
vendored
@ -1,66 +0,0 @@
|
||||
version: 0.1
|
||||
log:
|
||||
level: debug
|
||||
fields:
|
||||
service: registry
|
||||
environment: development
|
||||
hooks:
|
||||
- type: mail
|
||||
disabled: true
|
||||
levels:
|
||||
- panic
|
||||
options:
|
||||
smtp:
|
||||
addr: mail.example.com:25
|
||||
username: mailuser
|
||||
password: password
|
||||
insecure: true
|
||||
from: sender@example.com
|
||||
to:
|
||||
- errors@example.com
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: redis
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
maintenance:
|
||||
uploadpurging:
|
||||
enabled: false
|
||||
http:
|
||||
addr: :5000
|
||||
debug:
|
||||
addr: localhost:5001
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
redis:
|
||||
addr: localhost:6379
|
||||
pool:
|
||||
maxidle: 16
|
||||
maxactive: 64
|
||||
idletimeout: 300s
|
||||
dialtimeout: 10ms
|
||||
readtimeout: 10ms
|
||||
writetimeout: 10ms
|
||||
notifications:
|
||||
endpoints:
|
||||
- name: local-5003
|
||||
url: http://localhost:5003/callback
|
||||
headers:
|
||||
Authorization: [Bearer <an example token>]
|
||||
timeout: 1s
|
||||
threshold: 10
|
||||
backoff: 1s
|
||||
disabled: true
|
||||
- name: local-8083
|
||||
url: http://localhost:8083/callback
|
||||
timeout: 1s
|
||||
threshold: 10
|
||||
backoff: 1s
|
||||
disabled: true
|
||||
health:
|
||||
storagedriver:
|
||||
enabled: true
|
||||
interval: 10s
|
||||
threshold: 3
|
18
gateway/vendor/github.com/docker/distribution/cmd/registry/config-example.yml
generated
vendored
18
gateway/vendor/github.com/docker/distribution/cmd/registry/config-example.yml
generated
vendored
@ -1,18 +0,0 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
health:
|
||||
storagedriver:
|
||||
enabled: true
|
||||
interval: 10s
|
||||
threshold: 3
|
25
gateway/vendor/github.com/docker/distribution/cmd/registry/main.go
generated
vendored
25
gateway/vendor/github.com/docker/distribution/cmd/registry/main.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "net/http/pprof"
|
||||
|
||||
"github.com/docker/distribution/registry"
|
||||
_ "github.com/docker/distribution/registry/auth/htpasswd"
|
||||
_ "github.com/docker/distribution/registry/auth/silly"
|
||||
_ "github.com/docker/distribution/registry/auth/token"
|
||||
_ "github.com/docker/distribution/registry/proxy"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/azure"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/filesystem"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/gcs"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/inmemory"
|
||||
_ "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/oss"
|
||||
_ "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"
|
||||
)
|
||||
|
||||
func main() {
|
||||
registry.RootCmd.Execute()
|
||||
}
|
647
gateway/vendor/github.com/docker/distribution/configuration/configuration.go
generated
vendored
647
gateway/vendor/github.com/docker/distribution/configuration/configuration.go
generated
vendored
@ -1,647 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Configuration is a versioned registry configuration, intended to be provided by a yaml file, and
|
||||
// optionally modified by environment variables.
|
||||
//
|
||||
// Note that yaml field names should never include _ characters, since this is the separator used
|
||||
// in environment variable names.
|
||||
type Configuration struct {
|
||||
// Version is the version which defines the format of the rest of the configuration
|
||||
Version Version `yaml:"version"`
|
||||
|
||||
// Log supports setting various parameters related to the logging
|
||||
// subsystem.
|
||||
Log struct {
|
||||
// AccessLog configures access logging.
|
||||
AccessLog struct {
|
||||
// Disabled disables access logging.
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
} `yaml:"accesslog,omitempty"`
|
||||
|
||||
// Level is the granularity at which registry operations are logged.
|
||||
Level Loglevel `yaml:"level"`
|
||||
|
||||
// Formatter overrides the default formatter with another. Options
|
||||
// include "text", "json" and "logstash".
|
||||
Formatter string `yaml:"formatter,omitempty"`
|
||||
|
||||
// Fields allows users to specify static string fields to include in
|
||||
// the logger context.
|
||||
Fields map[string]interface{} `yaml:"fields,omitempty"`
|
||||
|
||||
// Hooks allows users to configure the log hooks, to enabling the
|
||||
// sequent handling behavior, when defined levels of log message emit.
|
||||
Hooks []LogHook `yaml:"hooks,omitempty"`
|
||||
}
|
||||
|
||||
// Loglevel is the level at which registry operations are logged. This is
|
||||
// deprecated. Please use Log.Level in the future.
|
||||
Loglevel Loglevel `yaml:"loglevel,omitempty"`
|
||||
|
||||
// Storage is the configuration for the registry's storage driver
|
||||
Storage Storage `yaml:"storage"`
|
||||
|
||||
// Auth allows configuration of various authorization methods that may be
|
||||
// used to gate requests.
|
||||
Auth Auth `yaml:"auth,omitempty"`
|
||||
|
||||
// Middleware lists all middlewares to be used by the registry.
|
||||
Middleware map[string][]Middleware `yaml:"middleware,omitempty"`
|
||||
|
||||
// Reporting is the configuration for error reporting
|
||||
Reporting Reporting `yaml:"reporting,omitempty"`
|
||||
|
||||
// HTTP contains configuration parameters for the registry's http
|
||||
// interface.
|
||||
HTTP struct {
|
||||
// Addr specifies the bind address for the registry instance.
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
|
||||
// Net specifies the net portion of the bind address. A default empty value means tcp.
|
||||
Net string `yaml:"net,omitempty"`
|
||||
|
||||
// Host specifies an externally-reachable address for the registry, as a fully
|
||||
// qualified URL.
|
||||
Host string `yaml:"host,omitempty"`
|
||||
|
||||
Prefix string `yaml:"prefix,omitempty"`
|
||||
|
||||
// Secret specifies the secret key which HMAC tokens are created with.
|
||||
Secret string `yaml:"secret,omitempty"`
|
||||
|
||||
// RelativeURLs specifies that relative URLs should be returned in
|
||||
// Location headers
|
||||
RelativeURLs bool `yaml:"relativeurls,omitempty"`
|
||||
|
||||
// TLS instructs the http server to listen with a TLS configuration.
|
||||
// This only support simple tls configuration with a cert and key.
|
||||
// Mostly, this is useful for testing situations or simple deployments
|
||||
// that require tls. If more complex configurations are required, use
|
||||
// a proxy or make a proposal to add support here.
|
||||
TLS struct {
|
||||
// Certificate specifies the path to an x509 certificate file to
|
||||
// be used for TLS.
|
||||
Certificate string `yaml:"certificate,omitempty"`
|
||||
|
||||
// Key specifies the path to the x509 key file, which should
|
||||
// contain the private portion for the file specified in
|
||||
// Certificate.
|
||||
Key string `yaml:"key,omitempty"`
|
||||
|
||||
// Specifies the CA certs for client authentication
|
||||
// A file may contain multiple CA certificates encoded as PEM
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
|
||||
// LetsEncrypt is used to configuration setting up TLS through
|
||||
// Let's Encrypt instead of manually specifying certificate and
|
||||
// key. If a TLS certificate is specified, the Let's Encrypt
|
||||
// section will not be used.
|
||||
LetsEncrypt struct {
|
||||
// CacheFile specifies cache file to use for lets encrypt
|
||||
// certificates and keys.
|
||||
CacheFile string `yaml:"cachefile,omitempty"`
|
||||
|
||||
// Email is the email to use during Let's Encrypt registration
|
||||
Email string `yaml:"email,omitempty"`
|
||||
} `yaml:"letsencrypt,omitempty"`
|
||||
} `yaml:"tls,omitempty"`
|
||||
|
||||
// Headers is a set of headers to include in HTTP responses. A common
|
||||
// use case for this would be security headers such as
|
||||
// Strict-Transport-Security. The map keys are the header names, and
|
||||
// the values are the associated header payloads.
|
||||
Headers http.Header `yaml:"headers,omitempty"`
|
||||
|
||||
// Debug configures the http debug interface, if specified. This can
|
||||
// include services such as pprof, expvar and other data that should
|
||||
// not be exposed externally. Left disabled by default.
|
||||
Debug struct {
|
||||
// Addr specifies the bind address for the debug server.
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
} `yaml:"debug,omitempty"`
|
||||
|
||||
// HTTP2 configuration options
|
||||
HTTP2 struct {
|
||||
// Specifies whether the registry should disallow clients attempting
|
||||
// to connect via http2. If set to true, only http/1.1 is supported.
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
} `yaml:"http2,omitempty"`
|
||||
} `yaml:"http,omitempty"`
|
||||
|
||||
// Notifications specifies configuration about various endpoint to which
|
||||
// registry events are dispatched.
|
||||
Notifications Notifications `yaml:"notifications,omitempty"`
|
||||
|
||||
// Redis configures the redis pool available to the registry webapp.
|
||||
Redis struct {
|
||||
// Addr specifies the the redis instance available to the application.
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
|
||||
// Password string to use when making a connection.
|
||||
Password string `yaml:"password,omitempty"`
|
||||
|
||||
// DB specifies the database to connect to on the redis instance.
|
||||
DB int `yaml:"db,omitempty"`
|
||||
|
||||
DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` // timeout for connect
|
||||
ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` // timeout for reads of data
|
||||
WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` // timeout for writes of data
|
||||
|
||||
// Pool configures the behavior of the redis connection pool.
|
||||
Pool struct {
|
||||
// MaxIdle sets the maximum number of idle connections.
|
||||
MaxIdle int `yaml:"maxidle,omitempty"`
|
||||
|
||||
// MaxActive sets the maximum number of connections that should be
|
||||
// opened before blocking a connection request.
|
||||
MaxActive int `yaml:"maxactive,omitempty"`
|
||||
|
||||
// IdleTimeout sets the amount time to wait before closing
|
||||
// inactive connections.
|
||||
IdleTimeout time.Duration `yaml:"idletimeout,omitempty"`
|
||||
} `yaml:"pool,omitempty"`
|
||||
} `yaml:"redis,omitempty"`
|
||||
|
||||
Health Health `yaml:"health,omitempty"`
|
||||
|
||||
Proxy Proxy `yaml:"proxy,omitempty"`
|
||||
|
||||
// Compatibility is used for configurations of working with older or deprecated features.
|
||||
Compatibility struct {
|
||||
// Schema1 configures how schema1 manifests will be handled
|
||||
Schema1 struct {
|
||||
// TrustKey is the signing key to use for adding the signature to
|
||||
// schema1 manifests.
|
||||
TrustKey string `yaml:"signingkeyfile,omitempty"`
|
||||
} `yaml:"schema1,omitempty"`
|
||||
} `yaml:"compatibility,omitempty"`
|
||||
|
||||
// Validation configures validation options for the registry.
|
||||
Validation struct {
|
||||
// Enabled enables the other options in this section. This field is
|
||||
// deprecated in favor of Disabled.
|
||||
Enabled bool `yaml:"enabled,omitempty"`
|
||||
// Disabled disables the other options in this section.
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
// Manifests configures manifest validation.
|
||||
Manifests struct {
|
||||
// URLs configures validation for URLs in pushed manifests.
|
||||
URLs struct {
|
||||
// Allow specifies regular expressions (https://godoc.org/regexp/syntax)
|
||||
// that URLs in pushed manifests must match.
|
||||
Allow []string `yaml:"allow,omitempty"`
|
||||
// Deny specifies regular expressions (https://godoc.org/regexp/syntax)
|
||||
// that URLs in pushed manifests must not match.
|
||||
Deny []string `yaml:"deny,omitempty"`
|
||||
} `yaml:"urls,omitempty"`
|
||||
} `yaml:"manifests,omitempty"`
|
||||
} `yaml:"validation,omitempty"`
|
||||
|
||||
// Policy configures registry policy options.
|
||||
Policy struct {
|
||||
// Repository configures policies for repositories
|
||||
Repository struct {
|
||||
// Classes is a list of repository classes which the
|
||||
// registry allows content for. This class is matched
|
||||
// against the configuration media type inside uploaded
|
||||
// manifests. When non-empty, the registry will enforce
|
||||
// the class in authorized resources.
|
||||
Classes []string `yaml:"classes"`
|
||||
} `yaml:"repository,omitempty"`
|
||||
} `yaml:"policy,omitempty"`
|
||||
}
|
||||
|
||||
// LogHook is composed of hook Level and Type.
|
||||
// After hooks configuration, it can execute the next handling automatically,
|
||||
// when defined levels of log message emitted.
|
||||
// Example: hook can sending an email notification when error log happens in app.
|
||||
type LogHook struct {
|
||||
// Disable lets user select to enable hook or not.
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
|
||||
// Type allows user to select which type of hook handler they want.
|
||||
Type string `yaml:"type,omitempty"`
|
||||
|
||||
// Levels set which levels of log message will let hook executed.
|
||||
Levels []string `yaml:"levels,omitempty"`
|
||||
|
||||
// MailOptions allows user to configure email parameters.
|
||||
MailOptions MailOptions `yaml:"options,omitempty"`
|
||||
}
|
||||
|
||||
// MailOptions provides the configuration sections to user, for specific handler.
|
||||
type MailOptions struct {
|
||||
SMTP struct {
|
||||
// Addr defines smtp host address
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
|
||||
// Username defines user name to smtp host
|
||||
Username string `yaml:"username,omitempty"`
|
||||
|
||||
// Password defines password of login user
|
||||
Password string `yaml:"password,omitempty"`
|
||||
|
||||
// Insecure defines if smtp login skips the secure certification.
|
||||
Insecure bool `yaml:"insecure,omitempty"`
|
||||
} `yaml:"smtp,omitempty"`
|
||||
|
||||
// From defines mail sending address
|
||||
From string `yaml:"from,omitempty"`
|
||||
|
||||
// To defines mail receiving address
|
||||
To []string `yaml:"to,omitempty"`
|
||||
}
|
||||
|
||||
// FileChecker is a type of entry in the health section for checking files.
|
||||
type FileChecker struct {
|
||||
// Interval is the duration in between checks
|
||||
Interval time.Duration `yaml:"interval,omitempty"`
|
||||
// File is the path to check
|
||||
File string `yaml:"file,omitempty"`
|
||||
// Threshold is the number of times a check must fail to trigger an
|
||||
// unhealthy state
|
||||
Threshold int `yaml:"threshold,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPChecker is a type of entry in the health section for checking HTTP URIs.
|
||||
type HTTPChecker struct {
|
||||
// Timeout is the duration to wait before timing out the HTTP request
|
||||
Timeout time.Duration `yaml:"timeout,omitempty"`
|
||||
// StatusCode is the expected status code
|
||||
StatusCode int
|
||||
// Interval is the duration in between checks
|
||||
Interval time.Duration `yaml:"interval,omitempty"`
|
||||
// URI is the HTTP URI to check
|
||||
URI string `yaml:"uri,omitempty"`
|
||||
// Headers lists static headers that should be added to all requests
|
||||
Headers http.Header `yaml:"headers"`
|
||||
// Threshold is the number of times a check must fail to trigger an
|
||||
// unhealthy state
|
||||
Threshold int `yaml:"threshold,omitempty"`
|
||||
}
|
||||
|
||||
// TCPChecker is a type of entry in the health section for checking TCP servers.
|
||||
type TCPChecker struct {
|
||||
// Timeout is the duration to wait before timing out the TCP connection
|
||||
Timeout time.Duration `yaml:"timeout,omitempty"`
|
||||
// Interval is the duration in between checks
|
||||
Interval time.Duration `yaml:"interval,omitempty"`
|
||||
// Addr is the TCP address to check
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
// Threshold is the number of times a check must fail to trigger an
|
||||
// unhealthy state
|
||||
Threshold int `yaml:"threshold,omitempty"`
|
||||
}
|
||||
|
||||
// Health provides the configuration section for health checks.
|
||||
type Health struct {
|
||||
// FileCheckers is a list of paths to check
|
||||
FileCheckers []FileChecker `yaml:"file,omitempty"`
|
||||
// HTTPCheckers is a list of URIs to check
|
||||
HTTPCheckers []HTTPChecker `yaml:"http,omitempty"`
|
||||
// TCPCheckers is a list of URIs to check
|
||||
TCPCheckers []TCPChecker `yaml:"tcp,omitempty"`
|
||||
// StorageDriver configures a health check on the configured storage
|
||||
// driver
|
||||
StorageDriver struct {
|
||||
// Enabled turns on the health check for the storage driver
|
||||
Enabled bool `yaml:"enabled,omitempty"`
|
||||
// Interval is the duration in between checks
|
||||
Interval time.Duration `yaml:"interval,omitempty"`
|
||||
// Threshold is the number of times a check must fail to trigger an
|
||||
// unhealthy state
|
||||
Threshold int `yaml:"threshold,omitempty"`
|
||||
} `yaml:"storagedriver,omitempty"`
|
||||
}
|
||||
|
||||
// v0_1Configuration is a Version 0.1 Configuration struct
|
||||
// This is currently aliased to Configuration, as it is the current version
|
||||
type v0_1Configuration Configuration
|
||||
|
||||
// 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
|
||||
func (version *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var versionString string
|
||||
err := unmarshal(&versionString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newVersion := Version(versionString)
|
||||
if _, err := newVersion.major(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := newVersion.minor(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*version = newVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
// CurrentVersion is the most recent Version that can be parsed
|
||||
var CurrentVersion = MajorMinorVersion(0, 1)
|
||||
|
||||
// Loglevel is the level at which operations are logged
|
||||
// This can be error, warn, info, or debug
|
||||
type Loglevel string
|
||||
|
||||
// UnmarshalYAML implements the yaml.Umarshaler interface
|
||||
// Unmarshals a string into a Loglevel, lowercasing the string and validating that it represents a
|
||||
// valid loglevel
|
||||
func (loglevel *Loglevel) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var loglevelString string
|
||||
err := unmarshal(&loglevelString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loglevelString = strings.ToLower(loglevelString)
|
||||
switch loglevelString {
|
||||
case "error", "warn", "info", "debug":
|
||||
default:
|
||||
return fmt.Errorf("Invalid loglevel %s Must be one of [error, warn, info, debug]", loglevelString)
|
||||
}
|
||||
|
||||
*loglevel = Loglevel(loglevelString)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parameters defines a key-value parameters mapping
|
||||
type Parameters map[string]interface{}
|
||||
|
||||
// Storage defines the configuration for registry object storage
|
||||
type Storage map[string]Parameters
|
||||
|
||||
// Type returns the storage driver type, such as filesystem or s3
|
||||
func (storage Storage) Type() string {
|
||||
var storageType []string
|
||||
|
||||
// Return only key in this map
|
||||
for k := range storage {
|
||||
switch k {
|
||||
case "maintenance":
|
||||
// allow configuration of maintenance
|
||||
case "cache":
|
||||
// allow configuration of caching
|
||||
case "delete":
|
||||
// allow configuration of delete
|
||||
case "redirect":
|
||||
// allow configuration of redirect
|
||||
default:
|
||||
storageType = append(storageType, k)
|
||||
}
|
||||
}
|
||||
if len(storageType) > 1 {
|
||||
panic("multiple storage drivers specified in configuration or environment: " + strings.Join(storageType, ", "))
|
||||
}
|
||||
if len(storageType) == 1 {
|
||||
return storageType[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Parameters returns the Parameters map for a Storage configuration
|
||||
func (storage Storage) Parameters() Parameters {
|
||||
return storage[storage.Type()]
|
||||
}
|
||||
|
||||
// setParameter changes the parameter at the provided key to the new value
|
||||
func (storage Storage) setParameter(key string, value interface{}) {
|
||||
storage[storage.Type()][key] = value
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface
|
||||
// Unmarshals a single item map into a Storage or a string into a Storage type with no parameters
|
||||
func (storage *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var storageMap map[string]Parameters
|
||||
err := unmarshal(&storageMap)
|
||||
if err == nil {
|
||||
if len(storageMap) > 1 {
|
||||
types := make([]string, 0, len(storageMap))
|
||||
for k := range storageMap {
|
||||
switch k {
|
||||
case "maintenance":
|
||||
// allow for configuration of maintenance
|
||||
case "cache":
|
||||
// allow configuration of caching
|
||||
case "delete":
|
||||
// allow configuration of delete
|
||||
case "redirect":
|
||||
// allow configuration of redirect
|
||||
default:
|
||||
types = append(types, k)
|
||||
}
|
||||
}
|
||||
|
||||
if len(types) > 1 {
|
||||
return fmt.Errorf("Must provide exactly one storage type. Provided: %v", types)
|
||||
}
|
||||
}
|
||||
*storage = storageMap
|
||||
return nil
|
||||
}
|
||||
|
||||
var storageType string
|
||||
err = unmarshal(&storageType)
|
||||
if err == nil {
|
||||
*storage = Storage{storageType: Parameters{}}
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface
|
||||
func (storage Storage) MarshalYAML() (interface{}, error) {
|
||||
if storage.Parameters() == nil {
|
||||
return storage.Type(), nil
|
||||
}
|
||||
return map[string]Parameters(storage), nil
|
||||
}
|
||||
|
||||
// Auth defines the configuration for registry authorization.
|
||||
type Auth map[string]Parameters
|
||||
|
||||
// Type returns the auth type, such as htpasswd or token
|
||||
func (auth Auth) Type() string {
|
||||
// Return only key in this map
|
||||
for k := range auth {
|
||||
return k
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Parameters returns the Parameters map for an Auth configuration
|
||||
func (auth Auth) Parameters() Parameters {
|
||||
return auth[auth.Type()]
|
||||
}
|
||||
|
||||
// setParameter changes the parameter at the provided key to the new value
|
||||
func (auth Auth) setParameter(key string, value interface{}) {
|
||||
auth[auth.Type()][key] = value
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface
|
||||
// Unmarshals a single item map into a Storage or a string into a Storage type with no parameters
|
||||
func (auth *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var m map[string]Parameters
|
||||
err := unmarshal(&m)
|
||||
if err == nil {
|
||||
if len(m) > 1 {
|
||||
types := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
types = append(types, k)
|
||||
}
|
||||
|
||||
// TODO(stevvooe): May want to change this slightly for
|
||||
// authorization to allow multiple challenges.
|
||||
return fmt.Errorf("must provide exactly one type. Provided: %v", types)
|
||||
|
||||
}
|
||||
*auth = m
|
||||
return nil
|
||||
}
|
||||
|
||||
var authType string
|
||||
err = unmarshal(&authType)
|
||||
if err == nil {
|
||||
*auth = Auth{authType: Parameters{}}
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface
|
||||
func (auth Auth) MarshalYAML() (interface{}, error) {
|
||||
if auth.Parameters() == nil {
|
||||
return auth.Type(), nil
|
||||
}
|
||||
return map[string]Parameters(auth), nil
|
||||
}
|
||||
|
||||
// Notifications configures multiple http endpoints.
|
||||
type Notifications struct {
|
||||
// Endpoints is a list of http configurations for endpoints that
|
||||
// respond to webhook notifications. In the future, we may allow other
|
||||
// kinds of endpoints, such as external queues.
|
||||
Endpoints []Endpoint `yaml:"endpoints,omitempty"`
|
||||
}
|
||||
|
||||
// Endpoint describes the configuration of an http webhook notification
|
||||
// endpoint.
|
||||
type Endpoint struct {
|
||||
Name string `yaml:"name"` // identifies the endpoint in the registry instance.
|
||||
Disabled bool `yaml:"disabled"` // disables the endpoint
|
||||
URL string `yaml:"url"` // post url for the endpoint.
|
||||
Headers http.Header `yaml:"headers"` // static headers that should be added to all requests
|
||||
Timeout time.Duration `yaml:"timeout"` // HTTP timeout
|
||||
Threshold int `yaml:"threshold"` // circuit breaker threshold before backing off on failure
|
||||
Backoff time.Duration `yaml:"backoff"` // backoff duration
|
||||
IgnoredMediaTypes []string `yaml:"ignoredmediatypes"` // target media types to ignore
|
||||
}
|
||||
|
||||
// Reporting defines error reporting methods.
|
||||
type Reporting struct {
|
||||
// Bugsnag configures error reporting for Bugsnag (bugsnag.com).
|
||||
Bugsnag BugsnagReporting `yaml:"bugsnag,omitempty"`
|
||||
// NewRelic configures error reporting for NewRelic (newrelic.com)
|
||||
NewRelic NewRelicReporting `yaml:"newrelic,omitempty"`
|
||||
}
|
||||
|
||||
// BugsnagReporting configures error reporting for Bugsnag (bugsnag.com).
|
||||
type BugsnagReporting struct {
|
||||
// APIKey is the Bugsnag api key.
|
||||
APIKey string `yaml:"apikey,omitempty"`
|
||||
// ReleaseStage tracks where the registry is deployed.
|
||||
// Examples: production, staging, development
|
||||
ReleaseStage string `yaml:"releasestage,omitempty"`
|
||||
// Endpoint is used for specifying an enterprise Bugsnag endpoint.
|
||||
Endpoint string `yaml:"endpoint,omitempty"`
|
||||
}
|
||||
|
||||
// NewRelicReporting configures error reporting for NewRelic (newrelic.com)
|
||||
type NewRelicReporting struct {
|
||||
// LicenseKey is the NewRelic user license key
|
||||
LicenseKey string `yaml:"licensekey,omitempty"`
|
||||
// Name is the component name of the registry in NewRelic
|
||||
Name string `yaml:"name,omitempty"`
|
||||
// Verbose configures debug output to STDOUT
|
||||
Verbose bool `yaml:"verbose,omitempty"`
|
||||
}
|
||||
|
||||
// Middleware configures named middlewares to be applied at injection points.
|
||||
type Middleware struct {
|
||||
// Name the middleware registers itself as
|
||||
Name string `yaml:"name"`
|
||||
// Flag to disable middleware easily
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
// Map of parameters that will be passed to the middleware's initialization function
|
||||
Options Parameters `yaml:"options"`
|
||||
}
|
||||
|
||||
// Proxy configures the registry as a pull through cache
|
||||
type Proxy struct {
|
||||
// RemoteURL is the URL of the remote registry
|
||||
RemoteURL string `yaml:"remoteurl"`
|
||||
|
||||
// Username of the hub user
|
||||
Username string `yaml:"username"`
|
||||
|
||||
// Password of the hub user
|
||||
Password string `yaml:"password"`
|
||||
}
|
||||
|
||||
// Parse parses an input configuration yaml document into a Configuration struct
|
||||
// This should generally be capable of handling old configuration format versions
|
||||
//
|
||||
// Environment variables may be used to override configuration parameters other than version,
|
||||
// following the scheme below:
|
||||
// Configuration.Abc may be replaced by the value of REGISTRY_ABC,
|
||||
// Configuration.Abc.Xyz may be replaced by the value of REGISTRY_ABC_XYZ, and so forth
|
||||
func Parse(rd io.Reader) (*Configuration, error) {
|
||||
in, err := ioutil.ReadAll(rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := NewParser("registry", []VersionedParseInfo{
|
||||
{
|
||||
Version: MajorMinorVersion(0, 1),
|
||||
ParseAs: reflect.TypeOf(v0_1Configuration{}),
|
||||
ConversionFunc: func(c interface{}) (interface{}, error) {
|
||||
if v0_1, ok := c.(*v0_1Configuration); ok {
|
||||
if v0_1.Loglevel == Loglevel("") {
|
||||
v0_1.Loglevel = Loglevel("info")
|
||||
}
|
||||
if v0_1.Storage.Type() == "" {
|
||||
return nil, errors.New("No storage configuration provided")
|
||||
}
|
||||
return (*Configuration)(v0_1), nil
|
||||
}
|
||||
return nil, fmt.Errorf("Expected *v0_1Configuration, received %#v", c)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
config := new(Configuration)
|
||||
err = p.Parse(in, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
529
gateway/vendor/github.com/docker/distribution/configuration/configuration_test.go
generated
vendored
529
gateway/vendor/github.com/docker/distribution/configuration/configuration_test.go
generated
vendored
@ -1,529 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Hook up gocheck into the "go test" runner
|
||||
func Test(t *testing.T) { TestingT(t) }
|
||||
|
||||
// configStruct is a canonical example configuration, which should map to configYamlV0_1
|
||||
var configStruct = Configuration{
|
||||
Version: "0.1",
|
||||
Log: struct {
|
||||
AccessLog struct {
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
} `yaml:"accesslog,omitempty"`
|
||||
Level Loglevel `yaml:"level"`
|
||||
Formatter string `yaml:"formatter,omitempty"`
|
||||
Fields map[string]interface{} `yaml:"fields,omitempty"`
|
||||
Hooks []LogHook `yaml:"hooks,omitempty"`
|
||||
}{
|
||||
Fields: map[string]interface{}{"environment": "test"},
|
||||
},
|
||||
Loglevel: "info",
|
||||
Storage: Storage{
|
||||
"s3": Parameters{
|
||||
"region": "us-east-1",
|
||||
"bucket": "my-bucket",
|
||||
"rootdirectory": "/registry",
|
||||
"encrypt": true,
|
||||
"secure": false,
|
||||
"accesskey": "SAMPLEACCESSKEY",
|
||||
"secretkey": "SUPERSECRET",
|
||||
"host": nil,
|
||||
"port": 42,
|
||||
},
|
||||
},
|
||||
Auth: Auth{
|
||||
"silly": Parameters{
|
||||
"realm": "silly",
|
||||
"service": "silly",
|
||||
},
|
||||
},
|
||||
Reporting: Reporting{
|
||||
Bugsnag: BugsnagReporting{
|
||||
APIKey: "BugsnagApiKey",
|
||||
},
|
||||
},
|
||||
Notifications: Notifications{
|
||||
Endpoints: []Endpoint{
|
||||
{
|
||||
Name: "endpoint-1",
|
||||
URL: "http://example.com",
|
||||
Headers: http.Header{
|
||||
"Authorization": []string{"Bearer <example>"},
|
||||
},
|
||||
IgnoredMediaTypes: []string{"application/octet-stream"},
|
||||
},
|
||||
},
|
||||
},
|
||||
HTTP: struct {
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
Net string `yaml:"net,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Prefix string `yaml:"prefix,omitempty"`
|
||||
Secret string `yaml:"secret,omitempty"`
|
||||
RelativeURLs bool `yaml:"relativeurls,omitempty"`
|
||||
TLS struct {
|
||||
Certificate string `yaml:"certificate,omitempty"`
|
||||
Key string `yaml:"key,omitempty"`
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
LetsEncrypt struct {
|
||||
CacheFile string `yaml:"cachefile,omitempty"`
|
||||
Email string `yaml:"email,omitempty"`
|
||||
} `yaml:"letsencrypt,omitempty"`
|
||||
} `yaml:"tls,omitempty"`
|
||||
Headers http.Header `yaml:"headers,omitempty"`
|
||||
Debug struct {
|
||||
Addr string `yaml:"addr,omitempty"`
|
||||
} `yaml:"debug,omitempty"`
|
||||
HTTP2 struct {
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
} `yaml:"http2,omitempty"`
|
||||
}{
|
||||
TLS: struct {
|
||||
Certificate string `yaml:"certificate,omitempty"`
|
||||
Key string `yaml:"key,omitempty"`
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
LetsEncrypt struct {
|
||||
CacheFile string `yaml:"cachefile,omitempty"`
|
||||
Email string `yaml:"email,omitempty"`
|
||||
} `yaml:"letsencrypt,omitempty"`
|
||||
}{
|
||||
ClientCAs: []string{"/path/to/ca.pem"},
|
||||
},
|
||||
Headers: http.Header{
|
||||
"X-Content-Type-Options": []string{"nosniff"},
|
||||
},
|
||||
HTTP2: struct {
|
||||
Disabled bool `yaml:"disabled,omitempty"`
|
||||
}{
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// configYamlV0_1 is a Version 0.1 yaml document representing configStruct
|
||||
var configYamlV0_1 = `
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
environment: test
|
||||
loglevel: info
|
||||
storage:
|
||||
s3:
|
||||
region: us-east-1
|
||||
bucket: my-bucket
|
||||
rootdirectory: /registry
|
||||
encrypt: true
|
||||
secure: false
|
||||
accesskey: SAMPLEACCESSKEY
|
||||
secretkey: SUPERSECRET
|
||||
host: ~
|
||||
port: 42
|
||||
auth:
|
||||
silly:
|
||||
realm: silly
|
||||
service: silly
|
||||
notifications:
|
||||
endpoints:
|
||||
- name: endpoint-1
|
||||
url: http://example.com
|
||||
headers:
|
||||
Authorization: [Bearer <example>]
|
||||
ignoredmediatypes:
|
||||
- application/octet-stream
|
||||
reporting:
|
||||
bugsnag:
|
||||
apikey: BugsnagApiKey
|
||||
http:
|
||||
clientcas:
|
||||
- /path/to/ca.pem
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
`
|
||||
|
||||
// inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory
|
||||
// storage driver with no parameters
|
||||
var inmemoryConfigYamlV0_1 = `
|
||||
version: 0.1
|
||||
loglevel: info
|
||||
storage: inmemory
|
||||
auth:
|
||||
silly:
|
||||
realm: silly
|
||||
service: silly
|
||||
notifications:
|
||||
endpoints:
|
||||
- name: endpoint-1
|
||||
url: http://example.com
|
||||
headers:
|
||||
Authorization: [Bearer <example>]
|
||||
ignoredmediatypes:
|
||||
- application/octet-stream
|
||||
http:
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
`
|
||||
|
||||
type ConfigSuite struct {
|
||||
expectedConfig *Configuration
|
||||
}
|
||||
|
||||
var _ = Suite(new(ConfigSuite))
|
||||
|
||||
func (suite *ConfigSuite) SetUpTest(c *C) {
|
||||
os.Clearenv()
|
||||
suite.expectedConfig = copyConfig(configStruct)
|
||||
}
|
||||
|
||||
// TestMarshalRoundtrip validates that configStruct can be marshaled and
|
||||
// unmarshaled without changing any parameters
|
||||
func (suite *ConfigSuite) TestMarshalRoundtrip(c *C) {
|
||||
configBytes, err := yaml.Marshal(suite.expectedConfig)
|
||||
c.Assert(err, IsNil)
|
||||
config, err := Parse(bytes.NewReader(configBytes))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseSimple validates that configYamlV0_1 can be parsed into a struct
|
||||
// matching configStruct
|
||||
func (suite *ConfigSuite) TestParseSimple(c *C) {
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseInmemory validates that configuration yaml with storage provided as
|
||||
// a string can be parsed into a Configuration struct with no storage parameters
|
||||
func (suite *ConfigSuite) TestParseInmemory(c *C) {
|
||||
suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
|
||||
suite.expectedConfig.Reporting = Reporting{}
|
||||
suite.expectedConfig.Log.Fields = nil
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(inmemoryConfigYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseIncomplete validates that an incomplete yaml configuration cannot
|
||||
// be parsed without providing environment variables to fill in the missing
|
||||
// components.
|
||||
func (suite *ConfigSuite) TestParseIncomplete(c *C) {
|
||||
incompleteConfigYaml := "version: 0.1"
|
||||
_, err := Parse(bytes.NewReader([]byte(incompleteConfigYaml)))
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
suite.expectedConfig.Log.Fields = nil
|
||||
suite.expectedConfig.Storage = Storage{"filesystem": Parameters{"rootdirectory": "/tmp/testroot"}}
|
||||
suite.expectedConfig.Auth = Auth{"silly": Parameters{"realm": "silly"}}
|
||||
suite.expectedConfig.Reporting = Reporting{}
|
||||
suite.expectedConfig.Notifications = Notifications{}
|
||||
suite.expectedConfig.HTTP.Headers = nil
|
||||
|
||||
// Note: this also tests that REGISTRY_STORAGE and
|
||||
// REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY can be used together
|
||||
os.Setenv("REGISTRY_STORAGE", "filesystem")
|
||||
os.Setenv("REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY", "/tmp/testroot")
|
||||
os.Setenv("REGISTRY_AUTH", "silly")
|
||||
os.Setenv("REGISTRY_AUTH_SILLY_REALM", "silly")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(incompleteConfigYaml)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseWithSameEnvStorage validates that providing environment variables
|
||||
// that match the given storage type will only include environment-defined
|
||||
// parameters and remove yaml-defined parameters
|
||||
func (suite *ConfigSuite) TestParseWithSameEnvStorage(c *C) {
|
||||
suite.expectedConfig.Storage = Storage{"s3": Parameters{"region": "us-east-1"}}
|
||||
|
||||
os.Setenv("REGISTRY_STORAGE", "s3")
|
||||
os.Setenv("REGISTRY_STORAGE_S3_REGION", "us-east-1")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseWithDifferentEnvStorageParams validates that providing environment variables that change
|
||||
// and add to the given storage parameters will change and add parameters to the parsed
|
||||
// Configuration struct
|
||||
func (suite *ConfigSuite) TestParseWithDifferentEnvStorageParams(c *C) {
|
||||
suite.expectedConfig.Storage.setParameter("region", "us-west-1")
|
||||
suite.expectedConfig.Storage.setParameter("secure", true)
|
||||
suite.expectedConfig.Storage.setParameter("newparam", "some Value")
|
||||
|
||||
os.Setenv("REGISTRY_STORAGE_S3_REGION", "us-west-1")
|
||||
os.Setenv("REGISTRY_STORAGE_S3_SECURE", "true")
|
||||
os.Setenv("REGISTRY_STORAGE_S3_NEWPARAM", "some Value")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseWithDifferentEnvStorageType validates that providing an environment variable that
|
||||
// changes the storage type will be reflected in the parsed Configuration struct
|
||||
func (suite *ConfigSuite) TestParseWithDifferentEnvStorageType(c *C) {
|
||||
suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
|
||||
|
||||
os.Setenv("REGISTRY_STORAGE", "inmemory")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseWithDifferentEnvStorageTypeAndParams validates that providing an environment variable
|
||||
// that changes the storage type will be reflected in the parsed Configuration struct and that
|
||||
// environment storage parameters will also be included
|
||||
func (suite *ConfigSuite) TestParseWithDifferentEnvStorageTypeAndParams(c *C) {
|
||||
suite.expectedConfig.Storage = Storage{"filesystem": Parameters{}}
|
||||
suite.expectedConfig.Storage.setParameter("rootdirectory", "/tmp/testroot")
|
||||
|
||||
os.Setenv("REGISTRY_STORAGE", "filesystem")
|
||||
os.Setenv("REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY", "/tmp/testroot")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseWithSameEnvLoglevel validates that providing an environment variable defining the log
|
||||
// level to the same as the one provided in the yaml will not change the parsed Configuration struct
|
||||
func (suite *ConfigSuite) TestParseWithSameEnvLoglevel(c *C) {
|
||||
os.Setenv("REGISTRY_LOGLEVEL", "info")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseWithDifferentEnvLoglevel validates that providing an environment variable defining the
|
||||
// log level will override the value provided in the yaml document
|
||||
func (suite *ConfigSuite) TestParseWithDifferentEnvLoglevel(c *C) {
|
||||
suite.expectedConfig.Loglevel = "error"
|
||||
|
||||
os.Setenv("REGISTRY_LOGLEVEL", "error")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseInvalidLoglevel validates that the parser will fail to parse a
|
||||
// configuration if the loglevel is malformed
|
||||
func (suite *ConfigSuite) TestParseInvalidLoglevel(c *C) {
|
||||
invalidConfigYaml := "version: 0.1\nloglevel: derp\nstorage: inmemory"
|
||||
_, err := Parse(bytes.NewReader([]byte(invalidConfigYaml)))
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
os.Setenv("REGISTRY_LOGLEVEL", "derp")
|
||||
|
||||
_, err = Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, NotNil)
|
||||
|
||||
}
|
||||
|
||||
// TestParseWithDifferentEnvReporting validates that environment variables
|
||||
// properly override reporting parameters
|
||||
func (suite *ConfigSuite) TestParseWithDifferentEnvReporting(c *C) {
|
||||
suite.expectedConfig.Reporting.Bugsnag.APIKey = "anotherBugsnagApiKey"
|
||||
suite.expectedConfig.Reporting.Bugsnag.Endpoint = "localhost:8080"
|
||||
suite.expectedConfig.Reporting.NewRelic.LicenseKey = "NewRelicLicenseKey"
|
||||
suite.expectedConfig.Reporting.NewRelic.Name = "some NewRelic NAME"
|
||||
|
||||
os.Setenv("REGISTRY_REPORTING_BUGSNAG_APIKEY", "anotherBugsnagApiKey")
|
||||
os.Setenv("REGISTRY_REPORTING_BUGSNAG_ENDPOINT", "localhost:8080")
|
||||
os.Setenv("REGISTRY_REPORTING_NEWRELIC_LICENSEKEY", "NewRelicLicenseKey")
|
||||
os.Setenv("REGISTRY_REPORTING_NEWRELIC_NAME", "some NewRelic NAME")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseInvalidVersion validates that the parser will fail to parse a newer configuration
|
||||
// version than the CurrentVersion
|
||||
func (suite *ConfigSuite) TestParseInvalidVersion(c *C) {
|
||||
suite.expectedConfig.Version = MajorMinorVersion(CurrentVersion.Major(), CurrentVersion.Minor()+1)
|
||||
configBytes, err := yaml.Marshal(suite.expectedConfig)
|
||||
c.Assert(err, IsNil)
|
||||
_, err = Parse(bytes.NewReader(configBytes))
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
// TestParseExtraneousVars validates that environment variables referring to
|
||||
// nonexistent variables don't cause side effects.
|
||||
func (suite *ConfigSuite) TestParseExtraneousVars(c *C) {
|
||||
suite.expectedConfig.Reporting.Bugsnag.Endpoint = "localhost:8080"
|
||||
|
||||
// A valid environment variable
|
||||
os.Setenv("REGISTRY_REPORTING_BUGSNAG_ENDPOINT", "localhost:8080")
|
||||
|
||||
// Environment variables which shouldn't set config items
|
||||
os.Setenv("registry_REPORTING_NEWRELIC_LICENSEKEY", "NewRelicLicenseKey")
|
||||
os.Setenv("REPORTING_NEWRELIC_NAME", "some NewRelic NAME")
|
||||
os.Setenv("REGISTRY_DUCKS", "quack")
|
||||
os.Setenv("REGISTRY_REPORTING_ASDF", "ghjk")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseEnvVarImplicitMaps validates that environment variables can set
|
||||
// values in maps that don't already exist.
|
||||
func (suite *ConfigSuite) TestParseEnvVarImplicitMaps(c *C) {
|
||||
readonly := make(map[string]interface{})
|
||||
readonly["enabled"] = true
|
||||
|
||||
maintenance := make(map[string]interface{})
|
||||
maintenance["readonly"] = readonly
|
||||
|
||||
suite.expectedConfig.Storage["maintenance"] = maintenance
|
||||
|
||||
os.Setenv("REGISTRY_STORAGE_MAINTENANCE_READONLY_ENABLED", "true")
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(config, DeepEquals, suite.expectedConfig)
|
||||
}
|
||||
|
||||
// TestParseEnvWrongTypeMap validates that incorrectly attempting to unmarshal a
|
||||
// string over existing map fails.
|
||||
func (suite *ConfigSuite) TestParseEnvWrongTypeMap(c *C) {
|
||||
os.Setenv("REGISTRY_STORAGE_S3", "somestring")
|
||||
|
||||
_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
// TestParseEnvWrongTypeStruct validates that incorrectly attempting to
|
||||
// unmarshal a string into a struct fails.
|
||||
func (suite *ConfigSuite) TestParseEnvWrongTypeStruct(c *C) {
|
||||
os.Setenv("REGISTRY_STORAGE_LOG", "somestring")
|
||||
|
||||
_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
// TestParseEnvWrongTypeSlice validates that incorrectly attempting to
|
||||
// unmarshal a string into a slice fails.
|
||||
func (suite *ConfigSuite) TestParseEnvWrongTypeSlice(c *C) {
|
||||
os.Setenv("REGISTRY_LOG_HOOKS", "somestring")
|
||||
|
||||
_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
// TestParseEnvMany tests several environment variable overrides.
|
||||
// The result is not checked - the goal of this test is to detect panics
|
||||
// from misuse of reflection.
|
||||
func (suite *ConfigSuite) TestParseEnvMany(c *C) {
|
||||
os.Setenv("REGISTRY_VERSION", "0.1")
|
||||
os.Setenv("REGISTRY_LOG_LEVEL", "debug")
|
||||
os.Setenv("REGISTRY_LOG_FORMATTER", "json")
|
||||
os.Setenv("REGISTRY_LOG_HOOKS", "json")
|
||||
os.Setenv("REGISTRY_LOG_FIELDS", "abc: xyz")
|
||||
os.Setenv("REGISTRY_LOG_HOOKS", "- type: asdf")
|
||||
os.Setenv("REGISTRY_LOGLEVEL", "debug")
|
||||
os.Setenv("REGISTRY_STORAGE", "s3")
|
||||
os.Setenv("REGISTRY_AUTH_PARAMS", "param1: value1")
|
||||
os.Setenv("REGISTRY_AUTH_PARAMS_VALUE2", "value2")
|
||||
os.Setenv("REGISTRY_AUTH_PARAMS_VALUE2", "value2")
|
||||
|
||||
_, err := Parse(bytes.NewReader([]byte(configYamlV0_1)))
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func checkStructs(c *C, t reflect.Type, structsChecked map[string]struct{}) {
|
||||
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Map || t.Kind() == reflect.Slice {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
if _, present := structsChecked[t.String()]; present {
|
||||
// Already checked this type
|
||||
return
|
||||
}
|
||||
|
||||
structsChecked[t.String()] = struct{}{}
|
||||
|
||||
byUpperCase := make(map[string]int)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
sf := t.Field(i)
|
||||
|
||||
// Check that the yaml tag does not contain an _.
|
||||
yamlTag := sf.Tag.Get("yaml")
|
||||
if strings.Contains(yamlTag, "_") {
|
||||
c.Fatalf("yaml field name includes _ character: %s", yamlTag)
|
||||
}
|
||||
upper := strings.ToUpper(sf.Name)
|
||||
if _, present := byUpperCase[upper]; present {
|
||||
c.Fatalf("field name collision in configuration object: %s", sf.Name)
|
||||
}
|
||||
byUpperCase[upper] = i
|
||||
|
||||
checkStructs(c, sf.Type, structsChecked)
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateConfigStruct makes sure that the config struct has no members
|
||||
// with yaml tags that would be ambiguous to the environment variable parser.
|
||||
func (suite *ConfigSuite) TestValidateConfigStruct(c *C) {
|
||||
structsChecked := make(map[string]struct{})
|
||||
checkStructs(c, reflect.TypeOf(Configuration{}), structsChecked)
|
||||
}
|
||||
|
||||
func copyConfig(config Configuration) *Configuration {
|
||||
configCopy := new(Configuration)
|
||||
|
||||
configCopy.Version = MajorMinorVersion(config.Version.Major(), config.Version.Minor())
|
||||
configCopy.Loglevel = config.Loglevel
|
||||
configCopy.Log = config.Log
|
||||
configCopy.Log.Fields = make(map[string]interface{}, len(config.Log.Fields))
|
||||
for k, v := range config.Log.Fields {
|
||||
configCopy.Log.Fields[k] = v
|
||||
}
|
||||
|
||||
configCopy.Storage = Storage{config.Storage.Type(): Parameters{}}
|
||||
for k, v := range config.Storage.Parameters() {
|
||||
configCopy.Storage.setParameter(k, v)
|
||||
}
|
||||
configCopy.Reporting = Reporting{
|
||||
Bugsnag: BugsnagReporting{config.Reporting.Bugsnag.APIKey, config.Reporting.Bugsnag.ReleaseStage, config.Reporting.Bugsnag.Endpoint},
|
||||
NewRelic: NewRelicReporting{config.Reporting.NewRelic.LicenseKey, config.Reporting.NewRelic.Name, config.Reporting.NewRelic.Verbose},
|
||||
}
|
||||
|
||||
configCopy.Auth = Auth{config.Auth.Type(): Parameters{}}
|
||||
for k, v := range config.Auth.Parameters() {
|
||||
configCopy.Auth.setParameter(k, v)
|
||||
}
|
||||
|
||||
configCopy.Notifications = Notifications{Endpoints: []Endpoint{}}
|
||||
for _, v := range config.Notifications.Endpoints {
|
||||
configCopy.Notifications.Endpoints = append(configCopy.Notifications.Endpoints, v)
|
||||
}
|
||||
|
||||
configCopy.HTTP.Headers = make(http.Header)
|
||||
for k, v := range config.HTTP.Headers {
|
||||
configCopy.HTTP.Headers[k] = v
|
||||
}
|
||||
|
||||
return configCopy
|
||||
}
|
283
gateway/vendor/github.com/docker/distribution/configuration/parser.go
generated
vendored
283
gateway/vendor/github.com/docker/distribution/configuration/parser.go
generated
vendored
@ -1,283 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Version is a major/minor version pair of the form Major.Minor
|
||||
// Major version upgrades indicate structure or type changes
|
||||
// Minor version upgrades should be strictly additive
|
||||
type Version string
|
||||
|
||||
// MajorMinorVersion constructs a Version from its Major and Minor components
|
||||
func MajorMinorVersion(major, minor uint) Version {
|
||||
return Version(fmt.Sprintf("%d.%d", major, minor))
|
||||
}
|
||||
|
||||
func (version Version) major() (uint, error) {
|
||||
majorPart := strings.Split(string(version), ".")[0]
|
||||
major, err := strconv.ParseUint(majorPart, 10, 0)
|
||||
return uint(major), err
|
||||
}
|
||||
|
||||
// Major returns the major version portion of a Version
|
||||
func (version Version) Major() uint {
|
||||
major, _ := version.major()
|
||||
return major
|
||||
}
|
||||
|
||||
func (version Version) minor() (uint, error) {
|
||||
minorPart := strings.Split(string(version), ".")[1]
|
||||
minor, err := strconv.ParseUint(minorPart, 10, 0)
|
||||
return uint(minor), err
|
||||
}
|
||||
|
||||
// Minor returns the minor version portion of a Version
|
||||
func (version Version) Minor() uint {
|
||||
minor, _ := version.minor()
|
||||
return minor
|
||||
}
|
||||
|
||||
// VersionedParseInfo defines how a specific version of a configuration should
|
||||
// be parsed into the current version
|
||||
type VersionedParseInfo struct {
|
||||
// Version is the version which this parsing information relates to
|
||||
Version Version
|
||||
// ParseAs defines the type which a configuration file of this version
|
||||
// should be parsed into
|
||||
ParseAs reflect.Type
|
||||
// ConversionFunc defines a method for converting the parsed configuration
|
||||
// (of type ParseAs) into the current configuration version
|
||||
// Note: this method signature is very unclear with the absence of generics
|
||||
ConversionFunc func(interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
type envVar struct {
|
||||
name string
|
||||
value string
|
||||
}
|
||||
|
||||
type envVars []envVar
|
||||
|
||||
func (a envVars) Len() int { return len(a) }
|
||||
func (a envVars) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a envVars) Less(i, j int) bool { return a[i].name < a[j].name }
|
||||
|
||||
// Parser can be used to parse a configuration file and environment of a defined
|
||||
// version into a unified output structure
|
||||
type Parser struct {
|
||||
prefix string
|
||||
mapping map[Version]VersionedParseInfo
|
||||
env envVars
|
||||
}
|
||||
|
||||
// NewParser returns a *Parser with the given environment prefix which handles
|
||||
// versioned configurations which match the given parseInfos
|
||||
func NewParser(prefix string, parseInfos []VersionedParseInfo) *Parser {
|
||||
p := Parser{prefix: prefix, mapping: make(map[Version]VersionedParseInfo)}
|
||||
|
||||
for _, parseInfo := range parseInfos {
|
||||
p.mapping[parseInfo.Version] = parseInfo
|
||||
}
|
||||
|
||||
for _, env := range os.Environ() {
|
||||
envParts := strings.SplitN(env, "=", 2)
|
||||
p.env = append(p.env, envVar{envParts[0], envParts[1]})
|
||||
}
|
||||
|
||||
// We must sort the environment variables lexically by name so that
|
||||
// more specific variables are applied before less specific ones
|
||||
// (i.e. REGISTRY_STORAGE before
|
||||
// REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY). This sucks, but it's a
|
||||
// lot simpler and easier to get right than unmarshalling map entries
|
||||
// into temporaries and merging with the existing entry.
|
||||
sort.Sort(p.env)
|
||||
|
||||
return &p
|
||||
}
|
||||
|
||||
// Parse reads in the given []byte and environment and writes the resulting
|
||||
// configuration into the input v
|
||||
//
|
||||
// Environment variables may be used to override configuration parameters other
|
||||
// than version, following the scheme below:
|
||||
// v.Abc may be replaced by the value of PREFIX_ABC,
|
||||
// v.Abc.Xyz may be replaced by the value of PREFIX_ABC_XYZ, and so forth
|
||||
func (p *Parser) Parse(in []byte, v interface{}) error {
|
||||
var versionedStruct struct {
|
||||
Version Version
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(in, &versionedStruct); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseInfo, ok := p.mapping[versionedStruct.Version]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unsupported version: %q", versionedStruct.Version)
|
||||
}
|
||||
|
||||
parseAs := reflect.New(parseInfo.ParseAs)
|
||||
err := yaml.Unmarshal(in, parseAs.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, envVar := range p.env {
|
||||
pathStr := envVar.name
|
||||
if strings.HasPrefix(pathStr, strings.ToUpper(p.prefix)+"_") {
|
||||
path := strings.Split(pathStr, "_")
|
||||
|
||||
err = p.overwriteFields(parseAs, pathStr, path[1:], envVar.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c, err := parseInfo.ConversionFunc(parseAs.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reflect.ValueOf(v).Elem().Set(reflect.Indirect(reflect.ValueOf(c)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// overwriteFields replaces configuration values with alternate values specified
|
||||
// through the environment. Precondition: an empty path slice must never be
|
||||
// passed in.
|
||||
func (p *Parser) overwriteFields(v reflect.Value, fullpath string, path []string, payload string) error {
|
||||
for v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
panic("encountered nil pointer while handling environment variable " + fullpath)
|
||||
}
|
||||
v = reflect.Indirect(v)
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
return p.overwriteStruct(v, fullpath, path, payload)
|
||||
case reflect.Map:
|
||||
return p.overwriteMap(v, fullpath, path, payload)
|
||||
case reflect.Interface:
|
||||
if v.NumMethod() == 0 {
|
||||
if !v.IsNil() {
|
||||
return p.overwriteFields(v.Elem(), fullpath, path, payload)
|
||||
}
|
||||
// Interface was empty; create an implicit map
|
||||
var template map[string]interface{}
|
||||
wrappedV := reflect.MakeMap(reflect.TypeOf(template))
|
||||
v.Set(wrappedV)
|
||||
return p.overwriteMap(wrappedV, fullpath, path, payload)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) overwriteStruct(v reflect.Value, fullpath string, path []string, payload string) error {
|
||||
// Generate case-insensitive map of struct fields
|
||||
byUpperCase := make(map[string]int)
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
sf := v.Type().Field(i)
|
||||
upper := strings.ToUpper(sf.Name)
|
||||
if _, present := byUpperCase[upper]; present {
|
||||
panic(fmt.Sprintf("field name collision in configuration object: %s", sf.Name))
|
||||
}
|
||||
byUpperCase[upper] = i
|
||||
}
|
||||
|
||||
fieldIndex, present := byUpperCase[path[0]]
|
||||
if !present {
|
||||
logrus.Warnf("Ignoring unrecognized environment variable %s", fullpath)
|
||||
return nil
|
||||
}
|
||||
field := v.Field(fieldIndex)
|
||||
sf := v.Type().Field(fieldIndex)
|
||||
|
||||
if len(path) == 1 {
|
||||
// Env var specifies this field directly
|
||||
fieldVal := reflect.New(sf.Type)
|
||||
err := yaml.Unmarshal([]byte(payload), fieldVal.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.Set(reflect.Indirect(fieldVal))
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the field is nil, must create an object
|
||||
switch sf.Type.Kind() {
|
||||
case reflect.Map:
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.MakeMap(sf.Type))
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(sf.Type))
|
||||
}
|
||||
}
|
||||
|
||||
err := p.overwriteFields(field, fullpath, path[1:], payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) overwriteMap(m reflect.Value, fullpath string, path []string, payload string) error {
|
||||
if m.Type().Key().Kind() != reflect.String {
|
||||
// non-string keys unsupported
|
||||
logrus.Warnf("Ignoring environment variable %s involving map with non-string keys", fullpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(path) > 1 {
|
||||
// If a matching key exists, get its value and continue the
|
||||
// overwriting process.
|
||||
for _, k := range m.MapKeys() {
|
||||
if strings.ToUpper(k.String()) == path[0] {
|
||||
mapValue := m.MapIndex(k)
|
||||
// If the existing value is nil, we want to
|
||||
// recreate it instead of using this value.
|
||||
if (mapValue.Kind() == reflect.Ptr ||
|
||||
mapValue.Kind() == reflect.Interface ||
|
||||
mapValue.Kind() == reflect.Map) &&
|
||||
mapValue.IsNil() {
|
||||
break
|
||||
}
|
||||
return p.overwriteFields(mapValue, fullpath, path[1:], payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (Re)create this key
|
||||
var mapValue reflect.Value
|
||||
if m.Type().Elem().Kind() == reflect.Map {
|
||||
mapValue = reflect.MakeMap(m.Type().Elem())
|
||||
} else {
|
||||
mapValue = reflect.New(m.Type().Elem())
|
||||
}
|
||||
if len(path) > 1 {
|
||||
err := p.overwriteFields(mapValue, fullpath, path[1:], payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := yaml.Unmarshal([]byte(payload), mapValue.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.SetMapIndex(reflect.ValueOf(strings.ToLower(path[0])), reflect.Indirect(mapValue))
|
||||
|
||||
return nil
|
||||
}
|
285
gateway/vendor/github.com/docker/distribution/context/http_test.go
generated
vendored
285
gateway/vendor/github.com/docker/distribution/context/http_test.go
generated
vendored
@ -1,285 +0,0 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWithRequest(t *testing.T) {
|
||||
var req http.Request
|
||||
|
||||
start := time.Now()
|
||||
req.Method = "GET"
|
||||
req.Host = "example.com"
|
||||
req.RequestURI = "/test-test"
|
||||
req.Header = make(http.Header)
|
||||
req.Header.Set("Referer", "foo.com/referer")
|
||||
req.Header.Set("User-Agent", "test/0.1")
|
||||
|
||||
ctx := WithRequest(Background(), &req)
|
||||
for _, testcase := range []struct {
|
||||
key string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
key: "http.request",
|
||||
expected: &req,
|
||||
},
|
||||
{
|
||||
key: "http.request.id",
|
||||
},
|
||||
{
|
||||
key: "http.request.method",
|
||||
expected: req.Method,
|
||||
},
|
||||
{
|
||||
key: "http.request.host",
|
||||
expected: req.Host,
|
||||
},
|
||||
{
|
||||
key: "http.request.uri",
|
||||
expected: req.RequestURI,
|
||||
},
|
||||
{
|
||||
key: "http.request.referer",
|
||||
expected: req.Referer(),
|
||||
},
|
||||
{
|
||||
key: "http.request.useragent",
|
||||
expected: req.UserAgent(),
|
||||
},
|
||||
{
|
||||
key: "http.request.remoteaddr",
|
||||
expected: req.RemoteAddr,
|
||||
},
|
||||
{
|
||||
key: "http.request.startedat",
|
||||
},
|
||||
} {
|
||||
v := ctx.Value(testcase.key)
|
||||
|
||||
if v == nil {
|
||||
t.Fatalf("value not found for %q", testcase.key)
|
||||
}
|
||||
|
||||
if testcase.expected != nil && v != testcase.expected {
|
||||
t.Fatalf("%s: %v != %v", testcase.key, v, testcase.expected)
|
||||
}
|
||||
|
||||
// Key specific checks!
|
||||
switch testcase.key {
|
||||
case "http.request.id":
|
||||
if _, ok := v.(string); !ok {
|
||||
t.Fatalf("request id not a string: %v", v)
|
||||
}
|
||||
case "http.request.startedat":
|
||||
vt, ok := v.(time.Time)
|
||||
if !ok {
|
||||
t.Fatalf("value not a time: %v", v)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if vt.After(now) {
|
||||
t.Fatalf("time generated too late: %v > %v", vt, now)
|
||||
}
|
||||
|
||||
if vt.Before(start) {
|
||||
t.Fatalf("time generated too early: %v < %v", vt, start)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testResponseWriter struct {
|
||||
flushed bool
|
||||
status int
|
||||
written int64
|
||||
header http.Header
|
||||
}
|
||||
|
||||
func (trw *testResponseWriter) Header() http.Header {
|
||||
if trw.header == nil {
|
||||
trw.header = make(http.Header)
|
||||
}
|
||||
|
||||
return trw.header
|
||||
}
|
||||
|
||||
func (trw *testResponseWriter) Write(p []byte) (n int, err error) {
|
||||
if trw.status == 0 {
|
||||
trw.status = http.StatusOK
|
||||
}
|
||||
|
||||
n = len(p)
|
||||
trw.written += int64(n)
|
||||
return
|
||||
}
|
||||
|
||||
func (trw *testResponseWriter) WriteHeader(status int) {
|
||||
trw.status = status
|
||||
}
|
||||
|
||||
func (trw *testResponseWriter) Flush() {
|
||||
trw.flushed = true
|
||||
}
|
||||
|
||||
func TestWithResponseWriter(t *testing.T) {
|
||||
trw := testResponseWriter{}
|
||||
ctx, rw := WithResponseWriter(Background(), &trw)
|
||||
|
||||
if ctx.Value("http.response") != rw {
|
||||
t.Fatalf("response not available in context: %v != %v", ctx.Value("http.response"), rw)
|
||||
}
|
||||
|
||||
grw, err := GetResponseWriter(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting response writer: %v", err)
|
||||
}
|
||||
|
||||
if grw != rw {
|
||||
t.Fatalf("unexpected response writer returned: %#v != %#v", grw, rw)
|
||||
}
|
||||
|
||||
if ctx.Value("http.response.status") != 0 {
|
||||
t.Fatalf("response status should always be a number and should be zero here: %v != 0", ctx.Value("http.response.status"))
|
||||
}
|
||||
|
||||
if n, err := rw.Write(make([]byte, 1024)); err != nil {
|
||||
t.Fatalf("unexpected error writing: %v", err)
|
||||
} else if n != 1024 {
|
||||
t.Fatalf("unexpected number of bytes written: %v != %v", n, 1024)
|
||||
}
|
||||
|
||||
if ctx.Value("http.response.status") != http.StatusOK {
|
||||
t.Fatalf("unexpected response status in context: %v != %v", ctx.Value("http.response.status"), http.StatusOK)
|
||||
}
|
||||
|
||||
if ctx.Value("http.response.written") != int64(1024) {
|
||||
t.Fatalf("unexpected number reported bytes written: %v != %v", ctx.Value("http.response.written"), 1024)
|
||||
}
|
||||
|
||||
// Make sure flush propagates
|
||||
rw.(http.Flusher).Flush()
|
||||
|
||||
if !trw.flushed {
|
||||
t.Fatalf("response writer not flushed")
|
||||
}
|
||||
|
||||
// Write another status and make sure context is correct. This normally
|
||||
// wouldn't work except for in this contrived testcase.
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
if ctx.Value("http.response.status") != http.StatusBadRequest {
|
||||
t.Fatalf("unexpected response status in context: %v != %v", ctx.Value("http.response.status"), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithVars(t *testing.T) {
|
||||
var req http.Request
|
||||
vars := map[string]string{
|
||||
"foo": "asdf",
|
||||
"bar": "qwer",
|
||||
}
|
||||
|
||||
getVarsFromRequest = func(r *http.Request) map[string]string {
|
||||
if r != &req {
|
||||
t.Fatalf("unexpected request: %v != %v", r, req)
|
||||
}
|
||||
|
||||
return vars
|
||||
}
|
||||
|
||||
ctx := WithVars(Background(), &req)
|
||||
for _, testcase := range []struct {
|
||||
key string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
key: "vars",
|
||||
expected: vars,
|
||||
},
|
||||
{
|
||||
key: "vars.foo",
|
||||
expected: "asdf",
|
||||
},
|
||||
{
|
||||
key: "vars.bar",
|
||||
expected: "qwer",
|
||||
},
|
||||
} {
|
||||
v := ctx.Value(testcase.key)
|
||||
|
||||
if !reflect.DeepEqual(v, testcase.expected) {
|
||||
t.Fatalf("%q: %v != %v", testcase.key, v, testcase.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SingleHostReverseProxy will insert an X-Forwarded-For header, and can be used to test
|
||||
// RemoteAddr(). A fake RemoteAddr cannot be set on the HTTP request - it is overwritten
|
||||
// at the transport layer to 127.0.0.1:<port> . However, as the X-Forwarded-For header
|
||||
// just contains the IP address, it is different enough for testing.
|
||||
func TestRemoteAddr(t *testing.T) {
|
||||
var expectedRemote string
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if r.RemoteAddr == expectedRemote {
|
||||
t.Errorf("Unexpected matching remote addresses")
|
||||
}
|
||||
|
||||
actualRemote := RemoteAddr(r)
|
||||
if expectedRemote != actualRemote {
|
||||
t.Errorf("Mismatching remote hosts: %v != %v", expectedRemote, actualRemote)
|
||||
}
|
||||
|
||||
w.WriteHeader(200)
|
||||
}))
|
||||
|
||||
defer backend.Close()
|
||||
backendURL, err := url.Parse(backend.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(backendURL)
|
||||
frontend := httptest.NewServer(proxy)
|
||||
defer frontend.Close()
|
||||
|
||||
// X-Forwarded-For set by proxy
|
||||
expectedRemote = "127.0.0.1"
|
||||
proxyReq, err := http.NewRequest("GET", frontend.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = http.DefaultClient.Do(proxyReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// RemoteAddr in X-Real-Ip
|
||||
getReq, err := http.NewRequest("GET", backend.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedRemote = "1.2.3.4"
|
||||
getReq.Header["X-Real-ip"] = []string{expectedRemote}
|
||||
_, err = http.DefaultClient.Do(getReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Valid X-Real-Ip and invalid X-Forwarded-For
|
||||
getReq.Header["X-forwarded-for"] = []string{"1.2.3"}
|
||||
_, err = http.DefaultClient.Do(getReq)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
85
gateway/vendor/github.com/docker/distribution/context/trace_test.go
generated
vendored
85
gateway/vendor/github.com/docker/distribution/context/trace_test.go
generated
vendored
@ -1,85 +0,0 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestWithTrace ensures that tracing has the expected values in the context.
|
||||
func TestWithTrace(t *testing.T) {
|
||||
pc, file, _, _ := runtime.Caller(0) // get current caller.
|
||||
f := runtime.FuncForPC(pc)
|
||||
|
||||
base := []valueTestCase{
|
||||
{
|
||||
key: "trace.id",
|
||||
notnilorempty: true,
|
||||
},
|
||||
|
||||
{
|
||||
key: "trace.file",
|
||||
expected: file,
|
||||
notnilorempty: true,
|
||||
},
|
||||
{
|
||||
key: "trace.line",
|
||||
notnilorempty: true,
|
||||
},
|
||||
{
|
||||
key: "trace.start",
|
||||
notnilorempty: true,
|
||||
},
|
||||
}
|
||||
|
||||
ctx, done := WithTrace(Background())
|
||||
defer done("this will be emitted at end of test")
|
||||
|
||||
checkContextForValues(t, ctx, append(base, valueTestCase{
|
||||
key: "trace.func",
|
||||
expected: f.Name(),
|
||||
}))
|
||||
|
||||
traced := func() {
|
||||
parentID := ctx.Value("trace.id") // ensure the parent trace id is correct.
|
||||
|
||||
pc, _, _, _ := runtime.Caller(0) // get current caller.
|
||||
f := runtime.FuncForPC(pc)
|
||||
ctx, done := WithTrace(ctx)
|
||||
defer done("this should be subordinate to the other trace")
|
||||
time.Sleep(time.Second)
|
||||
checkContextForValues(t, ctx, append(base, valueTestCase{
|
||||
key: "trace.func",
|
||||
expected: f.Name(),
|
||||
}, valueTestCase{
|
||||
key: "trace.parent.id",
|
||||
expected: parentID,
|
||||
}))
|
||||
}
|
||||
traced()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
type valueTestCase struct {
|
||||
key string
|
||||
expected interface{}
|
||||
notnilorempty bool // just check not empty/not nil
|
||||
}
|
||||
|
||||
func checkContextForValues(t *testing.T, ctx Context, values []valueTestCase) {
|
||||
|
||||
for _, testcase := range values {
|
||||
v := ctx.Value(testcase.key)
|
||||
if testcase.notnilorempty {
|
||||
if v == nil || v == "" {
|
||||
t.Fatalf("value was nil or empty for %q: %#v", testcase.key, v)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if v != testcase.expected {
|
||||
t.Fatalf("unexpected value for key %q: %v != %v", testcase.key, v, testcase.expected)
|
||||
}
|
||||
}
|
||||
}
|
19
gateway/vendor/github.com/docker/distribution/context/version_test.go
generated
vendored
19
gateway/vendor/github.com/docker/distribution/context/version_test.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
package context
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestVersionContext(t *testing.T) {
|
||||
ctx := Background()
|
||||
|
||||
if GetVersion(ctx) != "" {
|
||||
t.Fatalf("context should not yet have a version")
|
||||
}
|
||||
|
||||
expected := "2.1-whatever"
|
||||
ctx = WithVersion(ctx, expected)
|
||||
version := GetVersion(ctx)
|
||||
|
||||
if version != expected {
|
||||
t.Fatalf("version was not set: %q != %q", version, expected)
|
||||
}
|
||||
}
|
36
gateway/vendor/github.com/docker/distribution/contrib/apache/README.MD
generated
vendored
36
gateway/vendor/github.com/docker/distribution/contrib/apache/README.MD
generated
vendored
@ -1,36 +0,0 @@
|
||||
# Apache HTTPd sample for Registry v1, v2 and mirror
|
||||
|
||||
3 containers involved
|
||||
|
||||
* Docker Registry v1 (registry 0.9.1)
|
||||
* Docker Registry v2 (registry 2.0.0)
|
||||
* Docker Registry v1 in mirror mode
|
||||
|
||||
HTTP for mirror and HTTPS for v1 & v2
|
||||
|
||||
* http://registry.example.com proxify Docker Registry 1.0 in Mirror mode
|
||||
* https://registry.example.com proxify Docker Registry 1.0 or 2.0 in Hosting mode
|
||||
|
||||
## 3 Docker containers should be started
|
||||
|
||||
* Docker Registry 1.0 in Mirror mode : port 5001
|
||||
* Docker Registry 1.0 in Hosting mode : port 5000
|
||||
* Docker Registry 2.0 in Hosting mode : port 5002
|
||||
|
||||
### Registry v1
|
||||
|
||||
docker run -d -e SETTINGS_FLAVOR=dev -v /var/lib/docker-registry/storage/hosting-v1:/tmp -p 5000:5000 registry:0.9.1"
|
||||
|
||||
### Mirror
|
||||
|
||||
docker run -d -e SETTINGS_FLAVOR=dev -e STANDALONE=false -e MIRROR_SOURCE=https://registry-1.docker.io -e MIRROR_SOURCE_INDEX=https://index.docker.io \
|
||||
-e MIRROR_TAGS_CACHE_TTL=172800 -v /var/lib/docker-registry/storage/mirror:/tmp -p 5001:5000 registry:0.9.1"
|
||||
|
||||
### Registry v2
|
||||
|
||||
docker run -d -e SETTINGS_FLAVOR=dev -v /var/lib/axway/docker-registry/storage/hosting2-v2:/tmp -p 5002:5000 registry:2"
|
||||
|
||||
# For Hosting mode access
|
||||
|
||||
* users should have account (valid-user) to be able to fetch images
|
||||
* only users using account docker-deployer will be allowed to push images
|
127
gateway/vendor/github.com/docker/distribution/contrib/apache/apache.conf
generated
vendored
127
gateway/vendor/github.com/docker/distribution/contrib/apache/apache.conf
generated
vendored
@ -1,127 +0,0 @@
|
||||
#
|
||||
# Sample Apache 2.x configuration where :
|
||||
#
|
||||
|
||||
<VirtualHost *:80>
|
||||
|
||||
ServerName registry.example.com
|
||||
ServerAlias www.registry.example.com
|
||||
|
||||
ProxyRequests off
|
||||
ProxyPreserveHost on
|
||||
|
||||
# no proxy for /error/ (Apache HTTPd errors messages)
|
||||
ProxyPass /error/ !
|
||||
|
||||
ProxyPass /_ping http://localhost:5001/_ping
|
||||
ProxyPassReverse /_ping http://localhost:5001/_ping
|
||||
|
||||
ProxyPass /v1 http://localhost:5001/v1
|
||||
ProxyPassReverse /v1 http://localhost:5001/v1
|
||||
|
||||
# Logs
|
||||
ErrorLog ${APACHE_LOG_DIR}/mirror_error_log
|
||||
CustomLog ${APACHE_LOG_DIR}/mirror_access_log combined env=!dontlog
|
||||
|
||||
</VirtualHost>
|
||||
|
||||
|
||||
<VirtualHost *:443>
|
||||
|
||||
ServerName registry.example.com
|
||||
ServerAlias www.registry.example.com
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/apache2/ssl/registry.example.com.crt
|
||||
SSLCertificateKeyFile /etc/apache2/ssl/registry.example.com.key
|
||||
|
||||
# Higher Strength SSL Ciphers
|
||||
SSLProtocol all -SSLv2 -SSLv3 -TLSv1
|
||||
SSLCipherSuite RC4-SHA:HIGH
|
||||
SSLHonorCipherOrder on
|
||||
|
||||
# Logs
|
||||
ErrorLog ${APACHE_LOG_DIR}/registry_error_ssl_log
|
||||
CustomLog ${APACHE_LOG_DIR}/registry_access_ssl_log combined env=!dontlog
|
||||
|
||||
Header always set "Docker-Distribution-Api-Version" "registry/2.0"
|
||||
Header onsuccess set "Docker-Distribution-Api-Version" "registry/2.0"
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
|
||||
ProxyRequests off
|
||||
ProxyPreserveHost on
|
||||
|
||||
# no proxy for /error/ (Apache HTTPd errors messages)
|
||||
ProxyPass /error/ !
|
||||
|
||||
#
|
||||
# Registry v1
|
||||
#
|
||||
|
||||
ProxyPass /v1 http://localhost:5000/v1
|
||||
ProxyPassReverse /v1 http://localhost:5000/v1
|
||||
|
||||
ProxyPass /_ping http://localhost:5000/_ping
|
||||
ProxyPassReverse /_ping http://localhost:5000/_ping
|
||||
|
||||
# Authentication require for push
|
||||
<Location /v1>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
AuthName "Registry Authentication"
|
||||
AuthType basic
|
||||
AuthUserFile "/etc/apache2/htpasswd/registry-htpasswd"
|
||||
|
||||
# Read access to authentified users
|
||||
<Limit GET HEAD>
|
||||
Require valid-user
|
||||
</Limit>
|
||||
|
||||
# Write access to docker-deployer account only
|
||||
<Limit POST PUT DELETE>
|
||||
Require user docker-deployer
|
||||
</Limit>
|
||||
|
||||
</Location>
|
||||
|
||||
# Allow ping to run unauthenticated.
|
||||
<Location /v1/_ping>
|
||||
Satisfy any
|
||||
Allow from all
|
||||
</Location>
|
||||
|
||||
# Allow ping to run unauthenticated.
|
||||
<Location /_ping>
|
||||
Satisfy any
|
||||
Allow from all
|
||||
</Location>
|
||||
|
||||
#
|
||||
# Registry v2
|
||||
#
|
||||
|
||||
ProxyPass /v2 http://localhost:5002/v2
|
||||
ProxyPassReverse /v2 http://localhost:5002/v2
|
||||
|
||||
<Location /v2>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
AuthName "Registry Authentication"
|
||||
AuthType basic
|
||||
AuthUserFile "/etc/apache2/htpasswd/registry-htpasswd"
|
||||
|
||||
# Read access to authentified users
|
||||
<Limit GET HEAD>
|
||||
Require valid-user
|
||||
</Limit>
|
||||
|
||||
# Write access to docker-deployer only
|
||||
<Limit POST PUT DELETE>
|
||||
Require user docker-deployer
|
||||
</Limit>
|
||||
|
||||
</Location>
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
|
147
gateway/vendor/github.com/docker/distribution/contrib/compose/README.md
generated
vendored
147
gateway/vendor/github.com/docker/distribution/contrib/compose/README.md
generated
vendored
@ -1,147 +0,0 @@
|
||||
# Docker Compose V1 + V2 registry
|
||||
|
||||
This compose configuration configures a `v1` and `v2` registry behind an `nginx`
|
||||
proxy. By default, you can access the combined registry at `localhost:5000`.
|
||||
|
||||
The configuration does not support pushing images to `v2` and pulling from `v1`.
|
||||
If a `docker` client has a version less than 1.6, Nginx will route its requests
|
||||
to the 1.0 registry. Requests from newer clients will route to the 2.0 registry.
|
||||
|
||||
### Install Docker Compose
|
||||
|
||||
1. Open a new terminal on the host with your `distribution` source.
|
||||
|
||||
2. Get the `docker-compose` binary.
|
||||
|
||||
$ sudo wget https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` -O /usr/local/bin/docker-compose
|
||||
|
||||
This command installs the binary in the `/usr/local/bin` directory.
|
||||
|
||||
3. Add executable permissions to the binary.
|
||||
|
||||
$ sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
## Build and run with Compose
|
||||
|
||||
1. In your terminal, navigate to the `distribution/contrib/compose` directory
|
||||
|
||||
This directory includes a single `docker-compose.yml` configuration.
|
||||
|
||||
nginx:
|
||||
build: "nginx"
|
||||
ports:
|
||||
- "5000:5000"
|
||||
links:
|
||||
- registryv1:registryv1
|
||||
- registryv2:registryv2
|
||||
registryv1:
|
||||
image: registry
|
||||
ports:
|
||||
- "5000"
|
||||
registryv2:
|
||||
build: "../../"
|
||||
ports:
|
||||
- "5000"
|
||||
|
||||
This configuration builds a new `nginx` image as specified by the
|
||||
`nginx/Dockerfile` file. The 1.0 registry comes from Docker's official
|
||||
public image. Finally, the registry 2.0 image is built from the
|
||||
`distribution/Dockerfile` you've used previously.
|
||||
|
||||
2. Get a registry 1.0 image.
|
||||
|
||||
$ docker pull registry:0.9.1
|
||||
|
||||
The Compose configuration looks for this image locally. If you don't do this
|
||||
step, later steps can fail.
|
||||
|
||||
3. Build `nginx`, the registry 2.0 image, and
|
||||
|
||||
$ docker-compose build
|
||||
registryv1 uses an image, skipping
|
||||
Building registryv2...
|
||||
Step 0 : FROM golang:1.4
|
||||
|
||||
...
|
||||
|
||||
Removing intermediate container 9f5f5068c3f3
|
||||
Step 4 : COPY docker-registry-v2.conf /etc/nginx/docker-registry-v2.conf
|
||||
---> 74acc70fa106
|
||||
Removing intermediate container edb84c2b40cb
|
||||
Successfully built 74acc70fa106
|
||||
|
||||
The commmand outputs its progress until it completes.
|
||||
|
||||
4. Start your configuration with compose.
|
||||
|
||||
$ docker-compose up
|
||||
Recreating compose_registryv1_1...
|
||||
Recreating compose_registryv2_1...
|
||||
Recreating compose_nginx_1...
|
||||
Attaching to compose_registryv1_1, compose_registryv2_1, compose_nginx_1
|
||||
...
|
||||
|
||||
|
||||
5. In another terminal, display the running configuration.
|
||||
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
a81ad2557702 compose_nginx:latest "nginx -g 'daemon of 8 minutes ago Up 8 minutes 80/tcp, 443/tcp, 0.0.0.0:5000->5000/tcp compose_nginx_1
|
||||
0618437450dd compose_registryv2:latest "registry cmd/regist 8 minutes ago Up 8 minutes 0.0.0.0:32777->5000/tcp compose_registryv2_1
|
||||
aa82b1ed8e61 registry:latest "docker-registry" 8 minutes ago Up 8 minutes 0.0.0.0:32776->5000/tcp compose_registryv1_1
|
||||
|
||||
### Explore a bit
|
||||
|
||||
1. Check for TLS on your `nginx` server.
|
||||
|
||||
$ curl -v https://localhost:5000
|
||||
* Rebuilt URL to: https://localhost:5000/
|
||||
* Hostname was NOT found in DNS cache
|
||||
* Trying 127.0.0.1...
|
||||
* Connected to localhost (127.0.0.1) port 5000 (#0)
|
||||
* successfully set certificate verify locations:
|
||||
* CAfile: none
|
||||
CApath: /etc/ssl/certs
|
||||
* SSLv3, TLS handshake, Client hello (1):
|
||||
* SSLv3, TLS handshake, Server hello (2):
|
||||
* SSLv3, TLS handshake, CERT (11):
|
||||
* SSLv3, TLS alert, Server hello (2):
|
||||
* SSL certificate problem: self signed certificate
|
||||
* Closing connection 0
|
||||
curl: (60) SSL certificate problem: self signed certificate
|
||||
More details here: http://curl.haxx.se/docs/sslcerts.html
|
||||
|
||||
2. Tag the `v1` registry image.
|
||||
|
||||
$ docker tag registry:latest localhost:5000/registry_one:latest
|
||||
|
||||
2. Push it to the localhost.
|
||||
|
||||
$ docker push localhost:5000/registry_one:latest
|
||||
|
||||
If you are using the 1.6 Docker client, this pushes the image the `v2 `registry.
|
||||
|
||||
4. Use `curl` to list the image in the registry.
|
||||
|
||||
$ curl -v -X GET http://localhost:32777/v2/registry1/tags/list
|
||||
* Hostname was NOT found in DNS cache
|
||||
* Trying 127.0.0.1...
|
||||
* Connected to localhost (127.0.0.1) port 32777 (#0)
|
||||
> GET /v2/registry1/tags/list HTTP/1.1
|
||||
> User-Agent: curl/7.36.0
|
||||
> Host: localhost:32777
|
||||
> Accept: */*
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=utf-8
|
||||
< Docker-Distribution-Api-Version: registry/2.0
|
||||
< Date: Tue, 14 Apr 2015 22:34:13 GMT
|
||||
< Content-Length: 39
|
||||
<
|
||||
{"name":"registry1","tags":["latest"]}
|
||||
* Connection #0 to host localhost left intact
|
||||
|
||||
This example refers to the specific port assigned to the 2.0 registry. You saw
|
||||
this port earlier, when you used `docker ps` to show your running containers.
|
||||
|
||||
|
15
gateway/vendor/github.com/docker/distribution/contrib/compose/docker-compose.yml
generated
vendored
15
gateway/vendor/github.com/docker/distribution/contrib/compose/docker-compose.yml
generated
vendored
@ -1,15 +0,0 @@
|
||||
nginx:
|
||||
build: "nginx"
|
||||
ports:
|
||||
- "5000:5000"
|
||||
links:
|
||||
- registryv1:registryv1
|
||||
- registryv2:registryv2
|
||||
registryv1:
|
||||
image: registry
|
||||
ports:
|
||||
- "5000"
|
||||
registryv2:
|
||||
build: "../../"
|
||||
ports:
|
||||
- "5000"
|
6
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/Dockerfile
generated
vendored
6
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/Dockerfile
generated
vendored
@ -1,6 +0,0 @@
|
||||
FROM nginx:1.7
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY registry.conf /etc/nginx/conf.d/registry.conf
|
||||
COPY docker-registry.conf /etc/nginx/docker-registry.conf
|
||||
COPY docker-registry-v2.conf /etc/nginx/docker-registry-v2.conf
|
@ -1,6 +0,0 @@
|
||||
proxy_pass http://docker-registry-v2;
|
||||
proxy_set_header Host $http_host; # required for docker client's sake
|
||||
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-Proto $scheme;
|
||||
proxy_read_timeout 900;
|
@ -1,7 +0,0 @@
|
||||
proxy_pass http://docker-registry;
|
||||
proxy_set_header Host $http_host; # required for docker client's sake
|
||||
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-Proto $scheme;
|
||||
proxy_set_header Authorization ""; # For basic auth through nginx in v1 to work, please comment this line
|
||||
proxy_read_timeout 900;
|
27
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/nginx.conf
generated
vendored
27
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/nginx.conf
generated
vendored
@ -1,27 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
|
41
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/registry.conf
generated
vendored
41
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/registry.conf
generated
vendored
@ -1,41 +0,0 @@
|
||||
# Docker registry proxy for api versions 1 and 2
|
||||
|
||||
upstream docker-registry {
|
||||
server registryv1:5000;
|
||||
}
|
||||
|
||||
upstream docker-registry-v2 {
|
||||
server registryv2:5000;
|
||||
}
|
||||
|
||||
# No client auth or TLS
|
||||
server {
|
||||
listen 5000;
|
||||
server_name localhost;
|
||||
|
||||
# disable any limits to avoid HTTP 413 for large image uploads
|
||||
client_max_body_size 0;
|
||||
|
||||
# required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
|
||||
chunked_transfer_encoding on;
|
||||
|
||||
location /v2/ {
|
||||
# Do not allow connections from docker 1.5 and earlier
|
||||
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
|
||||
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
# To add basic authentication to v2 use auth_basic setting plus add_header
|
||||
# auth_basic "registry.localhost";
|
||||
# auth_basic_user_file test.password;
|
||||
# add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
|
||||
|
||||
include docker-registry-v2.conf;
|
||||
}
|
||||
|
||||
location / {
|
||||
include docker-registry.conf;
|
||||
}
|
||||
}
|
||||
|
9
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/Dockerfile
generated
vendored
9
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/Dockerfile
generated
vendored
@ -1,9 +0,0 @@
|
||||
FROM distribution/golem:0.1
|
||||
|
||||
MAINTAINER Docker Distribution Team <distribution@docker.com>
|
||||
|
||||
RUN apk add --no-cache git
|
||||
|
||||
ENV TMPDIR /var/lib/docker/tmp
|
||||
|
||||
WORKDIR /go/src/github.com/docker/distribution/contrib/docker-integration
|
63
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/README.md
generated
vendored
63
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/README.md
generated
vendored
@ -1,63 +0,0 @@
|
||||
# Docker Registry Integration Testing
|
||||
|
||||
These integration tests cover interactions between registry clients such as
|
||||
the docker daemon and the registry server. All tests can be run using the
|
||||
[golem integration test runner](https://github.com/docker/golem)
|
||||
|
||||
The integration tests configure components using docker compose
|
||||
(see docker-compose.yaml) and the runner can be using the golem
|
||||
configuration file (see golem.conf).
|
||||
|
||||
## Running integration tests
|
||||
|
||||
### Run using multiversion script
|
||||
|
||||
The integration tests in the `contrib/docker-integration` directory can be simply
|
||||
run by executing the run script `./run_multiversion.sh`. If there is no running
|
||||
daemon to connect to, run as `./run_multiversion.sh -d`.
|
||||
|
||||
This command will build the distribution image from the locally checked out
|
||||
version and run against multiple versions of docker defined in the script. To
|
||||
run a specific version of the registry or docker, Golem will need to be
|
||||
executed manually.
|
||||
|
||||
### Run manually using Golem
|
||||
|
||||
Using the golem tool directly allows running against multiple versions of
|
||||
the registry and docker. Running against multiple versions of the registry
|
||||
can be useful for testing changes in the docker daemon which are not
|
||||
covered by the default run script.
|
||||
|
||||
#### Installing Golem
|
||||
|
||||
Golem is distributed as an executable binary which can be installed from
|
||||
the [release page](https://github.com/docker/golem/releases/tag/v0.1).
|
||||
|
||||
#### Running golem with docker
|
||||
|
||||
Additionally golem can be run as a docker image requiring no additonal
|
||||
installation.
|
||||
|
||||
`docker run --privileged -v "$GOPATH/src/github.com/docker/distribution/contrib/docker-integration:/test" -w /test distribution/golem golem -rundaemon .`
|
||||
|
||||
#### Golem custom images
|
||||
|
||||
Golem tests version of software by defining the docker image to test.
|
||||
|
||||
Run with registry 2.2.1 and docker 1.10.3
|
||||
|
||||
`golem -i golem-dind:latest,docker:1.10.3-dind,1.10.3 -i golem-distribution:latest,registry:2.2.1 .`
|
||||
|
||||
|
||||
#### Use golem caching for developing tests
|
||||
|
||||
Golem allows caching image configuration to reduce test start up time.
|
||||
Using this cache will allow tests with the same set of images to start
|
||||
up quickly. This can be useful when developing tests and needing the
|
||||
test to run quickly. If there are changes which effect the image (such as
|
||||
building a new registry image), then startup time will be slower.
|
||||
|
||||
Run this command multiple times and after the first time test runs
|
||||
should start much quicker.
|
||||
`golem -cache ~/.cache/docker/golem -i golem-dind:latest,docker:1.10.3-dind,1.10.3 -i golem-distribution:latest,registry:2.2.1 .`
|
||||
|
@ -1,91 +0,0 @@
|
||||
nginx:
|
||||
build: "nginx"
|
||||
ports:
|
||||
- "5000:5000"
|
||||
- "5002:5002"
|
||||
- "5440:5440"
|
||||
- "5441:5441"
|
||||
- "5442:5442"
|
||||
- "5443:5443"
|
||||
- "5444:5444"
|
||||
- "5445:5445"
|
||||
- "5446:5446"
|
||||
- "5447:5447"
|
||||
- "5448:5448"
|
||||
- "5554:5554"
|
||||
- "5555:5555"
|
||||
- "5556:5556"
|
||||
- "5557:5557"
|
||||
- "5558:5558"
|
||||
- "5559:5559"
|
||||
- "5600:5600"
|
||||
- "6666:6666"
|
||||
links:
|
||||
- registryv2:registryv2
|
||||
- malevolent:malevolent
|
||||
- registryv2token:registryv2token
|
||||
- tokenserver:tokenserver
|
||||
- registryv2tokenoauth:registryv2tokenoauth
|
||||
- registryv2tokenoauthnotls:registryv2tokenoauthnotls
|
||||
- tokenserveroauth:tokenserveroauth
|
||||
registryv2:
|
||||
image: golem-distribution:latest
|
||||
ports:
|
||||
- "5000"
|
||||
registryv2token:
|
||||
image: golem-distribution:latest
|
||||
ports:
|
||||
- "5000"
|
||||
volumes:
|
||||
- ./tokenserver/registry-config.yml:/etc/docker/registry/config.yml
|
||||
- ./tokenserver/certs/localregistry.cert:/etc/docker/registry/localregistry.cert
|
||||
- ./tokenserver/certs/localregistry.key:/etc/docker/registry/localregistry.key
|
||||
- ./tokenserver/certs/signing.cert:/etc/docker/registry/tokenbundle.pem
|
||||
tokenserver:
|
||||
build: "tokenserver"
|
||||
command: "--debug -addr 0.0.0.0:5556 -issuer registry-test -passwd .htpasswd -tlscert tls.cert -tlskey tls.key -key sign.key -realm http://auth.localregistry:5556"
|
||||
ports:
|
||||
- "5556"
|
||||
registryv2tokenoauth:
|
||||
image: golem-distribution:latest
|
||||
ports:
|
||||
- "5000"
|
||||
volumes:
|
||||
- ./tokenserver-oauth/registry-config.yml:/etc/docker/registry/config.yml
|
||||
- ./tokenserver-oauth/certs/localregistry.cert:/etc/docker/registry/localregistry.cert
|
||||
- ./tokenserver-oauth/certs/localregistry.key:/etc/docker/registry/localregistry.key
|
||||
- ./tokenserver-oauth/certs/signing.cert:/etc/docker/registry/tokenbundle.pem
|
||||
registryv2tokenoauthnotls:
|
||||
image: golem-distribution:latest
|
||||
ports:
|
||||
- "5000"
|
||||
volumes:
|
||||
- ./tokenserver-oauth/registry-config-notls.yml:/etc/docker/registry/config.yml
|
||||
- ./tokenserver-oauth/certs/signing.cert:/etc/docker/registry/tokenbundle.pem
|
||||
tokenserveroauth:
|
||||
build: "tokenserver-oauth"
|
||||
command: "--debug -addr 0.0.0.0:5559 -issuer registry-test -passwd .htpasswd -tlscert tls.cert -tlskey tls.key -key sign.key -realm http://auth.localregistry:5559 -enforce-class"
|
||||
ports:
|
||||
- "5559"
|
||||
malevolent:
|
||||
image: "dmcgowan/malevolent:0.1.0"
|
||||
command: "-l 0.0.0.0:6666 -r http://registryv2:5000 -c /certs/localregistry.cert -k /certs/localregistry.key"
|
||||
links:
|
||||
- registryv2:registryv2
|
||||
volumes:
|
||||
- ./malevolent-certs:/certs:ro
|
||||
ports:
|
||||
- "6666"
|
||||
docker:
|
||||
image: golem-dind:latest
|
||||
container_name: dockerdaemon
|
||||
command: "docker daemon --debug -s $DOCKER_GRAPHDRIVER"
|
||||
privileged: true
|
||||
environment:
|
||||
DOCKER_GRAPHDRIVER:
|
||||
volumes:
|
||||
- /etc/generated_certs.d:/etc/docker/certs.d
|
||||
- /var/lib/docker
|
||||
links:
|
||||
- nginx:localregistry
|
||||
- nginx:auth.localregistry
|
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/golem.conf
generated
vendored
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/golem.conf
generated
vendored
@ -1,18 +0,0 @@
|
||||
[[suite]]
|
||||
dind=true
|
||||
images=[ "nginx:1.9", "dmcgowan/token-server:simple", "dmcgowan/token-server:oauth", "dmcgowan/malevolent:0.1.0", "dmcgowan/ncat:latest" ]
|
||||
|
||||
[[suite.pretest]]
|
||||
command="sh ./install_certs.sh /etc/generated_certs.d"
|
||||
[[suite.testrunner]]
|
||||
command="bats -t ."
|
||||
format="tap"
|
||||
env=["TEST_REPO=hello-world", "TEST_TAG=latest", "TEST_USER=testuser", "TEST_PASSWORD=passpassword", "TEST_REGISTRY=localregistry", "TEST_SKIP_PULL=true"]
|
||||
[[suite.customimage]]
|
||||
tag="golem-distribution:latest"
|
||||
default="registry:2.2.1"
|
||||
[[suite.customimage]]
|
||||
tag="golem-dind:latest"
|
||||
default="docker:1.10.1-dind"
|
||||
version="1.10.1"
|
||||
|
127
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/helpers.bash
generated
vendored
127
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/helpers.bash
generated
vendored
@ -1,127 +0,0 @@
|
||||
# has_digest enforces the last output line is "Digest: sha256:..."
|
||||
# the input is the output from a docker push cli command
|
||||
function has_digest() {
|
||||
filtered=$(echo "$1" |sed -rn '/[dD]igest\: sha(256|384|512)/ p')
|
||||
[ "$filtered" != "" ]
|
||||
# See http://wiki.alpinelinux.org/wiki/Regex#BREs before making changes to regex
|
||||
digest=$(expr "$filtered" : ".*\(sha[0-9]\{3,3\}:[a-z0-9]*\)")
|
||||
}
|
||||
|
||||
# tempImage creates a new image using the provided name
|
||||
# requires bats
|
||||
function tempImage() {
|
||||
dir=$(mktemp -d)
|
||||
run dd if=/dev/urandom of="$dir/f" bs=1024 count=512
|
||||
cat <<DockerFileContent > "$dir/Dockerfile"
|
||||
FROM scratch
|
||||
COPY f /f
|
||||
|
||||
CMD []
|
||||
DockerFileContent
|
||||
|
||||
cp_t $dir "/tmpbuild/"
|
||||
exec_t "cd /tmpbuild/; docker build --no-cache -t $1 .; rm -rf /tmpbuild/"
|
||||
}
|
||||
|
||||
# skip basic auth tests with Docker 1.6, where they don't pass due to
|
||||
# certificate issues, requires bats
|
||||
function basic_auth_version_check() {
|
||||
run sh -c 'docker version | fgrep -q "Client version: 1.6."'
|
||||
if [ "$status" -eq 0 ]; then
|
||||
skip "Basic auth tests don't support 1.6.x"
|
||||
fi
|
||||
}
|
||||
|
||||
email="a@nowhere.com"
|
||||
|
||||
# docker_t_login calls login with email depending on version
|
||||
function docker_t_login() {
|
||||
# Only pass email field pre 1.11, no deprecation warning
|
||||
parse_version "$GOLEM_DIND_VERSION"
|
||||
v=$version
|
||||
parse_version "1.11.0"
|
||||
if [ "$v" -lt "$version" ]; then
|
||||
run docker_t login -e $email $@
|
||||
else
|
||||
run docker_t login $@
|
||||
fi
|
||||
}
|
||||
|
||||
# login issues a login to docker to the provided server
|
||||
# uses user, password, and email variables set outside of function
|
||||
# requies bats
|
||||
function login() {
|
||||
rm -f /root/.docker/config.json
|
||||
|
||||
docker_t_login -u $user -p $password $1
|
||||
if [ "$status" -ne 0 ]; then
|
||||
echo $output
|
||||
fi
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Handle different deprecation warnings
|
||||
parse_version "$GOLEM_DIND_VERSION"
|
||||
v=$version
|
||||
parse_version "1.11.0"
|
||||
if [ "$v" -lt "$version" ]; then
|
||||
# First line is WARNING about credential save or email deprecation (maybe both)
|
||||
[ "${lines[2]}" = "Login Succeeded" -o "${lines[1]}" = "Login Succeeded" ]
|
||||
else
|
||||
[ "${lines[0]}" = "Login Succeeded" ]
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function login_oauth() {
|
||||
login $@
|
||||
|
||||
tmpFile=$(mktemp)
|
||||
get_file_t /root/.docker/config.json $tmpFile
|
||||
run awk -v RS="" "/\"$1\": \\{[[:space:]]+\"auth\": \"[[:alnum:]]+\",[[:space:]]+\"identitytoken\"/ {exit 3}" $tmpFile
|
||||
[ "$status" -eq 3 ]
|
||||
}
|
||||
|
||||
function parse_version() {
|
||||
version=$(echo "$1" | cut -d '-' -f1) # Strip anything after '-'
|
||||
major=$(echo "$version" | cut -d . -f1)
|
||||
minor=$(echo "$version" | cut -d . -f2)
|
||||
rev=$(echo "$version" | cut -d . -f3)
|
||||
|
||||
version=$((major * 1000 * 1000 + minor * 1000 + rev))
|
||||
}
|
||||
|
||||
function version_check() {
|
||||
name=$1
|
||||
checkv=$2
|
||||
minv=$3
|
||||
parse_version "$checkv"
|
||||
v=$version
|
||||
parse_version "$minv"
|
||||
if [ "$v" -lt "$version" ]; then
|
||||
skip "$name version \"$checkv\" does not meet required version \"$minv\""
|
||||
fi
|
||||
}
|
||||
|
||||
function get_file_t() {
|
||||
docker cp dockerdaemon:$1 $2
|
||||
}
|
||||
|
||||
function cp_t() {
|
||||
docker cp $1 dockerdaemon:$2
|
||||
}
|
||||
|
||||
function exec_t() {
|
||||
docker exec dockerdaemon sh -c "$@"
|
||||
}
|
||||
|
||||
function docker_t() {
|
||||
docker exec dockerdaemon docker $@
|
||||
}
|
||||
|
||||
# build creates a new docker image id from another image
|
||||
function build() {
|
||||
docker exec -i dockerdaemon docker build --no-cache -t $1 - <<DOCKERFILE
|
||||
FROM $2
|
||||
MAINTAINER distribution@docker.com
|
||||
DOCKERFILE
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
hostname="localregistry"
|
||||
installdir="$1"
|
||||
|
||||
install_ca() {
|
||||
mkdir -p $1/$hostname:$2
|
||||
cp ./nginx/ssl/registry-ca+ca.pem $1/$hostname:$2/ca.crt
|
||||
if [ "$3" != "" ]; then
|
||||
cp ./nginx/ssl/registry-$3+client-cert.pem $1/$hostname:$2/client.cert
|
||||
cp ./nginx/ssl/registry-$3+client-key.pem $1/$hostname:$2/client.key
|
||||
fi
|
||||
}
|
||||
|
||||
install_test_certs() {
|
||||
install_ca $1 5440
|
||||
install_ca $1 5441
|
||||
install_ca $1 5442 ca
|
||||
install_ca $1 5443 noca
|
||||
install_ca $1 5444 ca
|
||||
install_ca $1 5447 ca
|
||||
# For test remove CA
|
||||
rm $1/${hostname}:5447/ca.crt
|
||||
install_ca $1 5448
|
||||
install_ca $1 5600
|
||||
}
|
||||
|
||||
install_ca_file() {
|
||||
mkdir -p $2
|
||||
cp $1 $2/ca.crt
|
||||
}
|
||||
|
||||
append_ca_file() {
|
||||
mkdir -p $2
|
||||
cat $1 >> $2/ca.crt
|
||||
}
|
||||
|
||||
install_test_certs $installdir
|
||||
|
||||
# Malevolent server
|
||||
install_ca_file ./malevolent-certs/ca.pem $installdir/$hostname:6666
|
||||
|
||||
# Token server
|
||||
install_ca_file ./tokenserver/certs/ca.pem $installdir/$hostname:5554
|
||||
install_ca_file ./tokenserver/certs/ca.pem $installdir/$hostname:5555
|
||||
install_ca_file ./tokenserver/certs/ca.pem $installdir/$hostname:5557
|
||||
install_ca_file ./tokenserver/certs/ca.pem $installdir/$hostname:5558
|
||||
append_ca_file ./tokenserver/certs/ca.pem $installdir/$hostname:5600
|
||||
|
@ -1,19 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfugAwIBAgIQZRKt7OeG+TlC2riszYwQQTALBgkqhkiG9w0BAQswJjER
|
||||
MA8GA1UEChMIUXVpY2tUTFMxETAPBgNVBAMTCFF1aWNrVExTMB4XDTE1MDgyMDIz
|
||||
MjE0OVoXDTE4MDgwNDIzMjE0OVowKzERMA8GA1UEChMIUXVpY2tUTFMxFjAUBgNV
|
||||
BAMTDWxvY2FscmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDPdsUBStNMz4coXfQVIJIafG85VkngM4fV7hrg7AbiGLCWvq8cWOrYM50G9Wmo
|
||||
twK1WeQ6bigYOjINgSfTxcy3adciVZIIJyXqboz6n2V0yRPWpakof939bvuAurAP
|
||||
tSqQ2V5fGN0ZZn4J4IbXMSovKwo7sG3X6i4q/8DYHZ/mKjvCRMPC3MGWqunknpkm
|
||||
dzyKbIFHaDKlAqIOwTsDhHvGzm/9n3D+h4sl5ZPBobuBEV2u5GR0H5ujak4+Kczt
|
||||
thCWtRkzCfnjW0TEanheSYJGu8OgCGoFjQnHotgqvOO6iHZCsrB3gf8WQeou+y9e
|
||||
+OyLZv3FmqdC9SXr3b0LGQTFAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIAoDAMBgNV
|
||||
HRMBAf8EAjAAMBgGA1UdEQQRMA+CDWxvY2FscmVnaXN0cnkwCwYJKoZIhvcNAQEL
|
||||
A4IBAQC/PP2Y9QVhO8t4BXML1QpNRWqXG8Gg0P1XIh6M6FoxcGIodLdbzui828YB
|
||||
wm9ZlyKars+nDdgLdQWawdV7hSd6s2NeQlHYQSGLsdTAVkgIxiD7D2Tw3kAZ6Zrj
|
||||
dPikoVAc+rBMm/BXQLzy95IAbBVOHOpBkOOgF+TYxeLnOc3GzbUqBi1Pq97DMaxr
|
||||
DaDuywH55P/6v7qt610UIsZ6+RZ78iiRx4Q+oRxEqGT0rXI76gVxOFabbJuFr1n1
|
||||
kEWa3u/BssJzX3KVAm7oUtaBnj2SH5fokFmvZ5lBXA4QO/5doOa8yZiFFvvQs7EY
|
||||
SWDxLrvS33UCtsCcpPggjehnxKaC
|
||||
-----END CERTIFICATE-----
|
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAz3bFAUrTTM+HKF30FSCSGnxvOVZJ4DOH1e4a4OwG4hiwlr6v
|
||||
HFjq2DOdBvVpqLcCtVnkOm4oGDoyDYEn08XMt2nXIlWSCCcl6m6M+p9ldMkT1qWp
|
||||
KH/d/W77gLqwD7UqkNleXxjdGWZ+CeCG1zEqLysKO7Bt1+ouKv/A2B2f5io7wkTD
|
||||
wtzBlqrp5J6ZJnc8imyBR2gypQKiDsE7A4R7xs5v/Z9w/oeLJeWTwaG7gRFdruRk
|
||||
dB+bo2pOPinM7bYQlrUZMwn541tExGp4XkmCRrvDoAhqBY0Jx6LYKrzjuoh2QrKw
|
||||
d4H/FkHqLvsvXvjsi2b9xZqnQvUl6929CxkExQIDAQABAoIBAQCZjCUI7NFwwxQc
|
||||
m1UAogeglMJZJHUu+9SoUD8Sg34grvdbyqueBm1iMOkiclaOKU1W3b4eRNNmAwRy
|
||||
nEnW4km+4hX48m5PnHHijYnIIFsd0YjeT+Pf9qtdXFvGjeWq6oIjjM3dAnD50LKu
|
||||
KsCB2oCHQoqjXNQfftJGvt2C1oI2/WvdOR4prnGXElVfASswX4PkP5LCfLhIx+Fr
|
||||
7ErfaRIKigLSaAWLKaw3IlL12Q/KkuGcnzYIzIRwY4VJ64ENN6M3+KknfGovQItL
|
||||
sCxceSe61THDP9AAI3Mequm8z3H0CImOWhJCge5l7ttLLMXZXqGxDCVx+3zvqlCa
|
||||
X0cgGSVBAoGBAOvTN3oJJx1vnh1mRj8+hqzFq1bjm4T/Wp314QWLeo++43II4uMM
|
||||
5hxUlO5ViY1sKxQrGwK+9c9ddxAvm5OAFFkzgW9EhDCu0tXUb2/vAJQ93SgqbcRu
|
||||
coXWJpk0eNW/ouk2s1X8dzs+sCs3a4H64fEEj8yhwoyovjfucspsn7t1AoGBAOE2
|
||||
ayLKx7CcWCiD/VGNvP7714MDst2isyq8reg8LEMmAaXR2IWWj5eGwKrImTQCsrjW
|
||||
P37aBp1lcWuuYRKl/WEGBy6JLNdATyUoYc1Yo+8YdenekkOtOHHJerlK3OKi3ZVp
|
||||
q4HJY9wzKg/wYLcbTmjjzKj+OBIZWwig73XUHwoRAoGBAJnuIrYbp1aFdvXFvnCl
|
||||
xY6c8DwlEWx8qY+V4S2XX4bYmOnkdwSxdLplU1lGqCSRyIS/pj/imdyjK4Z7LNfY
|
||||
sG+RORmB5a9JTgGZSqwLm5snzmXbXA7t8P7/S+6Q25baIeKMe/7SbplTT/bFk/0h
|
||||
371MtvhhVfYuZwtnL7KFuLXJAoGBAMQ3UHKYsBC8tsZd8Pf8AL07mFHKiC04Etfa
|
||||
Wb5rpri+RVM+mGITgnmnavehHHHHJAWMjPetZ3P8rSv/Ww4PVsoQoXM3Cr1jh1E9
|
||||
dLCfWPz4l8syIscaBYKF4wnLItXGxj3mOgoy93EjlrMaYHlILjGOv4JBM4L5WmoT
|
||||
JW7IaF6xAoGAZ4K8MwU/cAah8VinMmLGxvWWuBSgTTebuY5zN603MvFLKv5necuc
|
||||
BZfTTxD+gOnxRT6QAh++tOsbBmsgR9HmTSlQSSgw1L7cwGyXzLCDYw+5K/03KXSU
|
||||
DaFdgtfcDDJO8WtjOgjyTRzEAOsqFta1ige4pIu5fTilNVMQlhts5Iw=
|
||||
-----END RSA PRIVATE KEY-----
|
192
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/malevolent.bats
generated
vendored
192
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/malevolent.bats
generated
vendored
@ -1,192 +0,0 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# This tests various expected error scenarios when pulling bad content
|
||||
|
||||
load helpers
|
||||
|
||||
host="localregistry:6666"
|
||||
base="malevolent-test"
|
||||
|
||||
function setup() {
|
||||
tempImage $base:latest
|
||||
}
|
||||
|
||||
@test "Test malevolent proxy pass through" {
|
||||
docker_t tag $base:latest $host/$base/nochange:latest
|
||||
run docker_t push $host/$base/nochange:latest
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
run docker_t pull $host/$base/nochange:latest
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "Test malevolent image name change" {
|
||||
imagename="$host/$base/rename"
|
||||
image="$imagename:lastest"
|
||||
docker_t tag $base:latest $image
|
||||
run docker_t push $image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
# Pull attempt should fail to verify manifest digest
|
||||
run docker_t pull "$imagename@$digest"
|
||||
echo "$output"
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test malevolent altered layer" {
|
||||
image="$host/$base/addfile:latest"
|
||||
tempImage $image
|
||||
run docker_t push $image
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
# Remove image to ensure layer is pulled and digest verified
|
||||
docker_t rmi -f $image
|
||||
|
||||
run docker_t pull $image
|
||||
echo "$output"
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test malevolent altered layer (by digest)" {
|
||||
imagename="$host/$base/addfile"
|
||||
image="$imagename:latest"
|
||||
tempImage $image
|
||||
run docker_t push $image
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
# Remove image to ensure layer is pulled and digest verified
|
||||
docker_t rmi -f $image
|
||||
|
||||
run docker_t pull "$imagename@$digest"
|
||||
echo "$output"
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test malevolent poisoned images" {
|
||||
truncid="777cf9284131"
|
||||
poison="${truncid}d77ca0863fb7f054c0a276d7e227b5e9a5d62b497979a481fa32"
|
||||
image1="$host/$base/image1/poison:$poison"
|
||||
tempImage $image1
|
||||
run docker_t push $image1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
image2="$host/$base/image2/poison:$poison"
|
||||
tempImage $image2
|
||||
run docker_t push $image2
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
|
||||
# Remove image to ensure layer is pulled and digest verified
|
||||
docker_t rmi -f $image1
|
||||
docker_t rmi -f $image2
|
||||
|
||||
run docker_t pull $image1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_t pull $image2
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Test if there are multiple images
|
||||
run docker_t images
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Test images have same ID and not the poison
|
||||
id1=$(docker_t inspect --format="{{.Id}}" $image1)
|
||||
id2=$(docker_t inspect --format="{{.Id}}" $image2)
|
||||
|
||||
# Remove old images
|
||||
docker_t rmi -f $image1
|
||||
docker_t rmi -f $image2
|
||||
|
||||
[ "$id1" != "$id2" ]
|
||||
|
||||
[ "$id1" != "$truncid" ]
|
||||
|
||||
[ "$id2" != "$truncid" ]
|
||||
}
|
||||
|
||||
@test "Test malevolent altered identical images" {
|
||||
truncid1="777cf9284131"
|
||||
poison1="${truncid1}d77ca0863fb7f054c0a276d7e227b5e9a5d62b497979a481fa32"
|
||||
truncid2="888cf9284131"
|
||||
poison2="${truncid2}d77ca0863fb7f054c0a276d7e227b5e9a5d62b497979a481fa64"
|
||||
|
||||
image1="$host/$base/image1/alteredid:$poison1"
|
||||
tempImage $image1
|
||||
run docker_t push $image1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
image2="$host/$base/image2/alteredid:$poison2"
|
||||
docker_t tag $image1 $image2
|
||||
run docker_t push $image2
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
|
||||
# Remove image to ensure layer is pulled and digest verified
|
||||
docker_t rmi -f $image1
|
||||
docker_t rmi -f $image2
|
||||
|
||||
run docker_t pull $image1
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_t pull $image2
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Test if there are multiple images
|
||||
run docker_t images
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Test images have same ID and not the poison
|
||||
id1=$(docker_t inspect --format="{{.Id}}" $image1)
|
||||
id2=$(docker_t inspect --format="{{.Id}}" $image2)
|
||||
|
||||
# Remove old images
|
||||
docker_t rmi -f $image1
|
||||
docker_t rmi -f $image2
|
||||
|
||||
[ "$id1" == "$id2" ]
|
||||
|
||||
[ "$id1" != "$truncid1" ]
|
||||
|
||||
[ "$id2" != "$truncid2" ]
|
||||
}
|
||||
|
||||
@test "Test malevolent resumeable pull" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.11.0"
|
||||
version_check registry "$GOLEM_DISTRIBUTION_VERSION" "2.3.0"
|
||||
|
||||
imagename="$host/$base/resumeable"
|
||||
image="$imagename:latest"
|
||||
tempImage $image
|
||||
run docker_t push $image
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
# Remove image to ensure layer is pulled and digest verified
|
||||
docker_t rmi -f $image
|
||||
|
||||
run docker_t pull "$imagename@$digest"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
FROM nginx:1.9
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY registry.conf /etc/nginx/conf.d/registry.conf
|
||||
COPY docker-registry-v2.conf /etc/nginx/docker-registry-v2.conf
|
||||
COPY registry-noauth.conf /etc/nginx/registry-noauth.conf
|
||||
COPY registry-basic.conf /etc/nginx/registry-basic.conf
|
||||
COPY test.passwd /etc/nginx/test.passwd
|
||||
COPY ssl /etc/nginx/ssl
|
||||
COPY v1 /var/www/html/v1
|
@ -1,6 +0,0 @@
|
||||
proxy_pass http://docker-registry-v2;
|
||||
proxy_set_header Host $http_host; # required for docker client's sake
|
||||
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-Proto $scheme;
|
||||
proxy_read_timeout 900;
|
@ -1,61 +0,0 @@
|
||||
user nginx;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
|
||||
# Setup TCP proxies
|
||||
stream {
|
||||
# Malevolent proxy
|
||||
server {
|
||||
listen 6666;
|
||||
proxy_pass malevolent:6666;
|
||||
}
|
||||
|
||||
# Registry configured for token server
|
||||
server {
|
||||
listen 5554;
|
||||
listen 5555;
|
||||
proxy_pass registryv2token:5000;
|
||||
}
|
||||
|
||||
# Token server
|
||||
server {
|
||||
listen 5556;
|
||||
proxy_pass tokenserver:5556;
|
||||
}
|
||||
|
||||
# Registry configured for token server with oauth
|
||||
server {
|
||||
listen 5557;
|
||||
listen 5558;
|
||||
proxy_pass registryv2tokenoauth:5000;
|
||||
}
|
||||
|
||||
# Token server with oauth
|
||||
server {
|
||||
listen 5559;
|
||||
proxy_pass tokenserveroauth:5559;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
client_max_body_size 0;
|
||||
chunked_transfer_encoding on;
|
||||
location /v2/ {
|
||||
auth_basic "registry.localhost";
|
||||
auth_basic_user_file test.passwd;
|
||||
add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
|
||||
include docker-registry-v2.conf;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
client_max_body_size 0;
|
||||
chunked_transfer_encoding on;
|
||||
location /v2/ {
|
||||
include docker-registry-v2.conf;
|
||||
}
|
260
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/registry.conf
generated
vendored
260
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/registry.conf
generated
vendored
@ -1,260 +0,0 @@
|
||||
# Docker registry proxy for api version 2
|
||||
|
||||
upstream docker-registry-v2 {
|
||||
server registryv2:5000;
|
||||
}
|
||||
|
||||
# No client auth or TLS
|
||||
server {
|
||||
listen 5000;
|
||||
server_name localhost;
|
||||
|
||||
# disable any limits to avoid HTTP 413 for large image uploads
|
||||
client_max_body_size 0;
|
||||
|
||||
# required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
|
||||
chunked_transfer_encoding on;
|
||||
|
||||
location /v2/ {
|
||||
# Do not allow connections from docker 1.5 and earlier
|
||||
# docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
|
||||
if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
|
||||
return 404;
|
||||
}
|
||||
|
||||
include docker-registry-v2.conf;
|
||||
}
|
||||
}
|
||||
|
||||
# No client auth or TLS (V2 Only)
|
||||
server {
|
||||
listen 5002;
|
||||
server_name localhost;
|
||||
|
||||
# disable any limits to avoid HTTP 413 for large image uploads
|
||||
client_max_body_size 0;
|
||||
|
||||
# required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
|
||||
chunked_transfer_encoding on;
|
||||
|
||||
location / {
|
||||
include docker-registry-v2.conf;
|
||||
}
|
||||
}
|
||||
|
||||
# TLS Configuration chart
|
||||
# Username/Password: testuser/passpassword
|
||||
# | ca | client | basic | notes
|
||||
# 5440 | yes | no | no | Tests CA certificate
|
||||
# 5441 | yes | no | yes | Tests basic auth over TLS
|
||||
# 5442 | yes | yes | no | Tests client auth with client CA
|
||||
# 5443 | yes | yes | no | Tests client auth without client CA
|
||||
# 5444 | yes | yes | yes | Tests using basic auth + tls auth
|
||||
# 5445 | no | no | no | Tests insecure using TLS
|
||||
# 5446 | no | no | yes | Tests sending credentials to server with insecure TLS
|
||||
# 5447 | no | yes | no | Tests client auth to insecure
|
||||
# 5448 | yes | no | no | Bad SSL version
|
||||
|
||||
server {
|
||||
listen 5440;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localhost-key.pem;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5441;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localhost-key.pem;
|
||||
include registry-basic.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5442;
|
||||
listen 5443;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localhost-key.pem;
|
||||
ssl_client_certificate /etc/nginx/ssl/registry-ca+ca.pem;
|
||||
ssl_verify_client on;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5444;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localhost-key.pem;
|
||||
ssl_client_certificate /etc/nginx/ssl/registry-ca+ca.pem;
|
||||
ssl_verify_client on;
|
||||
include registry-basic.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5445;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-noca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-noca+localhost-key.pem;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5446;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-noca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-noca+localhost-key.pem;
|
||||
include registry-basic.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5447;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-noca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-noca+localhost-key.pem;
|
||||
ssl_client_certificate /etc/nginx/ssl/registry-ca+ca.pem;
|
||||
ssl_verify_client on;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5448;
|
||||
server_name localhost;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localhost-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localhost-key.pem;
|
||||
ssl_protocols SSLv3;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
# Add configuration for localregistry server_name
|
||||
# Requires configuring /etc/hosts to use
|
||||
# Set /etc/hosts entry to external IP, not 127.0.0.1 for testing
|
||||
# Docker secure/insecure registry features
|
||||
server {
|
||||
listen 5440;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localregistry-key.pem;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5441;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localregistry-key.pem;
|
||||
include registry-basic.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5442;
|
||||
listen 5443;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localregistry-key.pem;
|
||||
ssl_client_certificate /etc/nginx/ssl/registry-ca+ca.pem;
|
||||
ssl_verify_client on;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5444;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localregistry-key.pem;
|
||||
ssl_client_certificate /etc/nginx/ssl/registry-ca+ca.pem;
|
||||
ssl_verify_client on;
|
||||
include registry-basic.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5445;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-noca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-noca+localregistry-key.pem;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5446;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-noca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-noca+localregistry-key.pem;
|
||||
include registry-basic.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5447;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-noca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-noca+localregistry-key.pem;
|
||||
ssl_client_certificate /etc/nginx/ssl/registry-ca+ca.pem;
|
||||
ssl_verify_client on;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5448;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localregistry-key.pem;
|
||||
ssl_protocols SSLv3;
|
||||
include registry-noauth.conf;
|
||||
}
|
||||
|
||||
|
||||
# V1 search test
|
||||
# Registry configured with token auth and no tls
|
||||
# TLS termination done by nginx, search results
|
||||
# served by nginx
|
||||
|
||||
upstream docker-registry-v2-oauth {
|
||||
server registryv2tokenoauthnotls:5000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 5600;
|
||||
server_name localregistry;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/registry-ca+localregistry-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/registry-ca+localregistry-key.pem;
|
||||
|
||||
root /var/www/html;
|
||||
|
||||
client_max_body_size 0;
|
||||
chunked_transfer_encoding on;
|
||||
location /v2/ {
|
||||
proxy_buffering off;
|
||||
proxy_pass http://docker-registry-v2-oauth;
|
||||
proxy_set_header Host $http_host; # required for docker client's sake
|
||||
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-Proto $scheme;
|
||||
proxy_read_timeout 900;
|
||||
}
|
||||
|
||||
location /v1/search {
|
||||
if ($http_authorization !~ "Bearer [a-zA-Z0-9\._-]+") {
|
||||
return 401;
|
||||
}
|
||||
try_files /v1/search.json =404;
|
||||
add_header Content-Type application/json;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
testuser:$apr1$YmLhHjm6$AjP4z8J1WgcUNxU8J4ue5.
|
@ -1 +0,0 @@
|
||||
{"num_pages":1,"num_results":2,"page":1,"page_size": 25,"query":"testsearch","results":[{"description":"","is_automated":false,"is_official":false,"is_trusted":false, "name":"dmcgowan/testsearch-1","star_count":1000},{"description":"Some automated build","is_automated":true,"is_official":false,"is_trusted":false,"name":"dmcgowan/testsearch-2","star_count":10}]}
|
103
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/plugins.bats
generated
vendored
103
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/plugins.bats
generated
vendored
@ -1,103 +0,0 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# This tests pushing and pulling plugins
|
||||
|
||||
load helpers
|
||||
|
||||
user="testuser"
|
||||
password="testpassword"
|
||||
base="hello-world"
|
||||
|
||||
#TODO: Create plugin image
|
||||
function create_plugin() {
|
||||
plugindir=$(mktemp -d)
|
||||
|
||||
cat - > $plugindir/config.json <<CONFIGJSON
|
||||
{
|
||||
"manifestVersion": "v0",
|
||||
"description": "A test plugin for integration tests",
|
||||
"entrypoint": ["/usr/bin/ncat", "-l", "-U", "//run/docker/plugins/plugin.sock"],
|
||||
"interface" : {
|
||||
"types": ["docker.volumedriver/1.0"],
|
||||
"socket": "plugin.sock"
|
||||
}
|
||||
}
|
||||
CONFIGJSON
|
||||
|
||||
cid=$(docker create dmcgowan/ncat:latest /bin/sh)
|
||||
|
||||
mkdir $plugindir/rootfs
|
||||
|
||||
docker export $cid | tar -x -C $plugindir/rootfs
|
||||
|
||||
docker rm $cid
|
||||
|
||||
daemontmp=$(docker exec dockerdaemon mktemp -d)
|
||||
|
||||
tar -c -C $plugindir . | docker exec -i dockerdaemon tar -x -C $daemontmp
|
||||
|
||||
docker exec dockerdaemon docker plugin create $1 $daemontmp
|
||||
|
||||
docker exec dockerdaemon rm -rf $daemontmp
|
||||
|
||||
rm -rf $plugindir
|
||||
}
|
||||
|
||||
@test "Test plugin push and pull" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.13.0-rc3"
|
||||
version_check docker "$GOLEM_DISTRIBUTION_VERSION" "2.6.0"
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/plugin1"
|
||||
|
||||
create_plugin $image
|
||||
|
||||
run docker_t plugin push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t plugin rm $image
|
||||
|
||||
docker_t plugin install --grant-all-permissions $image
|
||||
}
|
||||
|
||||
@test "Test plugin push and failed image pull" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.13.0-rc3"
|
||||
version_check docker "$GOLEM_DISTRIBUTION_VERSION" "2.6.0"
|
||||
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/plugin-not-image"
|
||||
|
||||
create_plugin $image
|
||||
|
||||
run docker_t plugin push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t plugin rm $image
|
||||
|
||||
run docker_t pull $image
|
||||
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test image push and failed plugin pull" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.13.0-rc3"
|
||||
version_check docker "$GOLEM_DISTRIBUTION_VERSION" "2.6.0"
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/image-not-plugin"
|
||||
|
||||
build $image "$base:latest"
|
||||
|
||||
run docker_t push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t rmi $image
|
||||
|
||||
run docker_t plugin install --grant-all-permissions $image
|
||||
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Run the integration tests with multiple versions of the Docker engine
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
|
||||
if [ "$TMPDIR" != "" ] && [ ! -d "$TMPDIR" ]; then
|
||||
mkdir -p $TMPDIR
|
||||
fi
|
||||
|
||||
cachedir=`mktemp -t -d golem-cache.XXXXXX`
|
||||
trap "rm -rf $cachedir" EXIT
|
||||
|
||||
if [ "$1" == "-d" ]; then
|
||||
# Drivers to use for Docker engines the tests are going to create.
|
||||
STORAGE_DRIVER=${STORAGE_DRIVER:-overlay}
|
||||
|
||||
docker daemon --log-level=panic --storage-driver="$STORAGE_DRIVER" &
|
||||
DOCKER_PID=$!
|
||||
|
||||
# Wait for it to become reachable.
|
||||
tries=10
|
||||
until docker version &> /dev/null; do
|
||||
(( tries-- ))
|
||||
if [ $tries -le 0 ]; then
|
||||
echo >&2 "error: daemon failed to start"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
trap "kill $DOCKER_PID" EXIT
|
||||
fi
|
||||
|
||||
distimage=$(docker build -q $DIR/../..)
|
||||
fullversion=$(git describe --match 'v[0-9]*' --dirty='.m' --always)
|
||||
distversion=${fullversion:1}
|
||||
|
||||
echo "Testing image $distimage with distribution version $distversion"
|
||||
|
||||
# Pull needed images before invoking golem to get pull time
|
||||
# These images are defined in golem.conf
|
||||
time docker pull nginx:1.9
|
||||
time docker pull golang:1.6
|
||||
time docker pull dmcgowan/token-server:simple
|
||||
time docker pull dmcgowan/token-server:oauth
|
||||
time docker pull distribution/golem-runner:0.1-bats
|
||||
|
||||
time docker pull docker:1.9.1-dind
|
||||
time docker pull docker:1.10.3-dind
|
||||
time docker pull docker:1.11.1-dind
|
||||
time docker pull docker:1.12.3-dind
|
||||
time docker pull docker:1.13.0-rc5-dind
|
||||
|
||||
golem -cache $cachedir \
|
||||
-i "golem-distribution:latest,$distimage,$distversion" \
|
||||
-i "golem-dind:latest,docker:1.9.1-dind,1.9.1" \
|
||||
-i "golem-dind:latest,docker:1.10.3-dind,1.10.3" \
|
||||
-i "golem-dind:latest,docker:1.11.1-dind,1.11.1" \
|
||||
-i "golem-dind:latest,docker:1.12.3-dind,1.12.3" \
|
||||
-i "golem-dind:latest,docker:1.13.0-rc5-dind,1.13.0" \
|
||||
$DIR
|
||||
|
108
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tls.bats
generated
vendored
108
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tls.bats
generated
vendored
@ -1,108 +0,0 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# Registry host name, should be set to non-localhost address and match
|
||||
# DNS name in nginx/ssl certificates and what is installed in /etc/docker/cert.d
|
||||
|
||||
load helpers
|
||||
|
||||
hostname="localregistry"
|
||||
base="hello-world"
|
||||
image="${base}:latest"
|
||||
|
||||
# Login information, should match values in nginx/test.passwd
|
||||
user=${TEST_USER:-"testuser"}
|
||||
password=${TEST_PASSWORD:-"passpassword"}
|
||||
|
||||
function setup() {
|
||||
tempImage $image
|
||||
}
|
||||
|
||||
@test "Test valid certificates" {
|
||||
docker_t tag $image $hostname:5440/$image
|
||||
run docker_t push $hostname:5440/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
}
|
||||
|
||||
@test "Test basic auth" {
|
||||
basic_auth_version_check
|
||||
login $hostname:5441
|
||||
docker_t tag $image $hostname:5441/$image
|
||||
run docker_t push $hostname:5441/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
}
|
||||
|
||||
@test "Test basic auth with build" {
|
||||
basic_auth_version_check
|
||||
login $hostname:5441
|
||||
|
||||
image1=$hostname:5441/$image-build
|
||||
image2=$hostname:5441/$image-build-2
|
||||
|
||||
tempImage $image1
|
||||
|
||||
run docker_t push $image1
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
docker_t rmi $image1
|
||||
|
||||
run build $image2 $image1
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_t push $image2
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
}
|
||||
|
||||
@test "Test TLS client auth" {
|
||||
docker_t tag $image $hostname:5442/$image
|
||||
run docker_t push $hostname:5442/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
}
|
||||
|
||||
@test "Test TLS client with invalid certificate authority fails" {
|
||||
docker_t tag $image $hostname:5443/$image
|
||||
run docker_t push $hostname:5443/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test basic auth with TLS client auth" {
|
||||
basic_auth_version_check
|
||||
login $hostname:5444
|
||||
docker_t tag $image $hostname:5444/$image
|
||||
run docker_t push $hostname:5444/$image
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
}
|
||||
|
||||
@test "Test unknown certificate authority fails" {
|
||||
docker_t tag $image $hostname:5445/$image
|
||||
run docker_t push $hostname:5445/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test basic auth with unknown certificate authority fails" {
|
||||
run login $hostname:5446
|
||||
[ "$status" -ne 0 ]
|
||||
docker_t tag $image $hostname:5446/$image
|
||||
run docker_t push $hostname:5446/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test TLS client auth to server with unknown certificate authority fails" {
|
||||
docker_t tag $image $hostname:5447/$image
|
||||
run docker_t push $hostname:5447/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test failure to connect to server fails to fallback to SSLv3" {
|
||||
docker_t tag $image $hostname:5448/$image
|
||||
run docker_t push $hostname:5448/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
129
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/token.bats
generated
vendored
129
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/token.bats
generated
vendored
@ -1,129 +0,0 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# This tests contacting a registry using a token server
|
||||
|
||||
load helpers
|
||||
|
||||
user="testuser"
|
||||
password="testpassword"
|
||||
base="hello-world"
|
||||
|
||||
@test "Test token server login" {
|
||||
login localregistry:5554
|
||||
}
|
||||
|
||||
@test "Test token server bad login" {
|
||||
docker_t_login -u "testuser" -p "badpassword" localregistry:5554
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
docker_t_login -u "baduser" -p "testpassword" localregistry:5554
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test push and pull with token auth" {
|
||||
login localregistry:5555
|
||||
image="localregistry:5555/testuser/token"
|
||||
build $image "$base:latest"
|
||||
|
||||
run docker_t push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t rmi $image
|
||||
|
||||
docker_t pull $image
|
||||
}
|
||||
|
||||
@test "Test push and pull with token auth wrong namespace" {
|
||||
login localregistry:5555
|
||||
image="localregistry:5555/notuser/token"
|
||||
build $image "$base:latest"
|
||||
|
||||
run docker_t push $image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test oauth token server login" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.11.0"
|
||||
|
||||
login_oauth localregistry:5557
|
||||
}
|
||||
|
||||
@test "Test oauth token server bad login" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.11.0"
|
||||
|
||||
docker_t_login -u "testuser" -p "badpassword" -e $email localregistry:5557
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
docker_t_login -u "baduser" -p "testpassword" -e $email localregistry:5557
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test oauth push and pull with token auth" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.11.0"
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/token"
|
||||
build $image "$base:latest"
|
||||
|
||||
run docker_t push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
docker_t rmi $image
|
||||
|
||||
docker_t pull $image
|
||||
}
|
||||
|
||||
@test "Test oauth push and build with token auth" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.11.0"
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/testuser/token-build"
|
||||
tempImage $image
|
||||
|
||||
run docker_t push $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
docker_t rmi $image
|
||||
|
||||
image2="localregistry:5558/testuser/token-build-2"
|
||||
run build $image2 $image
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run docker_t push $image2
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
has_digest "$output"
|
||||
|
||||
}
|
||||
|
||||
@test "Test oauth push and pull with token auth wrong namespace" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.11.0"
|
||||
|
||||
login_oauth localregistry:5558
|
||||
image="localregistry:5558/notuser/token"
|
||||
build $image "$base:latest"
|
||||
|
||||
run docker_t push $image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "Test oauth with v1 search" {
|
||||
version_check docker "$GOLEM_DIND_VERSION" "1.12.0"
|
||||
|
||||
run docker_t search localregistry:5600/testsearch
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
login_oauth localregistry:5600
|
||||
|
||||
run docker_t search localregistry:5600/testsearch
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
echo $output | grep "testsearch-1"
|
||||
echo $output | grep "testsearch-2"
|
||||
}
|
@ -1 +0,0 @@
|
||||
testuser:$2y$05$T2MlBvkN1R/yICNnLuf1leOlOfAY0DvybctbbWUFKlojfkShVgn4m
|
@ -1,8 +0,0 @@
|
||||
FROM dmcgowan/token-server@sha256:5a6f76d3086cdf63249c77b521108387b49d85a30c5e1c4fe82fdf5ae3b76ba7
|
||||
|
||||
WORKDIR /
|
||||
|
||||
COPY ./.htpasswd /.htpasswd
|
||||
COPY ./certs/auth.localregistry.cert /tls.cert
|
||||
COPY ./certs/auth.localregistry.key /tls.key
|
||||
COPY ./certs/signing.key /sign.key
|
@ -1,19 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDHDCCAgagAwIBAgIRAKhhQMnqZx+hkOmoUYgPb+kwCwYJKoZIhvcNAQELMCYx
|
||||
ETAPBgNVBAoTCFF1aWNrVExTMREwDwYDVQQDEwhRdWlja1RMUzAeFw0xNjAxMjgw
|
||||
MDQyMzFaFw0xOTAxMTIwMDQyMzFaMDAxETAPBgNVBAoTCFF1aWNrVExTMRswGQYD
|
||||
VQQDExJhdXRoLmxvY2FscmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQD1tUf1EghBlIRrE83yF4zDgRu7vH2Jo0kygKJUWtQQe+DfXyjjE/fg
|
||||
FdKnnoEjsIeF9hxNbTt0ldDz7/n97pbMhoiXULi9iq4jlgSzVL2XEAgrON0YSY/c
|
||||
Lmmd1KSa/pOUZr2WMAYPZ+FdQfE1W7SMNbErPefBqYdFzpZ+esAtvbajYwIjl8Vy
|
||||
9c4bidx4vgnNrR9GcFYibjC5sj8syh/OtbzzqiVGT8YcPpmMG6KNRkausa4gqpon
|
||||
NKYG8C3WDaiPCLYKcvFrFfdEWF/m2oj14eXACXT9iwp8r4bsLgXrZwqcpKOWfVRu
|
||||
qHC8aV476EYgxWCAOANExUdUaRt5wL/jAgMBAAGjPzA9MA4GA1UdDwEB/wQEAwIA
|
||||
oDAMBgNVHRMBAf8EAjAAMB0GA1UdEQQWMBSCEmF1dGgubG9jYWxyZWdpc3RyeTAL
|
||||
BgkqhkiG9w0BAQsDggEBABxPGK9FdGDxcLowNsExKnnZvmQT3H0u+Dux1gkp0AhH
|
||||
KOrmx3LUENUKLSgotzx133tgOgR5lzAWVFy7bhLwlPhOslxf2oEfztsAMd/tY8rW
|
||||
PrG2ZqYqlzEQQ9INbAc3woo5A3slN07uhP3F16jNqoMM4zRmw6Ba70CluGKT7x5+
|
||||
xVjKoWITLjWDXT5m35PnsN8CpBaFzXYcod/5p9XwCFp0s+aNxfpZECCV/3yqIr+J
|
||||
ALzroPh43FAlG96o4NyYZ2Msp63newN19R2+TgpV4nXuw2mLVDpvetP7RRqnpvj/
|
||||
qwRgt5j4hFjJWb61M0ELL7A9fA71h1ImdGCvnArdBQs=
|
||||
-----END CERTIFICATE-----
|
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA9bVH9RIIQZSEaxPN8heMw4Ebu7x9iaNJMoCiVFrUEHvg318o
|
||||
4xP34BXSp56BI7CHhfYcTW07dJXQ8+/5/e6WzIaIl1C4vYquI5YEs1S9lxAIKzjd
|
||||
GEmP3C5pndSkmv6TlGa9ljAGD2fhXUHxNVu0jDWxKz3nwamHRc6WfnrALb22o2MC
|
||||
I5fFcvXOG4nceL4Jza0fRnBWIm4wubI/LMofzrW886olRk/GHD6ZjBuijUZGrrGu
|
||||
IKqaJzSmBvAt1g2ojwi2CnLxaxX3RFhf5tqI9eHlwAl0/YsKfK+G7C4F62cKnKSj
|
||||
ln1UbqhwvGleO+hGIMVggDgDRMVHVGkbecC/4wIDAQABAoIBAQCrsjXKRwOF8CZo
|
||||
PLqZBWPT6hBbK+f9miC4LbNBhwbRTf9hl7mWlImOCTHe95/+NIk/Ty+P21jEqzwM
|
||||
ehETJPoziX9BXaL6sEHnlBlMx1aEjStoKKA3LJBeqAAdzk4IEQVHmlO4824IreqJ
|
||||
pF7Njnunzo0zTlr4tWJVoXsAfv5z9tNtdkxYBbIa0fjfGtlqXU3gLq58FCON3mB/
|
||||
NGc0AyA1UFGp0FzpdEcwTGD4InsXbcmsl2l/VPBJuZbryITRqWs6BbK++80DRhNt
|
||||
afMhP+IzKrWSCp0rBYrqqz6AevtlKdEfQK1yXPEjN/63QLMevt8mF/1JCp//TQnf
|
||||
Z6bIQbAhAoGBAP7vFA0PcvoXt9MXvvAwrKY1s6pNw4nWPG27qY1/m+DkBwP8IQms
|
||||
4AWGv1wscZzXJYTvaLO5/qjmGUj50ohcVEvyZJioh1pKXA8Chxvd6rBA/O/Lj5E0
|
||||
3MOSA5Q0gxJ0Mhv0zGbbyN5fY8D8zhxoqQP4LoW+UdZG2Oi6JxsQ9c9dAoGBAPa8
|
||||
U3bGuM5OGA9EWP7mkB/VnjDTL1aEIN3cOHbHIKwH/loxdYcNMBE7vwxV1CzgIzXT
|
||||
wsL0iE15fQdK938u0+um8aH5QtbWNI8tdk1XVjEC/i3C7N6WVUutneCKUDb4QxiB
|
||||
9OvWCbNNiN+xTKBBM93YlwO3GYfrW9Pmm9q1+hg/AoGBALJlUS22gun50PxaIJZq
|
||||
KVcCO2DQnCYHki/j48mN4+HjD/m85M2lePrFCYIR48syTyIQer9SR5+frVAA6k/b
|
||||
9G1VCQo+3MDVSkiCp1Nb3tBKGfYgB65ARMBinDiI6rPuNeaUTrkn0g+yxtaU0hLV
|
||||
Nnj9omia/x+oYj+xjI4HN0xNAoGARy92dSJIV104m88ATip/EnAzP6ruUWu1f8z1
|
||||
jW9OAdQckjEK03f+kjpGmGx61qekAPejjVO3r4KJi/0ZAtyjz61OsYiUvB748wYO
|
||||
x6mW+HUAmHtQk7eTzE2+6vV8xx9BXGTCIPiTu+N2xfMFRIcLS8odZ7j/6LMCv1Qd
|
||||
SzCNg0kCgYBaNlEs4pK1VxZZpEWwVmFpgIxfEfxLIaGrek6wBTcCn/VA2M0oHuez
|
||||
mlMio8VY0yWPBJz30JflDiTmYIvteLPMHT0N0J6isiXLhzJSFI4+cAMLE2Q5v8rz
|
||||
W+W5/L8YZeierW0qJat1BrgStaf5ZLpiOc9pKBSwycydPH5BfVdK/A==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -1,19 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfugAwIBAgIQN7rT95eAy75c4n6/AsDJODALBgkqhkiG9w0BAQswJjER
|
||||
MA8GA1UEChMIUXVpY2tUTFMxETAPBgNVBAMTCFF1aWNrVExTMB4XDTE2MDEyODAw
|
||||
NDIzMloXDTE5MDExMjAwNDIzMlowKzERMA8GA1UEChMIUXVpY2tUTFMxFjAUBgNV
|
||||
BAMTDWxvY2FscmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDLi75QEkl/qekcoOJlNv9y1IXvrbU2ssl4ViJiZRjWx+/CkyCCOyf9YUpAgRLr
|
||||
Pskqde2mwhuNP8yBlOBb17Sapz7N3+hJi5j9vLBAFcamPeF3PqxjFv7j5TKkRmSI
|
||||
dFYQclREwMUd3qEH322KkqOnsEEfdmCgFqWORe+QR5AxzxQP3Pnd4OYH1yZCh0MQ
|
||||
P2pJgrxxf2I5I/m1AUgoHV1cdBbCv9LGohJPpMtwPC0dJpgMFcnf6hT37At236AY
|
||||
V437HiRruY7iPWkYFrSPWpwdslJ32MZvRN5RS163jZXjiZ7qWnQOiiDJfXe4evB/
|
||||
yQLN4m0qVQxsMz7rkY7OsqaXAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIAoDAMBgNV
|
||||
HRMBAf8EAjAAMBgGA1UdEQQRMA+CDWxvY2FscmVnaXN0cnkwCwYJKoZIhvcNAQEL
|
||||
A4IBAQAyUb3EuMaOylBeV8+4KeBiE4lxykDOwLLSk3jXRsVVtfJpX3v8l5vwo/Jf
|
||||
iG8tzzz+7uiskI96u3TsekUtVkUxujfKevMP+369K/59s7NRmwwlFMyB2fvL14B2
|
||||
oweVjWvM/8fZl6irtFdbJFXXRm7paKso5cmfImxhojAwohgcd4XTVLE/7juYa582
|
||||
AaBdRuIiyL71MU9qa1mC5+57AaSLPYaPKpahemgYYkV1Z403Kd6rXchxdQ8JIAL8
|
||||
+0oYTSC+svnz1tUU/V5E5id9LQaTmDN5iIVFhNpqAaZmR45UI86woWvnkMb8Ants
|
||||
4aknwTwY3300PuTqBdQufvOFDRN5
|
||||
-----END CERTIFICATE-----
|
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAy4u+UBJJf6npHKDiZTb/ctSF7621NrLJeFYiYmUY1sfvwpMg
|
||||
gjsn/WFKQIES6z7JKnXtpsIbjT/MgZTgW9e0mqc+zd/oSYuY/bywQBXGpj3hdz6s
|
||||
Yxb+4+UypEZkiHRWEHJURMDFHd6hB99tipKjp7BBH3ZgoBaljkXvkEeQMc8UD9z5
|
||||
3eDmB9cmQodDED9qSYK8cX9iOSP5tQFIKB1dXHQWwr/SxqIST6TLcDwtHSaYDBXJ
|
||||
3+oU9+wLdt+gGFeN+x4ka7mO4j1pGBa0j1qcHbJSd9jGb0TeUUtet42V44me6lp0
|
||||
DoogyX13uHrwf8kCzeJtKlUMbDM+65GOzrKmlwIDAQABAoIBAF6vFMp+lz4RteSh
|
||||
Wm8m1FGAVwWVUpStOlcGClynFpTi0L88XYT3K7UMStQSttBDlqRv0ysdZF+ia+lj
|
||||
bbKLdvHyFp8CJzX/AB4YZgyJlKzEYFtuBhbaHZu5hIMyU5W+OELSTCznV0p7w4C8
|
||||
CGLLr+FTdhfCo1QU9NJn6fa9s2/XRdSClBBalAHYs0ZS7ZckaF/sPiC/VapfBMet
|
||||
qjJXNYiO6pXYriGWKF9zdAMfk2CM0BVWbnwQZkMSEQirrTcJwm3ezyloXCv2nywK
|
||||
/VzbUT1HJVyzo5oAwTd0MwDc2oEMiFzlfO028zY4LDltpia+SyWvFi5NaIqzFESc
|
||||
yLgJacECgYEA3jvH+ZQHQf42Md8TCciokaYvwWIKJdk4WRjbvE5cBZekyXAm7/3b
|
||||
/1VFDKsy2RPlfmfHP3wy9rlnjzsRveB5qaclgS8aI67AYsWd/yRgfRatl7Ve9bHl
|
||||
LY6VM5L/DZTxykcqivwjc77XoDuBfUKs6tyuSLQku+FOTbLtNYlUCHECgYEA6nkR
|
||||
lkXufyLmDhNb3093RsYvPcs1kGaIIGTnz3cxWNh485DgsyLBuYQ5ugupQkzM8YSt
|
||||
ohDTmVpggqjlXQxCg0Zw8gkEV0v8KsLGjn1CuTJg/mBArXlelq1FEeRAYC9/YfOz
|
||||
ocXegHV7wDKKtcraNZFsEc7Z0LwbC9wtzSFG44cCgYASkMX1CLPOhJE8e1lY0OWc
|
||||
PVjx++HDJbF6aAQ7aARyBygiF/d4xylw3EvHcinuTqY2eC8CE7siN3z6T0H9Ldqc
|
||||
HLWaZDf30SqLVd0MKprQ+GsKKIHFXtY5hxbZ1ybtmIrWjjl0oPnJOqFC5pW7xC0z
|
||||
9bmtozcKZxkmjpMYjN9zUQKBgQCqV6KLRerqunPgLfhE1/qTlE+l2QflDFhBEI3I
|
||||
j5NuNHZKnSphehK7sHAv1WD2Jc2OeRGb+BWCB8Ktqf5YBxwbOwW7EQnyUeW1OyP9
|
||||
SMs8uHj21P6oCNDLLr5LLUQHnPoyM1aBZLstICzziMR1JhY5bJjSpzBfEQmlKCSu
|
||||
LkrN6QKBgQCRXrBJRUxeJj7wCnCSq0Clf9NhCpQnwo4bEx8sKlj8K8ku8MvwQwoM
|
||||
3KfWc7bOl6A2/mM/k4yoHtBMM9X9xqYtsgeFhxuiWBcfTmTxWh73LQ48Kgbrgodt
|
||||
6yTccnjr7OtBidD85c6lgjAUgcL43QY8mlw0OhzXAZ2R5HWFp4ht+w==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -1,18 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC9TCCAd+gAwIBAgIRAJ6IIisIZxL86oe3oeoAgWUwCwYJKoZIhvcNAQELMCYx
|
||||
ETAPBgNVBAoTCFF1aWNrVExTMREwDwYDVQQDEwhRdWlja1RMUzAeFw0xNjAxMjgw
|
||||
MDQyMzNaFw0xOTAxMTIwMDQyMzNaMBMxETAPBgNVBAoTCFF1aWNrVExTMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3IXUwqSdO2QTj2ET6fJPGe+KWVnt
|
||||
QCQQWjkWVpOz8L2A29BRvv9z6lYNf9sOM0Xb5IUAgoZ/s3U6LNYT/RWYFBfeo40r
|
||||
Xd/MNKAn0kFsSb6BIKmUwPqFeqc8wiPX6yY4SbF1sUTkCTkw3yFHg/AIlwmhpFH3
|
||||
9mAmV+x0kTzFR/78ZDD5CUNS59bbu+7UqB06YrJuVEwPY98YixSPXTcaKimsUe+K
|
||||
IY8FQ6yN6l27MK56wlj4hw2gYz+cyBUBCExCgYMQlOSg2ilH4qYyFvccSDUH7jTA
|
||||
NwpsIBfdoUVbI+j2ivn+ZGD614LtIStGgUu0mDDVxVOWnRvq/z7LMaa2jwIDAQAB
|
||||
ozUwMzAOBgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0T
|
||||
AQH/BAIwADALBgkqhkiG9w0BAQsDggEBAJq3JzTLrIWCF8rHLTTm1icE9PjOO0sV
|
||||
a1wrmdJ6NwRbJ66dLZ/4G/NZjVOnce9WFHYLFSEG+wx5YVUPuJXpJaSdy0h8F0Uw
|
||||
hiJwgeVsGg7vcf4G6mWHrsauDOhylnD31UtYPX1Ao/jcntyyf+gCQpY1J/B8l1yU
|
||||
LNOwvWLVLpZwZ4ehbKA/UnDXgA+3uHvpzl//cPe0cnt+Mhrgzk5mIMwVR6zCZw1G
|
||||
oVutAHpv2PXxRwTMu51J+QtSL2b2w3mGHxDLpmz8UdXOtkxdpmDT8kIOtX0T5yGL
|
||||
29F3fa81iZPs02GWjSGOfOzmCCvaA4C5KJvY/WulF7OOgwvrBpQwqTI=
|
||||
-----END CERTIFICATE-----
|
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA3IXUwqSdO2QTj2ET6fJPGe+KWVntQCQQWjkWVpOz8L2A29BR
|
||||
vv9z6lYNf9sOM0Xb5IUAgoZ/s3U6LNYT/RWYFBfeo40rXd/MNKAn0kFsSb6BIKmU
|
||||
wPqFeqc8wiPX6yY4SbF1sUTkCTkw3yFHg/AIlwmhpFH39mAmV+x0kTzFR/78ZDD5
|
||||
CUNS59bbu+7UqB06YrJuVEwPY98YixSPXTcaKimsUe+KIY8FQ6yN6l27MK56wlj4
|
||||
hw2gYz+cyBUBCExCgYMQlOSg2ilH4qYyFvccSDUH7jTANwpsIBfdoUVbI+j2ivn+
|
||||
ZGD614LtIStGgUu0mDDVxVOWnRvq/z7LMaa2jwIDAQABAoIBAD2tiNZv6DImSXo+
|
||||
sq0qQomEf/OBvWPFMnWppd/NK/TXa+UPHO4I0MjoDJqIEC6zCU+fC4d2St1MmlrT
|
||||
/X85vPFRw8mGwGxfHeRSLxEVj04I5GDYTWy0JQUrJUk/cTKp2/Bwm/RaylTyFAM0
|
||||
caYrSpvD69vjuTDFr7PDxM6iaqM53zK/vD8kCe81z+wN0UbAKsLlUOKztjH6SzL9
|
||||
uVOkekIT/j3L2xxyQhjmhfA3TuCP4uNK/+6/4ovl9Nj4pQsFomsCk4phgqy9SOm1
|
||||
4yufmVd8k7J3cppMlMPNc+7tqe2Xn593Y8QT95y3yhtkFECF70yBw64HMDDpA22p
|
||||
5b/JV9ECgYEA9H4RBXOwbdjcpCa9H3mFjHqUQCqNme1vOSGiflZh9KBCDKgdqugm
|
||||
KHpvAECADie0p6XRHpxRvufKnGFkJwedfeiKz51T+0dqgPxWncYT1TC+cAjOSzfM
|
||||
wBpUOcAyvTTviwGbg4bLanHo4remzCbcnRvHQX4YfPFCjT9GhsU+XEUCgYEA5ubz
|
||||
IlSu1wwFJpoO24ZykGUyqGUQXzR0NrXiLrpF0764qjmHyF8SPJPv1XegSxP/nUTz
|
||||
SjVfJ7wye/X9qlOpBY8mzy9qQMMKc1cQBV1yVW8IRZ7pMYQZO7qmrZD/DWTa5qWt
|
||||
pqSbIH2FKedELsKJA/SBtczKjspOdDKyh0UelsMCgYA7DyTfc0XAEy2hPXZb3wgC
|
||||
mi2rnlvcPf2rCFPvPsCkzf2GfynDehaVmpWrsuj8Al1iTezI/yvD+Mv5oJEH2JAT
|
||||
tROq+S8rOOIiTFJEBHAQBJlMCOSESPNdyD5mQOZAzEO9CWNejzYd/WwrL//Luut5
|
||||
zBcC3AngTIsuAYXw0j6xHQKBgQDamkAJep7k3W5q82OplgoUhpqFLtlnKSP1QBFZ
|
||||
J+U/6Mqv7jONEeUUEQL42H6bVd2kqUikMw9ZcSVikquLfBUDPFoDwOIZWg4k0IJM
|
||||
cgHyvGHad+5SgLva/oUawbGWnqtXvfc/U4vCINPXrimxE1/grLW4xp/mu8W24OCA
|
||||
jIG/PQKBgD/Apl+sfqiB/6ONBjjIswA4yFkEXHSZNpAgcPwhA+cO5D0afEWz2HIx
|
||||
VeOh5NjN1EL0hX8clFW4bfkK1Vr0kjvbMUXnBWaibUgpiVQl9O9WjaKQLZrp4sRu
|
||||
x2kJ07Qn6ri7f/lsqOELZwBy95iHWRdePptaAKkRGxJstHI7dgUt
|
||||
-----END RSA PRIVATE KEY-----
|
@ -1,15 +0,0 @@
|
||||
version: 0.1
|
||||
loglevel: debug
|
||||
storage:
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /tmp/registry-dev
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
auth:
|
||||
token:
|
||||
realm: "https://auth.localregistry:5559/token/"
|
||||
issuer: "registry-test"
|
||||
service: "registry-test"
|
||||
rootcertbundle: "/etc/docker/registry/tokenbundle.pem"
|
@ -1,18 +0,0 @@
|
||||
version: 0.1
|
||||
loglevel: debug
|
||||
storage:
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /tmp/registry-dev
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
tls:
|
||||
certificate: "/etc/docker/registry/localregistry.cert"
|
||||
key: "/etc/docker/registry/localregistry.key"
|
||||
auth:
|
||||
token:
|
||||
realm: "https://auth.localregistry:5559/token/"
|
||||
issuer: "registry-test"
|
||||
service: "registry-test"
|
||||
rootcertbundle: "/etc/docker/registry/tokenbundle.pem"
|
@ -1 +0,0 @@
|
||||
testuser:$2y$05$T2MlBvkN1R/yICNnLuf1leOlOfAY0DvybctbbWUFKlojfkShVgn4m
|
@ -1,8 +0,0 @@
|
||||
FROM dmcgowan/token-server@sha256:0eab50ebdff5b6b95b3addf4edbd8bd2f5b940f27b41b43c94afdf05863a81af
|
||||
|
||||
WORKDIR /
|
||||
|
||||
COPY ./.htpasswd /.htpasswd
|
||||
COPY ./certs/auth.localregistry.cert /tls.cert
|
||||
COPY ./certs/auth.localregistry.key /tls.key
|
||||
COPY ./certs/signing.key /sign.key
|
@ -1,19 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDHDCCAgagAwIBAgIRAKhhQMnqZx+hkOmoUYgPb+kwCwYJKoZIhvcNAQELMCYx
|
||||
ETAPBgNVBAoTCFF1aWNrVExTMREwDwYDVQQDEwhRdWlja1RMUzAeFw0xNjAxMjgw
|
||||
MDQyMzFaFw0xOTAxMTIwMDQyMzFaMDAxETAPBgNVBAoTCFF1aWNrVExTMRswGQYD
|
||||
VQQDExJhdXRoLmxvY2FscmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQD1tUf1EghBlIRrE83yF4zDgRu7vH2Jo0kygKJUWtQQe+DfXyjjE/fg
|
||||
FdKnnoEjsIeF9hxNbTt0ldDz7/n97pbMhoiXULi9iq4jlgSzVL2XEAgrON0YSY/c
|
||||
Lmmd1KSa/pOUZr2WMAYPZ+FdQfE1W7SMNbErPefBqYdFzpZ+esAtvbajYwIjl8Vy
|
||||
9c4bidx4vgnNrR9GcFYibjC5sj8syh/OtbzzqiVGT8YcPpmMG6KNRkausa4gqpon
|
||||
NKYG8C3WDaiPCLYKcvFrFfdEWF/m2oj14eXACXT9iwp8r4bsLgXrZwqcpKOWfVRu
|
||||
qHC8aV476EYgxWCAOANExUdUaRt5wL/jAgMBAAGjPzA9MA4GA1UdDwEB/wQEAwIA
|
||||
oDAMBgNVHRMBAf8EAjAAMB0GA1UdEQQWMBSCEmF1dGgubG9jYWxyZWdpc3RyeTAL
|
||||
BgkqhkiG9w0BAQsDggEBABxPGK9FdGDxcLowNsExKnnZvmQT3H0u+Dux1gkp0AhH
|
||||
KOrmx3LUENUKLSgotzx133tgOgR5lzAWVFy7bhLwlPhOslxf2oEfztsAMd/tY8rW
|
||||
PrG2ZqYqlzEQQ9INbAc3woo5A3slN07uhP3F16jNqoMM4zRmw6Ba70CluGKT7x5+
|
||||
xVjKoWITLjWDXT5m35PnsN8CpBaFzXYcod/5p9XwCFp0s+aNxfpZECCV/3yqIr+J
|
||||
ALzroPh43FAlG96o4NyYZ2Msp63newN19R2+TgpV4nXuw2mLVDpvetP7RRqnpvj/
|
||||
qwRgt5j4hFjJWb61M0ELL7A9fA71h1ImdGCvnArdBQs=
|
||||
-----END CERTIFICATE-----
|
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA9bVH9RIIQZSEaxPN8heMw4Ebu7x9iaNJMoCiVFrUEHvg318o
|
||||
4xP34BXSp56BI7CHhfYcTW07dJXQ8+/5/e6WzIaIl1C4vYquI5YEs1S9lxAIKzjd
|
||||
GEmP3C5pndSkmv6TlGa9ljAGD2fhXUHxNVu0jDWxKz3nwamHRc6WfnrALb22o2MC
|
||||
I5fFcvXOG4nceL4Jza0fRnBWIm4wubI/LMofzrW886olRk/GHD6ZjBuijUZGrrGu
|
||||
IKqaJzSmBvAt1g2ojwi2CnLxaxX3RFhf5tqI9eHlwAl0/YsKfK+G7C4F62cKnKSj
|
||||
ln1UbqhwvGleO+hGIMVggDgDRMVHVGkbecC/4wIDAQABAoIBAQCrsjXKRwOF8CZo
|
||||
PLqZBWPT6hBbK+f9miC4LbNBhwbRTf9hl7mWlImOCTHe95/+NIk/Ty+P21jEqzwM
|
||||
ehETJPoziX9BXaL6sEHnlBlMx1aEjStoKKA3LJBeqAAdzk4IEQVHmlO4824IreqJ
|
||||
pF7Njnunzo0zTlr4tWJVoXsAfv5z9tNtdkxYBbIa0fjfGtlqXU3gLq58FCON3mB/
|
||||
NGc0AyA1UFGp0FzpdEcwTGD4InsXbcmsl2l/VPBJuZbryITRqWs6BbK++80DRhNt
|
||||
afMhP+IzKrWSCp0rBYrqqz6AevtlKdEfQK1yXPEjN/63QLMevt8mF/1JCp//TQnf
|
||||
Z6bIQbAhAoGBAP7vFA0PcvoXt9MXvvAwrKY1s6pNw4nWPG27qY1/m+DkBwP8IQms
|
||||
4AWGv1wscZzXJYTvaLO5/qjmGUj50ohcVEvyZJioh1pKXA8Chxvd6rBA/O/Lj5E0
|
||||
3MOSA5Q0gxJ0Mhv0zGbbyN5fY8D8zhxoqQP4LoW+UdZG2Oi6JxsQ9c9dAoGBAPa8
|
||||
U3bGuM5OGA9EWP7mkB/VnjDTL1aEIN3cOHbHIKwH/loxdYcNMBE7vwxV1CzgIzXT
|
||||
wsL0iE15fQdK938u0+um8aH5QtbWNI8tdk1XVjEC/i3C7N6WVUutneCKUDb4QxiB
|
||||
9OvWCbNNiN+xTKBBM93YlwO3GYfrW9Pmm9q1+hg/AoGBALJlUS22gun50PxaIJZq
|
||||
KVcCO2DQnCYHki/j48mN4+HjD/m85M2lePrFCYIR48syTyIQer9SR5+frVAA6k/b
|
||||
9G1VCQo+3MDVSkiCp1Nb3tBKGfYgB65ARMBinDiI6rPuNeaUTrkn0g+yxtaU0hLV
|
||||
Nnj9omia/x+oYj+xjI4HN0xNAoGARy92dSJIV104m88ATip/EnAzP6ruUWu1f8z1
|
||||
jW9OAdQckjEK03f+kjpGmGx61qekAPejjVO3r4KJi/0ZAtyjz61OsYiUvB748wYO
|
||||
x6mW+HUAmHtQk7eTzE2+6vV8xx9BXGTCIPiTu+N2xfMFRIcLS8odZ7j/6LMCv1Qd
|
||||
SzCNg0kCgYBaNlEs4pK1VxZZpEWwVmFpgIxfEfxLIaGrek6wBTcCn/VA2M0oHuez
|
||||
mlMio8VY0yWPBJz30JflDiTmYIvteLPMHT0N0J6isiXLhzJSFI4+cAMLE2Q5v8rz
|
||||
W+W5/L8YZeierW0qJat1BrgStaf5ZLpiOc9pKBSwycydPH5BfVdK/A==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -1,19 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDETCCAfugAwIBAgIQN7rT95eAy75c4n6/AsDJODALBgkqhkiG9w0BAQswJjER
|
||||
MA8GA1UEChMIUXVpY2tUTFMxETAPBgNVBAMTCFF1aWNrVExTMB4XDTE2MDEyODAw
|
||||
NDIzMloXDTE5MDExMjAwNDIzMlowKzERMA8GA1UEChMIUXVpY2tUTFMxFjAUBgNV
|
||||
BAMTDWxvY2FscmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDLi75QEkl/qekcoOJlNv9y1IXvrbU2ssl4ViJiZRjWx+/CkyCCOyf9YUpAgRLr
|
||||
Pskqde2mwhuNP8yBlOBb17Sapz7N3+hJi5j9vLBAFcamPeF3PqxjFv7j5TKkRmSI
|
||||
dFYQclREwMUd3qEH322KkqOnsEEfdmCgFqWORe+QR5AxzxQP3Pnd4OYH1yZCh0MQ
|
||||
P2pJgrxxf2I5I/m1AUgoHV1cdBbCv9LGohJPpMtwPC0dJpgMFcnf6hT37At236AY
|
||||
V437HiRruY7iPWkYFrSPWpwdslJ32MZvRN5RS163jZXjiZ7qWnQOiiDJfXe4evB/
|
||||
yQLN4m0qVQxsMz7rkY7OsqaXAgMBAAGjOjA4MA4GA1UdDwEB/wQEAwIAoDAMBgNV
|
||||
HRMBAf8EAjAAMBgGA1UdEQQRMA+CDWxvY2FscmVnaXN0cnkwCwYJKoZIhvcNAQEL
|
||||
A4IBAQAyUb3EuMaOylBeV8+4KeBiE4lxykDOwLLSk3jXRsVVtfJpX3v8l5vwo/Jf
|
||||
iG8tzzz+7uiskI96u3TsekUtVkUxujfKevMP+369K/59s7NRmwwlFMyB2fvL14B2
|
||||
oweVjWvM/8fZl6irtFdbJFXXRm7paKso5cmfImxhojAwohgcd4XTVLE/7juYa582
|
||||
AaBdRuIiyL71MU9qa1mC5+57AaSLPYaPKpahemgYYkV1Z403Kd6rXchxdQ8JIAL8
|
||||
+0oYTSC+svnz1tUU/V5E5id9LQaTmDN5iIVFhNpqAaZmR45UI86woWvnkMb8Ants
|
||||
4aknwTwY3300PuTqBdQufvOFDRN5
|
||||
-----END CERTIFICATE-----
|
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAy4u+UBJJf6npHKDiZTb/ctSF7621NrLJeFYiYmUY1sfvwpMg
|
||||
gjsn/WFKQIES6z7JKnXtpsIbjT/MgZTgW9e0mqc+zd/oSYuY/bywQBXGpj3hdz6s
|
||||
Yxb+4+UypEZkiHRWEHJURMDFHd6hB99tipKjp7BBH3ZgoBaljkXvkEeQMc8UD9z5
|
||||
3eDmB9cmQodDED9qSYK8cX9iOSP5tQFIKB1dXHQWwr/SxqIST6TLcDwtHSaYDBXJ
|
||||
3+oU9+wLdt+gGFeN+x4ka7mO4j1pGBa0j1qcHbJSd9jGb0TeUUtet42V44me6lp0
|
||||
DoogyX13uHrwf8kCzeJtKlUMbDM+65GOzrKmlwIDAQABAoIBAF6vFMp+lz4RteSh
|
||||
Wm8m1FGAVwWVUpStOlcGClynFpTi0L88XYT3K7UMStQSttBDlqRv0ysdZF+ia+lj
|
||||
bbKLdvHyFp8CJzX/AB4YZgyJlKzEYFtuBhbaHZu5hIMyU5W+OELSTCznV0p7w4C8
|
||||
CGLLr+FTdhfCo1QU9NJn6fa9s2/XRdSClBBalAHYs0ZS7ZckaF/sPiC/VapfBMet
|
||||
qjJXNYiO6pXYriGWKF9zdAMfk2CM0BVWbnwQZkMSEQirrTcJwm3ezyloXCv2nywK
|
||||
/VzbUT1HJVyzo5oAwTd0MwDc2oEMiFzlfO028zY4LDltpia+SyWvFi5NaIqzFESc
|
||||
yLgJacECgYEA3jvH+ZQHQf42Md8TCciokaYvwWIKJdk4WRjbvE5cBZekyXAm7/3b
|
||||
/1VFDKsy2RPlfmfHP3wy9rlnjzsRveB5qaclgS8aI67AYsWd/yRgfRatl7Ve9bHl
|
||||
LY6VM5L/DZTxykcqivwjc77XoDuBfUKs6tyuSLQku+FOTbLtNYlUCHECgYEA6nkR
|
||||
lkXufyLmDhNb3093RsYvPcs1kGaIIGTnz3cxWNh485DgsyLBuYQ5ugupQkzM8YSt
|
||||
ohDTmVpggqjlXQxCg0Zw8gkEV0v8KsLGjn1CuTJg/mBArXlelq1FEeRAYC9/YfOz
|
||||
ocXegHV7wDKKtcraNZFsEc7Z0LwbC9wtzSFG44cCgYASkMX1CLPOhJE8e1lY0OWc
|
||||
PVjx++HDJbF6aAQ7aARyBygiF/d4xylw3EvHcinuTqY2eC8CE7siN3z6T0H9Ldqc
|
||||
HLWaZDf30SqLVd0MKprQ+GsKKIHFXtY5hxbZ1ybtmIrWjjl0oPnJOqFC5pW7xC0z
|
||||
9bmtozcKZxkmjpMYjN9zUQKBgQCqV6KLRerqunPgLfhE1/qTlE+l2QflDFhBEI3I
|
||||
j5NuNHZKnSphehK7sHAv1WD2Jc2OeRGb+BWCB8Ktqf5YBxwbOwW7EQnyUeW1OyP9
|
||||
SMs8uHj21P6oCNDLLr5LLUQHnPoyM1aBZLstICzziMR1JhY5bJjSpzBfEQmlKCSu
|
||||
LkrN6QKBgQCRXrBJRUxeJj7wCnCSq0Clf9NhCpQnwo4bEx8sKlj8K8ku8MvwQwoM
|
||||
3KfWc7bOl6A2/mM/k4yoHtBMM9X9xqYtsgeFhxuiWBcfTmTxWh73LQ48Kgbrgodt
|
||||
6yTccnjr7OtBidD85c6lgjAUgcL43QY8mlw0OhzXAZ2R5HWFp4ht+w==
|
||||
-----END RSA PRIVATE KEY-----
|
@ -1,18 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC9TCCAd+gAwIBAgIRAJ6IIisIZxL86oe3oeoAgWUwCwYJKoZIhvcNAQELMCYx
|
||||
ETAPBgNVBAoTCFF1aWNrVExTMREwDwYDVQQDEwhRdWlja1RMUzAeFw0xNjAxMjgw
|
||||
MDQyMzNaFw0xOTAxMTIwMDQyMzNaMBMxETAPBgNVBAoTCFF1aWNrVExTMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3IXUwqSdO2QTj2ET6fJPGe+KWVnt
|
||||
QCQQWjkWVpOz8L2A29BRvv9z6lYNf9sOM0Xb5IUAgoZ/s3U6LNYT/RWYFBfeo40r
|
||||
Xd/MNKAn0kFsSb6BIKmUwPqFeqc8wiPX6yY4SbF1sUTkCTkw3yFHg/AIlwmhpFH3
|
||||
9mAmV+x0kTzFR/78ZDD5CUNS59bbu+7UqB06YrJuVEwPY98YixSPXTcaKimsUe+K
|
||||
IY8FQ6yN6l27MK56wlj4hw2gYz+cyBUBCExCgYMQlOSg2ilH4qYyFvccSDUH7jTA
|
||||
NwpsIBfdoUVbI+j2ivn+ZGD614LtIStGgUu0mDDVxVOWnRvq/z7LMaa2jwIDAQAB
|
||||
ozUwMzAOBgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0T
|
||||
AQH/BAIwADALBgkqhkiG9w0BAQsDggEBAJq3JzTLrIWCF8rHLTTm1icE9PjOO0sV
|
||||
a1wrmdJ6NwRbJ66dLZ/4G/NZjVOnce9WFHYLFSEG+wx5YVUPuJXpJaSdy0h8F0Uw
|
||||
hiJwgeVsGg7vcf4G6mWHrsauDOhylnD31UtYPX1Ao/jcntyyf+gCQpY1J/B8l1yU
|
||||
LNOwvWLVLpZwZ4ehbKA/UnDXgA+3uHvpzl//cPe0cnt+Mhrgzk5mIMwVR6zCZw1G
|
||||
oVutAHpv2PXxRwTMu51J+QtSL2b2w3mGHxDLpmz8UdXOtkxdpmDT8kIOtX0T5yGL
|
||||
29F3fa81iZPs02GWjSGOfOzmCCvaA4C5KJvY/WulF7OOgwvrBpQwqTI=
|
||||
-----END CERTIFICATE-----
|
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA3IXUwqSdO2QTj2ET6fJPGe+KWVntQCQQWjkWVpOz8L2A29BR
|
||||
vv9z6lYNf9sOM0Xb5IUAgoZ/s3U6LNYT/RWYFBfeo40rXd/MNKAn0kFsSb6BIKmU
|
||||
wPqFeqc8wiPX6yY4SbF1sUTkCTkw3yFHg/AIlwmhpFH39mAmV+x0kTzFR/78ZDD5
|
||||
CUNS59bbu+7UqB06YrJuVEwPY98YixSPXTcaKimsUe+KIY8FQ6yN6l27MK56wlj4
|
||||
hw2gYz+cyBUBCExCgYMQlOSg2ilH4qYyFvccSDUH7jTANwpsIBfdoUVbI+j2ivn+
|
||||
ZGD614LtIStGgUu0mDDVxVOWnRvq/z7LMaa2jwIDAQABAoIBAD2tiNZv6DImSXo+
|
||||
sq0qQomEf/OBvWPFMnWppd/NK/TXa+UPHO4I0MjoDJqIEC6zCU+fC4d2St1MmlrT
|
||||
/X85vPFRw8mGwGxfHeRSLxEVj04I5GDYTWy0JQUrJUk/cTKp2/Bwm/RaylTyFAM0
|
||||
caYrSpvD69vjuTDFr7PDxM6iaqM53zK/vD8kCe81z+wN0UbAKsLlUOKztjH6SzL9
|
||||
uVOkekIT/j3L2xxyQhjmhfA3TuCP4uNK/+6/4ovl9Nj4pQsFomsCk4phgqy9SOm1
|
||||
4yufmVd8k7J3cppMlMPNc+7tqe2Xn593Y8QT95y3yhtkFECF70yBw64HMDDpA22p
|
||||
5b/JV9ECgYEA9H4RBXOwbdjcpCa9H3mFjHqUQCqNme1vOSGiflZh9KBCDKgdqugm
|
||||
KHpvAECADie0p6XRHpxRvufKnGFkJwedfeiKz51T+0dqgPxWncYT1TC+cAjOSzfM
|
||||
wBpUOcAyvTTviwGbg4bLanHo4remzCbcnRvHQX4YfPFCjT9GhsU+XEUCgYEA5ubz
|
||||
IlSu1wwFJpoO24ZykGUyqGUQXzR0NrXiLrpF0764qjmHyF8SPJPv1XegSxP/nUTz
|
||||
SjVfJ7wye/X9qlOpBY8mzy9qQMMKc1cQBV1yVW8IRZ7pMYQZO7qmrZD/DWTa5qWt
|
||||
pqSbIH2FKedELsKJA/SBtczKjspOdDKyh0UelsMCgYA7DyTfc0XAEy2hPXZb3wgC
|
||||
mi2rnlvcPf2rCFPvPsCkzf2GfynDehaVmpWrsuj8Al1iTezI/yvD+Mv5oJEH2JAT
|
||||
tROq+S8rOOIiTFJEBHAQBJlMCOSESPNdyD5mQOZAzEO9CWNejzYd/WwrL//Luut5
|
||||
zBcC3AngTIsuAYXw0j6xHQKBgQDamkAJep7k3W5q82OplgoUhpqFLtlnKSP1QBFZ
|
||||
J+U/6Mqv7jONEeUUEQL42H6bVd2kqUikMw9ZcSVikquLfBUDPFoDwOIZWg4k0IJM
|
||||
cgHyvGHad+5SgLva/oUawbGWnqtXvfc/U4vCINPXrimxE1/grLW4xp/mu8W24OCA
|
||||
jIG/PQKBgD/Apl+sfqiB/6ONBjjIswA4yFkEXHSZNpAgcPwhA+cO5D0afEWz2HIx
|
||||
VeOh5NjN1EL0hX8clFW4bfkK1Vr0kjvbMUXnBWaibUgpiVQl9O9WjaKQLZrp4sRu
|
||||
x2kJ07Qn6ri7f/lsqOELZwBy95iHWRdePptaAKkRGxJstHI7dgUt
|
||||
-----END RSA PRIVATE KEY-----
|
@ -1,18 +0,0 @@
|
||||
version: 0.1
|
||||
loglevel: debug
|
||||
storage:
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /tmp/registry-dev
|
||||
http:
|
||||
addr: 0.0.0.0:5000
|
||||
tls:
|
||||
certificate: "/etc/docker/registry/localregistry.cert"
|
||||
key: "/etc/docker/registry/localregistry.key"
|
||||
auth:
|
||||
token:
|
||||
realm: "https://auth.localregistry:5556/token/"
|
||||
issuer: "registry-test"
|
||||
service: "registry-test"
|
||||
rootcertbundle: "/etc/docker/registry/tokenbundle.pem"
|
38
gateway/vendor/github.com/docker/distribution/contrib/token-server/errors.go
generated
vendored
38
gateway/vendor/github.com/docker/distribution/contrib/token-server/errors.go
generated
vendored
@ -1,38 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
var (
|
||||
errGroup = "tokenserver"
|
||||
|
||||
// ErrorBadTokenOption is returned when a token parameter is invalid
|
||||
ErrorBadTokenOption = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "BAD_TOKEN_OPTION",
|
||||
Message: "bad token option",
|
||||
Description: `This error may be returned when a request for a
|
||||
token contains an option which is not valid`,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
|
||||
// ErrorMissingRequiredField is returned when a required form field is missing
|
||||
ErrorMissingRequiredField = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "MISSING_REQUIRED_FIELD",
|
||||
Message: "missing required field",
|
||||
Description: `This error may be returned when a request for a
|
||||
token does not contain a required form field`,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
|
||||
// ErrorUnsupportedValue is returned when a form field has an unsupported value
|
||||
ErrorUnsupportedValue = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||
Value: "UNSUPPORTED_VALUE",
|
||||
Message: "unsupported value",
|
||||
Description: `This error may be returned when a request for a
|
||||
token contains a form field with an unsupported value`,
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
})
|
||||
)
|
425
gateway/vendor/github.com/docker/distribution/contrib/token-server/main.go
generated
vendored
425
gateway/vendor/github.com/docker/distribution/contrib/token-server/main.go
generated
vendored
@ -1,425 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/auth"
|
||||
_ "github.com/docker/distribution/registry/auth/htpasswd"
|
||||
"github.com/docker/libtrust"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var (
|
||||
enforceRepoClass bool
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
issuer = &TokenIssuer{}
|
||||
pkFile string
|
||||
addr string
|
||||
debug bool
|
||||
err error
|
||||
|
||||
passwdFile string
|
||||
realm string
|
||||
|
||||
cert string
|
||||
certKey string
|
||||
)
|
||||
|
||||
flag.StringVar(&issuer.Issuer, "issuer", "distribution-token-server", "Issuer string for token")
|
||||
flag.StringVar(&pkFile, "key", "", "Private key file")
|
||||
flag.StringVar(&addr, "addr", "localhost:8080", "Address to listen on")
|
||||
flag.BoolVar(&debug, "debug", false, "Debug mode")
|
||||
|
||||
flag.StringVar(&passwdFile, "passwd", ".htpasswd", "Passwd file")
|
||||
flag.StringVar(&realm, "realm", "", "Authentication realm")
|
||||
|
||||
flag.StringVar(&cert, "tlscert", "", "Certificate file for TLS")
|
||||
flag.StringVar(&certKey, "tlskey", "", "Certificate key for TLS")
|
||||
|
||||
flag.BoolVar(&enforceRepoClass, "enforce-class", false, "Enforce policy for single repository class")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
if pkFile == "" {
|
||||
issuer.SigningKey, err = libtrust.GenerateECP256PrivateKey()
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error generating private key: %v", err)
|
||||
}
|
||||
logrus.Debugf("Using newly generated key with id %s", issuer.SigningKey.KeyID())
|
||||
} else {
|
||||
issuer.SigningKey, err = libtrust.LoadKeyFile(pkFile)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error loading key file %s: %v", pkFile, err)
|
||||
}
|
||||
logrus.Debugf("Loaded private key with id %s", issuer.SigningKey.KeyID())
|
||||
}
|
||||
|
||||
if realm == "" {
|
||||
logrus.Fatalf("Must provide realm")
|
||||
}
|
||||
|
||||
ac, err := auth.GetAccessController("htpasswd", map[string]interface{}{
|
||||
"realm": realm,
|
||||
"path": passwdFile,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error initializing access controller: %v", err)
|
||||
}
|
||||
|
||||
// TODO: Make configurable
|
||||
issuer.Expiration = 15 * time.Minute
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
ts := &tokenServer{
|
||||
issuer: issuer,
|
||||
accessController: ac,
|
||||
refreshCache: map[string]refreshToken{},
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.Path("/token/").Methods("GET").Handler(handlerWithContext(ctx, ts.getToken))
|
||||
router.Path("/token/").Methods("POST").Handler(handlerWithContext(ctx, ts.postToken))
|
||||
|
||||
if cert == "" {
|
||||
err = http.ListenAndServe(addr, router)
|
||||
} else if certKey == "" {
|
||||
logrus.Fatalf("Must provide certficate (-tlscert) and key (-tlskey)")
|
||||
} else {
|
||||
err = http.ListenAndServeTLS(addr, cert, certKey, router)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logrus.Infof("Error serving: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// handlerWithContext wraps the given context-aware handler by setting up the
|
||||
// request context from a base context.
|
||||
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) {
|
||||
ctx := context.WithRequest(ctx, r)
|
||||
logger := context.GetRequestLogger(ctx)
|
||||
ctx = context.WithLogger(ctx, logger)
|
||||
|
||||
handler(ctx, w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func handleError(ctx context.Context, err error, w http.ResponseWriter) {
|
||||
ctx, w = context.WithResponseWriter(ctx, w)
|
||||
|
||||
if serveErr := errcode.ServeJSON(w, err); serveErr != nil {
|
||||
context.GetResponseLogger(ctx).Errorf("error sending error response: %v", serveErr)
|
||||
return
|
||||
}
|
||||
|
||||
context.GetResponseLogger(ctx).Info("application error")
|
||||
}
|
||||
|
||||
var refreshCharacters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
const refreshTokenLength = 15
|
||||
|
||||
func newRefreshToken() string {
|
||||
s := make([]rune, refreshTokenLength)
|
||||
for i := range s {
|
||||
s[i] = refreshCharacters[rand.Intn(len(refreshCharacters))]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type refreshToken struct {
|
||||
subject string
|
||||
service string
|
||||
}
|
||||
|
||||
type tokenServer struct {
|
||||
issuer *TokenIssuer
|
||||
accessController auth.AccessController
|
||||
refreshCache map[string]refreshToken
|
||||
}
|
||||
|
||||
type tokenResponse struct {
|
||||
Token string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
ExpiresIn int `json:"expires_in,omitempty"`
|
||||
}
|
||||
|
||||
var repositoryClassCache = map[string]string{}
|
||||
|
||||
func filterAccessList(ctx context.Context, scope string, requestedAccessList []auth.Access) []auth.Access {
|
||||
if !strings.HasSuffix(scope, "/") {
|
||||
scope = scope + "/"
|
||||
}
|
||||
grantedAccessList := make([]auth.Access, 0, len(requestedAccessList))
|
||||
for _, access := range requestedAccessList {
|
||||
if access.Type == "repository" {
|
||||
if !strings.HasPrefix(access.Name, scope) {
|
||||
context.GetLogger(ctx).Debugf("Resource scope not allowed: %s", access.Name)
|
||||
continue
|
||||
}
|
||||
if enforceRepoClass {
|
||||
if class, ok := repositoryClassCache[access.Name]; ok {
|
||||
if class != access.Class {
|
||||
context.GetLogger(ctx).Debugf("Different repository class: %q, previously %q", access.Class, class)
|
||||
continue
|
||||
}
|
||||
} else if strings.EqualFold(access.Action, "push") {
|
||||
repositoryClassCache[access.Name] = access.Class
|
||||
}
|
||||
}
|
||||
} else if access.Type == "registry" {
|
||||
if access.Name != "catalog" {
|
||||
context.GetLogger(ctx).Debugf("Unknown registry resource: %s", access.Name)
|
||||
continue
|
||||
}
|
||||
// TODO: Limit some actions to "admin" users
|
||||
} else {
|
||||
context.GetLogger(ctx).Debugf("Skipping unsupported resource type: %s", access.Type)
|
||||
continue
|
||||
}
|
||||
grantedAccessList = append(grantedAccessList, access)
|
||||
}
|
||||
return grantedAccessList
|
||||
}
|
||||
|
||||
type acctSubject struct{}
|
||||
|
||||
func (acctSubject) String() string { return "acctSubject" }
|
||||
|
||||
type requestedAccess struct{}
|
||||
|
||||
func (requestedAccess) String() string { return "requestedAccess" }
|
||||
|
||||
type grantedAccess struct{}
|
||||
|
||||
func (grantedAccess) String() string { return "grantedAccess" }
|
||||
|
||||
// getToken handles authenticating the request and authorizing access to the
|
||||
// requested scopes.
|
||||
func (ts *tokenServer) getToken(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
context.GetLogger(ctx).Info("getToken")
|
||||
|
||||
params := r.URL.Query()
|
||||
service := params.Get("service")
|
||||
scopeSpecifiers := params["scope"]
|
||||
var offline bool
|
||||
if offlineStr := params.Get("offline_token"); offlineStr != "" {
|
||||
var err error
|
||||
offline, err = strconv.ParseBool(offlineStr)
|
||||
if err != nil {
|
||||
handleError(ctx, ErrorBadTokenOption.WithDetail(err), w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
requestedAccessList := ResolveScopeSpecifiers(ctx, scopeSpecifiers)
|
||||
|
||||
authorizedCtx, err := ts.accessController.Authorized(ctx, requestedAccessList...)
|
||||
if err != nil {
|
||||
challenge, ok := err.(auth.Challenge)
|
||||
if !ok {
|
||||
handleError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
|
||||
// Get response context.
|
||||
ctx, w = context.WithResponseWriter(ctx, w)
|
||||
|
||||
challenge.SetHeaders(w)
|
||||
handleError(ctx, errcode.ErrorCodeUnauthorized.WithDetail(challenge.Error()), w)
|
||||
|
||||
context.GetResponseLogger(ctx).Info("get token authentication challenge")
|
||||
|
||||
return
|
||||
}
|
||||
ctx = authorizedCtx
|
||||
|
||||
username := context.GetStringValue(ctx, "auth.user.name")
|
||||
|
||||
ctx = context.WithValue(ctx, acctSubject{}, username)
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, acctSubject{}))
|
||||
|
||||
context.GetLogger(ctx).Info("authenticated client")
|
||||
|
||||
ctx = context.WithValue(ctx, requestedAccess{}, requestedAccessList)
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, requestedAccess{}))
|
||||
|
||||
grantedAccessList := filterAccessList(ctx, username, requestedAccessList)
|
||||
ctx = context.WithValue(ctx, grantedAccess{}, grantedAccessList)
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, grantedAccess{}))
|
||||
|
||||
token, err := ts.issuer.CreateJWT(username, service, grantedAccessList)
|
||||
if err != nil {
|
||||
handleError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
|
||||
context.GetLogger(ctx).Info("authorized client")
|
||||
|
||||
response := tokenResponse{
|
||||
Token: token,
|
||||
ExpiresIn: int(ts.issuer.Expiration.Seconds()),
|
||||
}
|
||||
|
||||
if offline {
|
||||
response.RefreshToken = newRefreshToken()
|
||||
ts.refreshCache[response.RefreshToken] = refreshToken{
|
||||
subject: username,
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
ctx, w = context.WithResponseWriter(ctx, w)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
|
||||
context.GetResponseLogger(ctx).Info("get token complete")
|
||||
}
|
||||
|
||||
type postTokenResponse struct {
|
||||
Token string `json:"access_token"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ExpiresIn int `json:"expires_in,omitempty"`
|
||||
IssuedAt string `json:"issued_at,omitempty"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
}
|
||||
|
||||
// postToken handles authenticating the request and authorizing access to the
|
||||
// requested scopes.
|
||||
func (ts *tokenServer) postToken(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
grantType := r.PostFormValue("grant_type")
|
||||
if grantType == "" {
|
||||
handleError(ctx, ErrorMissingRequiredField.WithDetail("missing grant_type value"), w)
|
||||
return
|
||||
}
|
||||
|
||||
service := r.PostFormValue("service")
|
||||
if service == "" {
|
||||
handleError(ctx, ErrorMissingRequiredField.WithDetail("missing service value"), w)
|
||||
return
|
||||
}
|
||||
|
||||
clientID := r.PostFormValue("client_id")
|
||||
if clientID == "" {
|
||||
handleError(ctx, ErrorMissingRequiredField.WithDetail("missing client_id value"), w)
|
||||
return
|
||||
}
|
||||
|
||||
var offline bool
|
||||
switch r.PostFormValue("access_type") {
|
||||
case "", "online":
|
||||
case "offline":
|
||||
offline = true
|
||||
default:
|
||||
handleError(ctx, ErrorUnsupportedValue.WithDetail("unknown access_type value"), w)
|
||||
return
|
||||
}
|
||||
|
||||
requestedAccessList := ResolveScopeList(ctx, r.PostFormValue("scope"))
|
||||
|
||||
var subject string
|
||||
var rToken string
|
||||
switch grantType {
|
||||
case "refresh_token":
|
||||
rToken = r.PostFormValue("refresh_token")
|
||||
if rToken == "" {
|
||||
handleError(ctx, ErrorUnsupportedValue.WithDetail("missing refresh_token value"), w)
|
||||
return
|
||||
}
|
||||
rt, ok := ts.refreshCache[rToken]
|
||||
if !ok || rt.service != service {
|
||||
handleError(ctx, errcode.ErrorCodeUnauthorized.WithDetail("invalid refresh token"), w)
|
||||
return
|
||||
}
|
||||
subject = rt.subject
|
||||
case "password":
|
||||
ca, ok := ts.accessController.(auth.CredentialAuthenticator)
|
||||
if !ok {
|
||||
handleError(ctx, ErrorUnsupportedValue.WithDetail("password grant type not supported"), w)
|
||||
return
|
||||
}
|
||||
subject = r.PostFormValue("username")
|
||||
if subject == "" {
|
||||
handleError(ctx, ErrorUnsupportedValue.WithDetail("missing username value"), w)
|
||||
return
|
||||
}
|
||||
password := r.PostFormValue("password")
|
||||
if password == "" {
|
||||
handleError(ctx, ErrorUnsupportedValue.WithDetail("missing password value"), w)
|
||||
return
|
||||
}
|
||||
if err := ca.AuthenticateUser(subject, password); err != nil {
|
||||
handleError(ctx, errcode.ErrorCodeUnauthorized.WithDetail("invalid credentials"), w)
|
||||
return
|
||||
}
|
||||
default:
|
||||
handleError(ctx, ErrorUnsupportedValue.WithDetail("unknown grant_type value"), w)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, acctSubject{}, subject)
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, acctSubject{}))
|
||||
|
||||
context.GetLogger(ctx).Info("authenticated client")
|
||||
|
||||
ctx = context.WithValue(ctx, requestedAccess{}, requestedAccessList)
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, requestedAccess{}))
|
||||
|
||||
grantedAccessList := filterAccessList(ctx, subject, requestedAccessList)
|
||||
ctx = context.WithValue(ctx, grantedAccess{}, grantedAccessList)
|
||||
ctx = context.WithLogger(ctx, context.GetLogger(ctx, grantedAccess{}))
|
||||
|
||||
token, err := ts.issuer.CreateJWT(subject, service, grantedAccessList)
|
||||
if err != nil {
|
||||
handleError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
|
||||
context.GetLogger(ctx).Info("authorized client")
|
||||
|
||||
response := postTokenResponse{
|
||||
Token: token,
|
||||
ExpiresIn: int(ts.issuer.Expiration.Seconds()),
|
||||
IssuedAt: time.Now().UTC().Format(time.RFC3339),
|
||||
Scope: ToScopeList(grantedAccessList),
|
||||
}
|
||||
|
||||
if offline {
|
||||
rToken = newRefreshToken()
|
||||
ts.refreshCache[rToken] = refreshToken{
|
||||
subject: subject,
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
if rToken != "" {
|
||||
response.RefreshToken = rToken
|
||||
}
|
||||
|
||||
ctx, w = context.WithResponseWriter(ctx, w)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
|
||||
context.GetResponseLogger(ctx).Info("post token complete")
|
||||
}
|
219
gateway/vendor/github.com/docker/distribution/contrib/token-server/token.go
generated
vendored
219
gateway/vendor/github.com/docker/distribution/contrib/token-server/token.go
generated
vendored
@ -1,219 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/registry/auth"
|
||||
"github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/docker/libtrust"
|
||||
)
|
||||
|
||||
// ResolveScopeSpecifiers converts a list of scope specifiers from a token
|
||||
// request's `scope` query parameters into a list of standard access objects.
|
||||
func ResolveScopeSpecifiers(ctx context.Context, scopeSpecs []string) []auth.Access {
|
||||
requestedAccessSet := make(map[auth.Access]struct{}, 2*len(scopeSpecs))
|
||||
|
||||
for _, scopeSpecifier := range scopeSpecs {
|
||||
// There should be 3 parts, separated by a `:` character.
|
||||
parts := strings.SplitN(scopeSpecifier, ":", 3)
|
||||
|
||||
if len(parts) != 3 {
|
||||
context.GetLogger(ctx).Infof("ignoring unsupported scope format %s", scopeSpecifier)
|
||||
continue
|
||||
}
|
||||
|
||||
resourceType, resourceName, actions := parts[0], parts[1], parts[2]
|
||||
|
||||
resourceType, resourceClass := splitResourceClass(resourceType)
|
||||
if resourceType == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Actions should be a comma-separated list of actions.
|
||||
for _, action := range strings.Split(actions, ",") {
|
||||
requestedAccess := auth.Access{
|
||||
Resource: auth.Resource{
|
||||
Type: resourceType,
|
||||
Class: resourceClass,
|
||||
Name: resourceName,
|
||||
},
|
||||
Action: action,
|
||||
}
|
||||
|
||||
// Add this access to the requested access set.
|
||||
requestedAccessSet[requestedAccess] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
requestedAccessList := make([]auth.Access, 0, len(requestedAccessSet))
|
||||
for requestedAccess := range requestedAccessSet {
|
||||
requestedAccessList = append(requestedAccessList, requestedAccess)
|
||||
}
|
||||
|
||||
return requestedAccessList
|
||||
}
|
||||
|
||||
var typeRegexp = regexp.MustCompile(`^([a-z0-9]+)(\([a-z0-9]+\))?$`)
|
||||
|
||||
func splitResourceClass(t string) (string, string) {
|
||||
matches := typeRegexp.FindStringSubmatch(t)
|
||||
if len(matches) < 2 {
|
||||
return "", ""
|
||||
}
|
||||
if len(matches) == 2 || len(matches[2]) < 2 {
|
||||
return matches[1], ""
|
||||
}
|
||||
return matches[1], matches[2][1 : len(matches[2])-1]
|
||||
}
|
||||
|
||||
// ResolveScopeList converts a scope list from a token request's
|
||||
// `scope` parameter into a list of standard access objects.
|
||||
func ResolveScopeList(ctx context.Context, scopeList string) []auth.Access {
|
||||
scopes := strings.Split(scopeList, " ")
|
||||
return ResolveScopeSpecifiers(ctx, scopes)
|
||||
}
|
||||
|
||||
func scopeString(a auth.Access) string {
|
||||
if a.Class != "" {
|
||||
return fmt.Sprintf("%s(%s):%s:%s", a.Type, a.Class, a.Name, a.Action)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s:%s", a.Type, a.Name, a.Action)
|
||||
}
|
||||
|
||||
// ToScopeList converts a list of access to a
|
||||
// scope list string
|
||||
func ToScopeList(access []auth.Access) string {
|
||||
var s []string
|
||||
for _, a := range access {
|
||||
s = append(s, scopeString(a))
|
||||
}
|
||||
return strings.Join(s, ",")
|
||||
}
|
||||
|
||||
// TokenIssuer represents an issuer capable of generating JWT tokens
|
||||
type TokenIssuer struct {
|
||||
Issuer string
|
||||
SigningKey libtrust.PrivateKey
|
||||
Expiration time.Duration
|
||||
}
|
||||
|
||||
// CreateJWT creates and signs a JSON Web Token for the given subject and
|
||||
// audience with the granted access.
|
||||
func (issuer *TokenIssuer) CreateJWT(subject string, audience string, grantedAccessList []auth.Access) (string, error) {
|
||||
// Make a set of access entries to put in the token's claimset.
|
||||
resourceActionSets := make(map[auth.Resource]map[string]struct{}, len(grantedAccessList))
|
||||
for _, access := range grantedAccessList {
|
||||
actionSet, exists := resourceActionSets[access.Resource]
|
||||
if !exists {
|
||||
actionSet = map[string]struct{}{}
|
||||
resourceActionSets[access.Resource] = actionSet
|
||||
}
|
||||
actionSet[access.Action] = struct{}{}
|
||||
}
|
||||
|
||||
accessEntries := make([]*token.ResourceActions, 0, len(resourceActionSets))
|
||||
for resource, actionSet := range resourceActionSets {
|
||||
actions := make([]string, 0, len(actionSet))
|
||||
for action := range actionSet {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
|
||||
accessEntries = append(accessEntries, &token.ResourceActions{
|
||||
Type: resource.Type,
|
||||
Class: resource.Class,
|
||||
Name: resource.Name,
|
||||
Actions: actions,
|
||||
})
|
||||
}
|
||||
|
||||
randomBytes := make([]byte, 15)
|
||||
_, err := io.ReadFull(rand.Reader, randomBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
randomID := base64.URLEncoding.EncodeToString(randomBytes)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
signingHash := crypto.SHA256
|
||||
var alg string
|
||||
switch issuer.SigningKey.KeyType() {
|
||||
case "RSA":
|
||||
alg = "RS256"
|
||||
case "EC":
|
||||
alg = "ES256"
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported signing key type %q", issuer.SigningKey.KeyType()))
|
||||
}
|
||||
|
||||
joseHeader := token.Header{
|
||||
Type: "JWT",
|
||||
SigningAlg: alg,
|
||||
}
|
||||
|
||||
if x5c := issuer.SigningKey.GetExtendedField("x5c"); x5c != nil {
|
||||
joseHeader.X5c = x5c.([]string)
|
||||
} else {
|
||||
var jwkMessage json.RawMessage
|
||||
jwkMessage, err = issuer.SigningKey.PublicKey().MarshalJSON()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
joseHeader.RawJWK = &jwkMessage
|
||||
}
|
||||
|
||||
exp := issuer.Expiration
|
||||
if exp == 0 {
|
||||
exp = 5 * time.Minute
|
||||
}
|
||||
|
||||
claimSet := token.ClaimSet{
|
||||
Issuer: issuer.Issuer,
|
||||
Subject: subject,
|
||||
Audience: audience,
|
||||
Expiration: now.Add(exp).Unix(),
|
||||
NotBefore: now.Unix(),
|
||||
IssuedAt: now.Unix(),
|
||||
JWTID: randomID,
|
||||
|
||||
Access: accessEntries,
|
||||
}
|
||||
|
||||
var (
|
||||
joseHeaderBytes []byte
|
||||
claimSetBytes []byte
|
||||
)
|
||||
|
||||
if joseHeaderBytes, err = json.Marshal(joseHeader); err != nil {
|
||||
return "", fmt.Errorf("unable to encode jose header: %s", err)
|
||||
}
|
||||
if claimSetBytes, err = json.Marshal(claimSet); err != nil {
|
||||
return "", fmt.Errorf("unable to encode claim set: %s", err)
|
||||
}
|
||||
|
||||
encodedJoseHeader := joseBase64Encode(joseHeaderBytes)
|
||||
encodedClaimSet := joseBase64Encode(claimSetBytes)
|
||||
encodingToSign := fmt.Sprintf("%s.%s", encodedJoseHeader, encodedClaimSet)
|
||||
|
||||
var signatureBytes []byte
|
||||
if signatureBytes, _, err = issuer.SigningKey.Sign(strings.NewReader(encodingToSign), signingHash); err != nil {
|
||||
return "", fmt.Errorf("unable to sign jwt payload: %s", err)
|
||||
}
|
||||
|
||||
signature := joseBase64Encode(signatureBytes)
|
||||
|
||||
return fmt.Sprintf("%s.%s", encodingToSign, signature), nil
|
||||
}
|
||||
|
||||
func joseBase64Encode(data []byte) string {
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(data), "=")
|
||||
}
|
7
gateway/vendor/github.com/docker/distribution/coverpkg.sh
generated
vendored
7
gateway/vendor/github.com/docker/distribution/coverpkg.sh
generated
vendored
@ -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 ' ' ','
|
371
gateway/vendor/github.com/docker/distribution/digestset/set_test.go
generated
vendored
371
gateway/vendor/github.com/docker/distribution/digestset/set_test.go
generated
vendored
@ -1,371 +0,0 @@
|
||||
package digestset
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func assertEqualDigests(t *testing.T, d1, d2 digest.Digest) {
|
||||
if d1 != d2 {
|
||||
t.Fatalf("Digests do not match:\n\tActual: %s\n\tExpected: %s", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
digests := []digest.Digest{
|
||||
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:5432111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6543111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6432111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6542111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6532111111111111111111111111111111111111111111111111111111111111",
|
||||
}
|
||||
|
||||
dset := NewSet()
|
||||
for i := range digests {
|
||||
if err := dset.Add(digests[i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
dgst, err := dset.Lookup("54")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqualDigests(t, dgst, digests[3])
|
||||
|
||||
dgst, err = dset.Lookup("1234")
|
||||
if err == nil {
|
||||
t.Fatal("Expected ambiguous error looking up: 1234")
|
||||
}
|
||||
if err != ErrDigestAmbiguous {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dgst, err = dset.Lookup("9876")
|
||||
if err == nil {
|
||||
t.Fatal("Expected ambiguous error looking up: 9876")
|
||||
}
|
||||
if err != ErrDigestNotFound {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dgst, err = dset.Lookup("sha256:1234")
|
||||
if err == nil {
|
||||
t.Fatal("Expected ambiguous error looking up: sha256:1234")
|
||||
}
|
||||
if err != ErrDigestAmbiguous {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dgst, err = dset.Lookup("sha256:12345")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqualDigests(t, dgst, digests[0])
|
||||
|
||||
dgst, err = dset.Lookup("sha256:12346")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqualDigests(t, dgst, digests[2])
|
||||
|
||||
dgst, err = dset.Lookup("12346")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqualDigests(t, dgst, digests[2])
|
||||
|
||||
dgst, err = dset.Lookup("12345")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEqualDigests(t, dgst, digests[0])
|
||||
}
|
||||
|
||||
func TestAddDuplication(t *testing.T) {
|
||||
digests := []digest.Digest{
|
||||
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:5432111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6543111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha512:65431111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha512:65421111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha512:65321111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
|
||||
}
|
||||
|
||||
dset := NewSet()
|
||||
for i := range digests {
|
||||
if err := dset.Add(digests[i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(dset.entries) != 8 {
|
||||
t.Fatal("Invalid dset size")
|
||||
}
|
||||
|
||||
if err := dset.Add(digest.Digest("sha256:1234511111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(dset.entries) != 8 {
|
||||
t.Fatal("Duplicate digest insert allowed")
|
||||
}
|
||||
|
||||
if err := dset.Add(digest.Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(dset.entries) != 9 {
|
||||
t.Fatal("Insert with different algorithm not allowed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
digests, err := createDigests(10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dset := NewSet()
|
||||
for i := range digests {
|
||||
if err := dset.Add(digests[i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
dgst, err := dset.Lookup(digests[0].String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if dgst != digests[0] {
|
||||
t.Fatalf("Unexpected digest value:\n\tExpected: %s\n\tActual: %s", digests[0], dgst)
|
||||
}
|
||||
|
||||
if err := dset.Remove(digests[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := dset.Lookup(digests[0].String()); err != ErrDigestNotFound {
|
||||
t.Fatalf("Expected error %v when looking up removed digest, got %v", ErrDigestNotFound, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
digests, err := createDigests(100)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dset := NewSet()
|
||||
for i := range digests {
|
||||
if err := dset.Add(digests[i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
all := map[digest.Digest]struct{}{}
|
||||
for _, dgst := range dset.All() {
|
||||
all[dgst] = struct{}{}
|
||||
}
|
||||
|
||||
if len(all) != len(digests) {
|
||||
t.Fatalf("Unexpected number of unique digests found:\n\tExpected: %d\n\tActual: %d", len(digests), len(all))
|
||||
}
|
||||
|
||||
for i, dgst := range digests {
|
||||
if _, ok := all[dgst]; !ok {
|
||||
t.Fatalf("Missing element at position %d: %s", i, dgst)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func assertEqualShort(t *testing.T, actual, expected string) {
|
||||
if actual != expected {
|
||||
t.Fatalf("Unexpected short value:\n\tExpected: %s\n\tActual: %s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortCodeTable(t *testing.T) {
|
||||
digests := []digest.Digest{
|
||||
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:5432111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6543111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6432111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6542111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:6532111111111111111111111111111111111111111111111111111111111111",
|
||||
}
|
||||
|
||||
dset := NewSet()
|
||||
for i := range digests {
|
||||
if err := dset.Add(digests[i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
dump := ShortCodeTable(dset, 2)
|
||||
|
||||
if len(dump) < len(digests) {
|
||||
t.Fatalf("Error unexpected size: %d, expecting %d", len(dump), len(digests))
|
||||
}
|
||||
assertEqualShort(t, dump[digests[0]], "12341")
|
||||
assertEqualShort(t, dump[digests[1]], "12345")
|
||||
assertEqualShort(t, dump[digests[2]], "12346")
|
||||
assertEqualShort(t, dump[digests[3]], "54")
|
||||
assertEqualShort(t, dump[digests[4]], "6543")
|
||||
assertEqualShort(t, dump[digests[5]], "64")
|
||||
assertEqualShort(t, dump[digests[6]], "6542")
|
||||
assertEqualShort(t, dump[digests[7]], "653")
|
||||
}
|
||||
|
||||
func createDigests(count int) ([]digest.Digest, error) {
|
||||
r := rand.New(rand.NewSource(25823))
|
||||
digests := make([]digest.Digest, count)
|
||||
for i := range digests {
|
||||
h := sha256.New()
|
||||
if err := binary.Write(h, binary.BigEndian, r.Int63()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
digests[i] = digest.NewDigest("sha256", h)
|
||||
}
|
||||
return digests, nil
|
||||
}
|
||||
|
||||
func benchAddNTable(b *testing.B, n int) {
|
||||
digests, err := createDigests(n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
|
||||
for j := range digests {
|
||||
if err = dset.Add(digests[j]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchLookupNTable(b *testing.B, n int, shortLen int) {
|
||||
digests, err := createDigests(n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
|
||||
for i := range digests {
|
||||
if err := dset.Add(digests[i]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
shorts := make([]string, 0, n)
|
||||
for _, short := range ShortCodeTable(dset, shortLen) {
|
||||
shorts = append(shorts, short)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err = dset.Lookup(shorts[i%n]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchRemoveNTable(b *testing.B, n int) {
|
||||
digests, err := createDigests(n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
|
||||
b.StopTimer()
|
||||
for j := range digests {
|
||||
if err = dset.Add(digests[j]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
b.StartTimer()
|
||||
for j := range digests {
|
||||
if err = dset.Remove(digests[j]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchShortCodeNTable(b *testing.B, n int, shortLen int) {
|
||||
digests, err := createDigests(n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
|
||||
for i := range digests {
|
||||
if err := dset.Add(digests[i]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ShortCodeTable(dset, shortLen)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdd10(b *testing.B) {
|
||||
benchAddNTable(b, 10)
|
||||
}
|
||||
|
||||
func BenchmarkAdd100(b *testing.B) {
|
||||
benchAddNTable(b, 100)
|
||||
}
|
||||
|
||||
func BenchmarkAdd1000(b *testing.B) {
|
||||
benchAddNTable(b, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkRemove10(b *testing.B) {
|
||||
benchRemoveNTable(b, 10)
|
||||
}
|
||||
|
||||
func BenchmarkRemove100(b *testing.B) {
|
||||
benchRemoveNTable(b, 100)
|
||||
}
|
||||
|
||||
func BenchmarkRemove1000(b *testing.B) {
|
||||
benchRemoveNTable(b, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkLookup10(b *testing.B) {
|
||||
benchLookupNTable(b, 10, 12)
|
||||
}
|
||||
|
||||
func BenchmarkLookup100(b *testing.B) {
|
||||
benchLookupNTable(b, 100, 12)
|
||||
}
|
||||
|
||||
func BenchmarkLookup1000(b *testing.B) {
|
||||
benchLookupNTable(b, 1000, 12)
|
||||
}
|
||||
|
||||
func BenchmarkShortCode10(b *testing.B) {
|
||||
benchShortCodeNTable(b, 10, 12)
|
||||
}
|
||||
func BenchmarkShortCode100(b *testing.B) {
|
||||
benchShortCodeNTable(b, 100, 12)
|
||||
}
|
||||
func BenchmarkShortCode1000(b *testing.B) {
|
||||
benchShortCodeNTable(b, 1000, 12)
|
||||
}
|
16
gateway/vendor/github.com/docker/distribution/docs/README.md
generated
vendored
16
gateway/vendor/github.com/docker/distribution/docs/README.md
generated
vendored
@ -1,16 +0,0 @@
|
||||
# The docs have been moved!
|
||||
|
||||
The documentation for Registry has been merged into
|
||||
[the general documentation repo](https://github.com/docker/docker.github.io).
|
||||
Commit history has been preserved.
|
||||
|
||||
The docs for Registry are now here:
|
||||
https://github.com/docker/docker.github.io/tree/master/registry
|
||||
|
||||
> Note: The definitive [./spec directory](spec/) directory and
|
||||
[configuration.md](configuration.md) file will be maintained in this repository
|
||||
and be refreshed periodically in
|
||||
[the general documentation repo](https://github.com/docker/docker.github.io).
|
||||
|
||||
As always, the docs in the general repo remain open-source and we appreciate
|
||||
your feedback and pull requests!
|
1120
gateway/vendor/github.com/docker/distribution/docs/configuration.md
generated
vendored
1120
gateway/vendor/github.com/docker/distribution/docs/configuration.md
generated
vendored
File diff suppressed because it is too large
Load Diff
5485
gateway/vendor/github.com/docker/distribution/docs/spec/api.md
generated
vendored
5485
gateway/vendor/github.com/docker/distribution/docs/spec/api.md
generated
vendored
File diff suppressed because it is too large
Load Diff
1215
gateway/vendor/github.com/docker/distribution/docs/spec/api.md.tmpl
generated
vendored
1215
gateway/vendor/github.com/docker/distribution/docs/spec/api.md.tmpl
generated
vendored
File diff suppressed because it is too large
Load Diff
12
gateway/vendor/github.com/docker/distribution/docs/spec/auth/index.md
generated
vendored
12
gateway/vendor/github.com/docker/distribution/docs/spec/auth/index.md
generated
vendored
@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Docker Registry Token Authentication"
|
||||
description: "Docker Registry v2 authentication schema"
|
||||
keywords: ["registry, on-prem, images, tags, repository, distribution, authentication, advanced"]
|
||||
---
|
||||
|
||||
# Docker Registry v2 authentication
|
||||
|
||||
See the [Token Authentication Specification](token.md),
|
||||
[Token Authentication Implementation](jwt.md),
|
||||
[Token Scope Documentation](scope.md),
|
||||
[OAuth2 Token Authentication](oauth.md) for more information.
|
329
gateway/vendor/github.com/docker/distribution/docs/spec/auth/jwt.md
generated
vendored
329
gateway/vendor/github.com/docker/distribution/docs/spec/auth/jwt.md
generated
vendored
@ -1,329 +0,0 @@
|
||||
---
|
||||
title: "Token Authentication Implementation"
|
||||
description: "Describe the reference implementation of the Docker Registry v2 authentication schema"
|
||||
keywords: ["registry, on-prem, images, tags, repository, distribution, JWT authentication, advanced"]
|
||||
---
|
||||
|
||||
# Docker Registry v2 Bearer token specification
|
||||
|
||||
This specification covers the `docker/distribution` implementation of the
|
||||
v2 Registry's authentication schema. Specifically, it describes the JSON
|
||||
Web Token schema that `docker/distribution` has adopted to implement the
|
||||
client-opaque Bearer token issued by an authentication service and
|
||||
understood by the registry.
|
||||
|
||||
This document borrows heavily from the [JSON Web Token Draft Spec](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32)
|
||||
|
||||
## Getting a Bearer Token
|
||||
|
||||
For this example, the client makes an HTTP GET request to the following URL:
|
||||
|
||||
```
|
||||
https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
|
||||
```
|
||||
|
||||
The token server should first attempt to authenticate the client using any
|
||||
authentication credentials provided with the request. As of Docker 1.8, the
|
||||
registry client in the Docker Engine only supports Basic Authentication to
|
||||
these token servers. If an attempt to authenticate to the token server fails,
|
||||
the token server should return a `401 Unauthorized` response indicating that
|
||||
the provided credentials are invalid.
|
||||
|
||||
Whether the token server requires authentication is up to the policy of that
|
||||
access control provider. Some requests may require authentication to determine
|
||||
access (such as pushing or pulling a private repository) while others may not
|
||||
(such as pulling from a public repository).
|
||||
|
||||
After authenticating the client (which may simply be an anonymous client if
|
||||
no attempt was made to authenticate), the token server must next query its
|
||||
access control list to determine whether the client has the requested scope. In
|
||||
this example request, if I have authenticated as user `jlhawn`, the token
|
||||
server will determine what access I have to the repository `samalba/my-app`
|
||||
hosted by the entity `registry.docker.io`.
|
||||
|
||||
Once the token server has determined what access the client has to the
|
||||
resources requested in the `scope` parameter, it will take the intersection of
|
||||
the set of requested actions on each resource and the set of actions that the
|
||||
client has in fact been granted. If the client only has a subset of the
|
||||
requested access **it must not be considered an error** as it is not the
|
||||
responsibility of the token server to indicate authorization errors as part of
|
||||
this workflow.
|
||||
|
||||
Continuing with the example request, the token server will find that the
|
||||
client's set of granted access to the repository is `[pull, push]` which when
|
||||
intersected with the requested access `[pull, push]` yields an equal set. If
|
||||
the granted access set was found only to be `[pull]` then the intersected set
|
||||
would only be `[pull]`. If the client has no access to the repository then the
|
||||
intersected set would be empty, `[]`.
|
||||
|
||||
It is this intersected set of access which is placed in the returned token.
|
||||
|
||||
The server will now construct a JSON Web Token to sign and return. A JSON Web
|
||||
Token has 3 main parts:
|
||||
|
||||
1. Headers
|
||||
|
||||
The header of a JSON Web Token is a standard JOSE header. The "typ" field
|
||||
will be "JWT" and it will also contain the "alg" which identifies the
|
||||
signing algorithm used to produce the signature. It also must have a "kid"
|
||||
field, representing the ID of the key which was used to sign the token.
|
||||
|
||||
The "kid" field has to be in a libtrust fingerprint compatible format.
|
||||
Such a format can be generated by following steps:
|
||||
|
||||
1. Take the DER encoded public key which the JWT token was signed against.
|
||||
|
||||
2. Create a SHA256 hash out of it and truncate to 240bits.
|
||||
|
||||
3. Split the result into 12 base32 encoded groups with `:` as delimiter.
|
||||
|
||||
Here is an example JOSE Header for a JSON Web Token (formatted with
|
||||
whitespace for readability):
|
||||
|
||||
```
|
||||
{
|
||||
"typ": "JWT",
|
||||
"alg": "ES256",
|
||||
"kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"
|
||||
}
|
||||
```
|
||||
|
||||
It specifies that this object is going to be a JSON Web token signed using
|
||||
the key with the given ID using the Elliptic Curve signature algorithm
|
||||
using a SHA256 hash.
|
||||
|
||||
2. Claim Set
|
||||
|
||||
The Claim Set is a JSON struct containing these standard registered claim
|
||||
name fields:
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>iss</code> (Issuer)
|
||||
</dt>
|
||||
<dd>
|
||||
The issuer of the token, typically the fqdn of the authorization
|
||||
server.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>sub</code> (Subject)
|
||||
</dt>
|
||||
<dd>
|
||||
The subject of the token; the name or id of the client which
|
||||
requested it. This should be empty (`""`) if the client did not
|
||||
authenticate.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>aud</code> (Audience)
|
||||
</dt>
|
||||
<dd>
|
||||
The intended audience of the token; the name or id of the service
|
||||
which will verify the token to authorize the client/subject.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>exp</code> (Expiration)
|
||||
</dt>
|
||||
<dd>
|
||||
The token should only be considered valid up to this specified date
|
||||
and time.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>nbf</code> (Not Before)
|
||||
</dt>
|
||||
<dd>
|
||||
The token should not be considered valid before this specified date
|
||||
and time.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>iat</code> (Issued At)
|
||||
</dt>
|
||||
<dd>
|
||||
Specifies the date and time which the Authorization server
|
||||
generated this token.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>jti</code> (JWT ID)
|
||||
</dt>
|
||||
<dd>
|
||||
A unique identifier for this token. Can be used by the intended
|
||||
audience to prevent replays of the token.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
The Claim Set will also contain a private claim name unique to this
|
||||
authorization server specification:
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>access</code>
|
||||
</dt>
|
||||
<dd>
|
||||
An array of access entry objects with the following fields:
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>type</code>
|
||||
</dt>
|
||||
<dd>
|
||||
The type of resource hosted by the service.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>name</code>
|
||||
</dt>
|
||||
<dd>
|
||||
The name of the resource of the given type hosted by the
|
||||
service.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>actions</code>
|
||||
</dt>
|
||||
<dd>
|
||||
An array of strings which give the actions authorized on
|
||||
this resource.
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
Here is an example of such a JWT Claim Set (formatted with whitespace for
|
||||
readability):
|
||||
|
||||
```
|
||||
{
|
||||
"iss": "auth.docker.com",
|
||||
"sub": "jlhawn",
|
||||
"aud": "registry.docker.com",
|
||||
"exp": 1415387315,
|
||||
"nbf": 1415387015,
|
||||
"iat": 1415387015,
|
||||
"jti": "tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws",
|
||||
"access": [
|
||||
{
|
||||
"type": "repository",
|
||||
"name": "samalba/my-app",
|
||||
"actions": [
|
||||
"pull",
|
||||
"push"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3. Signature
|
||||
|
||||
The authorization server will produce a JOSE header and Claim Set with no
|
||||
extraneous whitespace, i.e., the JOSE Header from above would be
|
||||
|
||||
```
|
||||
{"typ":"JWT","alg":"ES256","kid":"PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"}
|
||||
```
|
||||
|
||||
and the Claim Set from above would be
|
||||
|
||||
```
|
||||
{"iss":"auth.docker.com","sub":"jlhawn","aud":"registry.docker.com","exp":1415387315,"nbf":1415387015,"iat":1415387015,"jti":"tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws","access":[{"type":"repository","name":"samalba/my-app","actions":["push","pull"]}]}
|
||||
```
|
||||
|
||||
The utf-8 representation of this JOSE header and Claim Set are then
|
||||
url-safe base64 encoded (sans trailing '=' buffer), producing:
|
||||
|
||||
```
|
||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0
|
||||
```
|
||||
|
||||
for the JOSE Header and
|
||||
|
||||
```
|
||||
eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
|
||||
```
|
||||
|
||||
for the Claim Set. These two are concatenated using a '.' character,
|
||||
yielding the string:
|
||||
|
||||
```
|
||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
|
||||
```
|
||||
|
||||
This is then used as the payload to a the `ES256` signature algorithm
|
||||
specified in the JOSE header and specified fully in [Section 3.4 of the JSON Web Algorithms (JWA)
|
||||
draft specification](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-38#section-3.4)
|
||||
|
||||
This example signature will use the following ECDSA key for the server:
|
||||
|
||||
```
|
||||
{
|
||||
"kty": "EC",
|
||||
"crv": "P-256",
|
||||
"kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6",
|
||||
"d": "R7OnbfMaD5J2jl7GeE8ESo7CnHSBm_1N2k9IXYFrKJA",
|
||||
"x": "m7zUpx3b-zmVE5cymSs64POG9QcyEpJaYCD82-549_Q",
|
||||
"y": "dU3biz8sZ_8GPB-odm8Wxz3lNDr1xcAQQPQaOcr1fmc"
|
||||
}
|
||||
```
|
||||
|
||||
A resulting signature of the above payload using this key is:
|
||||
|
||||
```
|
||||
QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
|
||||
```
|
||||
|
||||
Concatenating all of these together with a `.` character gives the
|
||||
resulting JWT:
|
||||
|
||||
```
|
||||
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
|
||||
```
|
||||
|
||||
This can now be placed in an HTTP response and returned to the client to use to
|
||||
authenticate to the audience service:
|
||||
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w"}
|
||||
```
|
||||
|
||||
## Using the signed token
|
||||
|
||||
Once the client has a token, it will try the registry request again with the
|
||||
token placed in the HTTP `Authorization` header like so:
|
||||
|
||||
```
|
||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
|
||||
```
|
||||
|
||||
This is also described in [Section 2.1 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1)
|
||||
|
||||
## Verifying the token
|
||||
|
||||
The registry must now verify the token presented by the user by inspecting the
|
||||
claim set within. The registry will:
|
||||
|
||||
- Ensure that the issuer (`iss` claim) is an authority it trusts.
|
||||
- Ensure that the registry identifies as the audience (`aud` claim).
|
||||
- Check that the current time is between the `nbf` and `exp` claim times.
|
||||
- If enforcing single-use tokens, check that the JWT ID (`jti` claim) value has
|
||||
not been seen before.
|
||||
- To enforce this, the registry may keep a record of `jti`s it has seen for
|
||||
up to the `exp` time of the token to prevent token replays.
|
||||
- Check the `access` claim value and use the identified resources and the list
|
||||
of actions authorized to determine whether the token grants the required
|
||||
level of access for the operation the client is attempting to perform.
|
||||
- Verify that the signature of the token is valid.
|
||||
|
||||
If any of these requirements are not met, the registry will return a
|
||||
`403 Forbidden` response to indicate that the token is invalid.
|
||||
|
||||
**Note**: it is only at this point in the workflow that an authorization error
|
||||
may occur. The token server should *not* return errors when the user does not
|
||||
have the requested authorization. Instead, the returned token should indicate
|
||||
whatever of the requested scope the client does have (the intersection of
|
||||
requested and granted access). If the token does not supply proper
|
||||
authorization then the registry will return the appropriate error.
|
||||
|
||||
At no point in this process should the registry need to call back to the
|
||||
authorization server. The registry only needs to be supplied with the trusted
|
||||
public keys to verify the token signatures.
|
190
gateway/vendor/github.com/docker/distribution/docs/spec/auth/oauth.md
generated
vendored
190
gateway/vendor/github.com/docker/distribution/docs/spec/auth/oauth.md
generated
vendored
@ -1,190 +0,0 @@
|
||||
---
|
||||
title: "Oauth2 Token Authentication"
|
||||
description: "Specifies the Docker Registry v2 authentication"
|
||||
keywords: ["registry, on-prem, images, tags, repository, distribution, oauth2, advanced"]
|
||||
---
|
||||
|
||||
# Docker Registry v2 authentication using OAuth2
|
||||
|
||||
This document describes support for the OAuth2 protocol within the authorization
|
||||
server. [RFC6749](https://tools.ietf.org/html/rfc6749) should be used as a
|
||||
reference for the protocol and HTTP endpoints described here.
|
||||
|
||||
**Note**: Not all token servers implement oauth2. If the request to the endpoint
|
||||
returns `404` using the HTTP `POST` method, refer to
|
||||
[Token Documentation](token.md) for using the HTTP `GET` method supported by all
|
||||
token servers.
|
||||
|
||||
## Refresh token format
|
||||
|
||||
The format of the refresh token is completely opaque to the client and should be
|
||||
determined by the authorization server. The authorization should ensure the
|
||||
token is sufficiently long and is responsible for storing any information about
|
||||
long-lived tokens which may be needed for revoking. Any information stored
|
||||
inside the token will not be extracted and presented by clients.
|
||||
|
||||
## Getting a token
|
||||
|
||||
POST /token
|
||||
|
||||
#### Headers
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
#### Post parameters
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>grant_type</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(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
|
||||
accompanying username and password paramters. Type "authorization_code"
|
||||
is reserved for future use for authenticating to an authorization server
|
||||
without having to send credentials directly from the client. When
|
||||
requesting an access token with a refresh token this should be set to
|
||||
"refresh_token".
|
||||
</dd>
|
||||
<dt>
|
||||
<code>service</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(REQUIRED) The name of the service which hosts the resource to get
|
||||
access for. Refresh tokens will only be good for getting tokens for
|
||||
this service.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>client_id</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(REQUIRED) String identifying the client. This client_id does not need
|
||||
to be registered with the authorization server but should be set to a
|
||||
meaningful value in order to allow auditing keys created by unregistered
|
||||
clients. Accepted syntax is defined in
|
||||
[RFC6749 Appendix A.1](https://tools.ietf.org/html/rfc6749#appendix-A.1)
|
||||
</dd>
|
||||
<dt>
|
||||
<code>access_type</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(OPTIONAL) Access which is being requested. If "offline" is provided
|
||||
then a refresh token will be returned. The default is "online" only
|
||||
returning short lived access token. If the grant type is "refresh_token"
|
||||
this will only return the same refresh token and not a new one.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>scope</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(OPTIONAL) The resource in question, formatted as one of the space-delimited
|
||||
entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
|
||||
shown above. This query parameter should only be specified once but may
|
||||
contain multiple scopes using the scope list format defined in the scope
|
||||
grammar. If multiple <code>scope</code> is provided from
|
||||
<code>WWW-Authenticate</code> header the scopes should first be
|
||||
converted to a scope list before requesting the token. The above example
|
||||
would be specified as: <code>scope=repository:samalba/my-app:push</code>.
|
||||
When requesting a refresh token the scopes may be empty since the
|
||||
refresh token will not be limited by this scope, only the provided short
|
||||
lived access token will have the scope limitation.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>refresh_token</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(OPTIONAL) The refresh token to use for authentication when grant type "refresh_token" is used.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>username</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(OPTIONAL) The username to use for authentication when grant type "password" is used.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>password</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(OPTIONAL) The password to use for authentication when grant type "password" is used.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
#### Response fields
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>access_token</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(REQUIRED) An opaque <code>Bearer</code> token that clients should
|
||||
supply to subsequent requests in the <code>Authorization</code> header.
|
||||
This token should not be attempted to be parsed or understood by the
|
||||
client but treated as opaque string.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>scope</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(REQUIRED) The scope granted inside the access token. This may be the
|
||||
same scope as requested or a subset. This requirement is stronger than
|
||||
specified in [RFC6749 Section 4.2.2](https://tools.ietf.org/html/rfc6749#section-4.2.2)
|
||||
by strictly requiring the scope in the return value.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>expires_in</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(REQUIRED) The duration in seconds since the token was issued that it
|
||||
will remain valid. When omitted, this defaults to 60 seconds. For
|
||||
compatibility with older clients, a token should never be returned with
|
||||
less than 60 seconds to live.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>issued_at</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(Optional) The <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC3339</a>-serialized UTC
|
||||
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
|
||||
expiration is from when the token exchange completed.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>refresh_token</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(Optional) Token which can be used to get additional access tokens for
|
||||
the same subject with different scopes. This token should be kept secure
|
||||
by the client and only sent to the authorization server which issues
|
||||
bearer tokens. This field will only be set when `access_type=offline` is
|
||||
provided in the request.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
#### Example getting refresh token
|
||||
|
||||
```
|
||||
POST /token HTTP/1.1
|
||||
Host: auth.docker.io
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=password&username=johndoe&password=A3ddj3w&service=hub.docker.io&client_id=dockerengine&access_type=offline
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5","expires_in":900,"scope":""}
|
||||
```
|
||||
|
||||
#### Example refreshing an Access Token
|
||||
|
||||
```
|
||||
POST /token HTTP/1.1
|
||||
Host: auth.docker.io
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=refresh_token&refresh_token=kas9Da81Dfa8&service=registry-1.docker.io&client_id=dockerengine&scope=repository:samalba/my-app:pull,push
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5":"expires_in":900,"scope":"repository:samalba/my-app:pull,repository:samalba/my-app:push"}
|
||||
```
|
148
gateway/vendor/github.com/docker/distribution/docs/spec/auth/scope.md
generated
vendored
148
gateway/vendor/github.com/docker/distribution/docs/spec/auth/scope.md
generated
vendored
@ -1,148 +0,0 @@
|
||||
---
|
||||
title: "Token Scope Documentation"
|
||||
description: "Describes the scope and access fields used for registry authorization tokens"
|
||||
keywords: ["registry, on-prem, images, tags, repository, distribution, advanced, access, scope"]
|
||||
---
|
||||
|
||||
# Docker Registry Token Scope and Access
|
||||
|
||||
Tokens used by the registry are always restricted what resources they may
|
||||
be used to access, where those resources may be accessed, and what actions
|
||||
may be done on those resources. Tokens always have the context of a user which
|
||||
the token was originally created for. This document describes how these
|
||||
restrictions are represented and enforced by the authorization server and
|
||||
resource providers.
|
||||
|
||||
## Scope Components
|
||||
|
||||
### Subject (Authenticated User)
|
||||
|
||||
The subject represents the user for which a token is valid. Any actions
|
||||
performed using an access token should be considered on behalf of the subject.
|
||||
This is included in the `sub` field of access token JWT. A refresh token should
|
||||
be limited to a single subject and only be able to give out access tokens for
|
||||
that subject.
|
||||
|
||||
### Audience (Resource Provider)
|
||||
|
||||
The audience represents a resource provider which is intended to be able to
|
||||
perform the actions specified in the access token. Any resource provider which
|
||||
does not match the audience should not use that access token. The audience is
|
||||
included in the `aud` field of the access token JWT. A refresh token should be
|
||||
limited to a single audience and only be able to give out access tokens for that
|
||||
audience.
|
||||
|
||||
### Resource Type
|
||||
|
||||
The resource type represents the type of resource which the resource name is
|
||||
intended to represent. This type may be specific to a resource provider but must
|
||||
be understood by the authorization server in order to validate the subject
|
||||
is authorized for a specific resource.
|
||||
|
||||
#### Resource Class
|
||||
|
||||
The resource type might have a resource class which further classifies the
|
||||
the resource name within the resource type. A class is not required and
|
||||
is specific to the resource type.
|
||||
|
||||
#### Example Resource Types
|
||||
|
||||
- `repository` - represents a single repository within a registry. A
|
||||
repository may represent many manifest or content blobs, but the resource type
|
||||
is considered the collections of those items. Actions which may be performed on
|
||||
a `repository` are `pull` for accessing the collection and `push` for adding to
|
||||
it. By default the `repository` type has the class of `image`.
|
||||
- `repository(plugin)` - represents a single repository of plugins within a
|
||||
registry. A plugin repository has the same content and actions as a repository.
|
||||
- `registry` - represents the entire registry. Used for administrative actions
|
||||
or lookup operations that span an entire registry.
|
||||
|
||||
### Resource Name
|
||||
|
||||
The resource name represent the name which identifies a resource for a resource
|
||||
provider. A resource is identified by this name and the provided resource type.
|
||||
An example of a resource name would be the name component of an image tag, such
|
||||
as "samalba/myapp" or "hostname/samalba/myapp".
|
||||
|
||||
### Resource Actions
|
||||
|
||||
The resource actions define the actions which the access token allows to be
|
||||
performed on the identified resource. These actions are type specific but will
|
||||
normally have actions identifying read and write access on the resource. Example
|
||||
for the `repository` type are `pull` for read access and `push` for write
|
||||
access.
|
||||
|
||||
## Authorization Server Use
|
||||
|
||||
Each access token request may include a scope and an audience. The subject is
|
||||
always derived from the passed in credentials or refresh token. When using
|
||||
a refresh token the passed in audience must match the audience defined for
|
||||
the refresh token. The audience (resource provider) is provided using the
|
||||
`service` field. Multiple resource scopes may be provided using multiple `scope`
|
||||
fields on the `GET` request. The `POST` request only takes in a single
|
||||
`scope` field but may use a space to separate a list of multiple resource
|
||||
scopes.
|
||||
|
||||
### Resource Scope Grammar
|
||||
|
||||
```
|
||||
scope := resourcescope [ ' ' resourcescope ]*
|
||||
resourcescope := resourcetype ":" resourcename ":" action [ ',' action ]*
|
||||
resourcetype := resourcetypevalue [ '(' resourcetypevalue ')' ]
|
||||
resourcetypevalue := /[a-z0-9]+/
|
||||
resourcename := [ hostname '/' ] component [ '/' component ]*
|
||||
hostname := hostcomponent ['.' hostcomponent]* [':' port-number]
|
||||
hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
||||
port-number := /[0-9]+/
|
||||
action := /[a-z]*/
|
||||
component := alpha-numeric [ separator alpha-numeric ]*
|
||||
alpha-numeric := /[a-z0-9]+/
|
||||
separator := /[_.]|__|[-]*/
|
||||
```
|
||||
Full reference grammar is defined
|
||||
[here](https://godoc.org/github.com/docker/distribution/reference). Currently
|
||||
the scope name grammar is a subset of the reference grammar.
|
||||
|
||||
> **NOTE:** that the `resourcename` may contain one `:` due to a possible port
|
||||
> number in the hostname component of the `resourcename`, so a naive
|
||||
> implementation that interprets the first three `:`-delimited tokens of a
|
||||
> `scope` to be the `resourcetype`, `resourcename`, and a list of `action`
|
||||
> would be insufficient.
|
||||
|
||||
## Resource Provider Use
|
||||
|
||||
Once a resource provider has verified the authenticity of the scope through
|
||||
JWT access token verification, the resource provider must ensure that scope
|
||||
satisfies the request. The resource provider should match the given audience
|
||||
according to name or URI the resource provider uses to identify itself. Any
|
||||
denial based on subject is not defined here and is up to resource provider, the
|
||||
subject is mainly provided for audit logs and any other user-specific rules
|
||||
which may need to be provided but are not defined by the authorization server.
|
||||
|
||||
The resource provider must ensure that ANY resource being accessed as the
|
||||
result of a request has the appropriate access scope. Both the resource type
|
||||
and resource name must match the accessed resource and an appropriate action
|
||||
scope must be included.
|
||||
|
||||
When appropriate authorization is not provided either due to lack of scope
|
||||
or missing token, the resource provider to return a `WWW-AUTHENTICATE` HTTP
|
||||
header with the `realm` as the authorization server, the `service` as the
|
||||
expected audience identifying string, and a `scope` field for each required
|
||||
resource scope to complete the request.
|
||||
|
||||
## JWT Access Tokens
|
||||
|
||||
Each JWT access token may only have a single subject and audience but multiple
|
||||
resource scopes. The subject and audience are put into standard JWT fields
|
||||
`sub` and `aud`. The resource scope is put into the `access` field. The
|
||||
structure of the access field can be seen in the
|
||||
[jwt documentation](jwt.md).
|
||||
|
||||
## Refresh Tokens
|
||||
|
||||
A refresh token must be defined for a single subject and audience. Further
|
||||
restricting scope to specific type, name, and actions combinations should be
|
||||
done by fetching an access token using the refresh token. Since the refresh
|
||||
token is not scoped to specific resources for an audience, extra care should
|
||||
be taken to only use the refresh token to negotiate new access tokens directly
|
||||
with the authorization server, and never with a resource provider.
|
250
gateway/vendor/github.com/docker/distribution/docs/spec/auth/token.md
generated
vendored
250
gateway/vendor/github.com/docker/distribution/docs/spec/auth/token.md
generated
vendored
@ -1,250 +0,0 @@
|
||||
---
|
||||
title: "Token Authentication Specification"
|
||||
description: "Specifies the Docker Registry v2 authentication"
|
||||
keywords: ["registry, on-prem, images, tags, repository, distribution, Bearer authentication, advanced"]
|
||||
---
|
||||
|
||||
# Docker Registry v2 authentication via central service
|
||||
|
||||
This document outlines the v2 Docker registry authentication scheme:
|
||||
|
||||

|
||||
|
||||
1. Attempt to begin a push/pull operation with the registry.
|
||||
2. If the registry requires authorization it will return a `401 Unauthorized`
|
||||
HTTP response with information on how to authenticate.
|
||||
3. The registry client makes a request to the authorization service for a
|
||||
Bearer token.
|
||||
4. The authorization service returns an opaque Bearer token representing the
|
||||
client's authorized access.
|
||||
5. The client retries the original request with the Bearer token embedded in
|
||||
the request's Authorization header.
|
||||
6. The Registry authorizes the client by validating the Bearer token and the
|
||||
claim set embedded within it and begins the push/pull session as usual.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Registry clients which can understand and respond to token auth challenges
|
||||
returned by the resource server.
|
||||
- An authorization server capable of managing access controls to their
|
||||
resources hosted by any given service (such as repositories in a Docker
|
||||
Registry).
|
||||
- A Docker Registry capable of trusting the authorization server to sign tokens
|
||||
which clients can use for authorization and the ability to verify these
|
||||
tokens for single use or for use during a sufficiently short period of time.
|
||||
|
||||
## Authorization Server Endpoint Descriptions
|
||||
|
||||
The described server is meant to serve as a standalone access control manager
|
||||
for resources hosted by other services which wish to authenticate and manage
|
||||
authorizations using a separate access control manager.
|
||||
|
||||
A service like this is used by the official Docker Registry to authenticate
|
||||
clients and verify their authorization to Docker image repositories.
|
||||
|
||||
As of Docker 1.6, the registry client within the Docker Engine has been updated
|
||||
to handle such an authorization workflow.
|
||||
|
||||
## How to authenticate
|
||||
|
||||
Registry V1 clients first contact the index to initiate a push or pull. Under
|
||||
the Registry V2 workflow, clients should contact the registry first. If the
|
||||
registry server requires authentication it will return a `401 Unauthorized`
|
||||
response with a `WWW-Authenticate` header detailing how to authenticate to this
|
||||
registry.
|
||||
|
||||
For example, say I (username `jlhawn`) am attempting to push an image to the
|
||||
repository `samalba/my-app`. For the registry to authorize this, I will need
|
||||
`push` access to the `samalba/my-app` repository. The registry will first
|
||||
return this response:
|
||||
|
||||
```
|
||||
HTTP/1.1 401 Unauthorized
|
||||
Content-Type: application/json; charset=utf-8
|
||||
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"
|
||||
Date: Thu, 10 Sep 2015 19:32:31 GMT
|
||||
Content-Length: 235
|
||||
Strict-Transport-Security: max-age=31536000
|
||||
|
||||
{"errors":[{"code":"UNAUTHORIZED","message":"access to the requested resource is not authorized","detail":[{"Type":"repository","Name":"samalba/my-app","Action":"pull"},{"Type":"repository","Name":"samalba/my-app","Action":"push"}]}]}
|
||||
```
|
||||
|
||||
Note the HTTP Response Header indicating the auth challenge:
|
||||
|
||||
```
|
||||
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push"
|
||||
```
|
||||
|
||||
This format is documented in [Section 3 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-3)
|
||||
|
||||
This challenge indicates that the registry requires a token issued by the
|
||||
specified token server and that the request the client is attempting will
|
||||
need to include sufficient access entries in its claim set. To respond to this
|
||||
challenge, the client will need to make a `GET` request to the URL
|
||||
`https://auth.docker.io/token` using the `service` and `scope` values from the
|
||||
`WWW-Authenticate` header.
|
||||
|
||||
## Requesting a Token
|
||||
|
||||
Defines getting a bearer and refresh token using the token endpoint.
|
||||
|
||||
#### Query Parameters
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>service</code>
|
||||
</dt>
|
||||
<dd>
|
||||
The name of the service which hosts the resource.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>offline_token</code>
|
||||
</dt>
|
||||
<dd>
|
||||
Whether to return a refresh token along with the bearer token. A refresh
|
||||
token is capable of getting additional bearer tokens for the same
|
||||
subject with different scopes. The refresh token does not have an
|
||||
expiration and should be considered completely opaque to the client.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>client_id</code>
|
||||
</dt>
|
||||
<dd>
|
||||
String identifying the client. This client_id does not need
|
||||
to be registered with the authorization server but should be set to a
|
||||
meaningful value in order to allow auditing keys created by unregistered
|
||||
clients. Accepted syntax is defined in
|
||||
[RFC6749 Appendix A.1](https://tools.ietf.org/html/rfc6749#appendix-A.1).
|
||||
</dd>
|
||||
<dt>
|
||||
<code>scope</code>
|
||||
</dt>
|
||||
<dd>
|
||||
The resource in question, formatted as one of the space-delimited
|
||||
entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
|
||||
shown above. This query parameter should be specified multiple times if
|
||||
there is more than one <code>scope</code> entry from the <code>WWW-Authenticate</code>
|
||||
header. The above example would be specified as:
|
||||
<code>scope=repository:samalba/my-app:push</code>. The scope field may
|
||||
be empty to request a refresh token without providing any resource
|
||||
permissions to the returned bearer token.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
#### Token Response Fields
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<code>token</code>
|
||||
</dt>
|
||||
<dd>
|
||||
An opaque <code>Bearer</code> token that clients should supply to subsequent
|
||||
requests in the <code>Authorization</code> header.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>access_token</code>
|
||||
</dt>
|
||||
<dd>
|
||||
For compatibility with OAuth 2.0, we will also accept <code>token</code> under the name
|
||||
<code>access_token</code>. At least one of these fields <b>must</b> be specified, but
|
||||
both may also appear (for compatibility with older clients). When both are specified,
|
||||
they should be equivalent; if they differ the client's choice is undefined.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>expires_in</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(Optional) The duration in seconds since the token was issued that it
|
||||
will remain valid. When omitted, this defaults to 60 seconds. For
|
||||
compatibility with older clients, a token should never be returned with
|
||||
less than 60 seconds to live.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>issued_at</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(Optional) The <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC3339</a>-serialized UTC
|
||||
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
|
||||
expiration is from when the token exchange completed.
|
||||
</dd>
|
||||
<dt>
|
||||
<code>refresh_token</code>
|
||||
</dt>
|
||||
<dd>
|
||||
(Optional) Token which can be used to get additional access tokens for
|
||||
the same subject with different scopes. This token should be kept secure
|
||||
by the client and only sent to the authorization server which issues
|
||||
bearer tokens. This field will only be set when `offline_token=true` is
|
||||
provided in the request.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
#### Example
|
||||
|
||||
For this example, the client makes an HTTP GET request to the following URL:
|
||||
|
||||
```
|
||||
https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
|
||||
```
|
||||
|
||||
The token server should first attempt to authenticate the client using any
|
||||
authentication credentials provided with the request. From Docker 1.11 the
|
||||
Docker engine supports both Basic Authentication and [OAuth2](oauth.md) for
|
||||
getting tokens. Docker 1.10 and before, the registry client in the Docker Engine
|
||||
only supports Basic Authentication. If an attempt to authenticate to the token
|
||||
server fails, the token server should return a `401 Unauthorized` response
|
||||
indicating that the provided credentials are invalid.
|
||||
|
||||
Whether the token server requires authentication is up to the policy of that
|
||||
access control provider. Some requests may require authentication to determine
|
||||
access (such as pushing or pulling a private repository) while others may not
|
||||
(such as pulling from a public repository).
|
||||
|
||||
After authenticating the client (which may simply be an anonymous client if
|
||||
no attempt was made to authenticate), the token server must next query its
|
||||
access control list to determine whether the client has the requested scope. In
|
||||
this example request, if I have authenticated as user `jlhawn`, the token
|
||||
server will determine what access I have to the repository `samalba/my-app`
|
||||
hosted by the entity `registry.docker.io`.
|
||||
|
||||
Once the token server has determined what access the client has to the
|
||||
resources requested in the `scope` parameter, it will take the intersection of
|
||||
the set of requested actions on each resource and the set of actions that the
|
||||
client has in fact been granted. If the client only has a subset of the
|
||||
requested access **it must not be considered an error** as it is not the
|
||||
responsibility of the token server to indicate authorization errors as part of
|
||||
this workflow.
|
||||
|
||||
Continuing with the example request, the token server will find that the
|
||||
client's set of granted access to the repository is `[pull, push]` which when
|
||||
intersected with the requested access `[pull, push]` yields an equal set. If
|
||||
the granted access set was found only to be `[pull]` then the intersected set
|
||||
would only be `[pull]`. If the client has no access to the repository then the
|
||||
intersected set would be empty, `[]`.
|
||||
|
||||
It is this intersected set of access which is placed in the returned token.
|
||||
|
||||
The server then constructs an implementation-specific token with this
|
||||
intersected set of access, and returns it to the Docker client to use to
|
||||
authenticate to the audience service (within the indicated window of time):
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w", "expires_in": 3600,"issued_at": "2009-11-10T23:00:00Z"}
|
||||
```
|
||||
|
||||
|
||||
## Using the Bearer token
|
||||
|
||||
Once the client has a token, it will try the registry request again with the
|
||||
token placed in the HTTP `Authorization` header like so:
|
||||
|
||||
```
|
||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
|
||||
```
|
||||
|
||||
This is also described in [Section 2.1 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1)
|
30
gateway/vendor/github.com/docker/distribution/docs/spec/implementations.md
generated
vendored
30
gateway/vendor/github.com/docker/distribution/docs/spec/implementations.md
generated
vendored
@ -1,30 +0,0 @@
|
||||
---
|
||||
published: false
|
||||
---
|
||||
|
||||
# Distribution API Implementations
|
||||
|
||||
This is a list of known implementations of the Distribution API spec.
|
||||
|
||||
## [Docker Distribution Registry](https://github.com/docker/distribution)
|
||||
|
||||
Docker distribution is the reference implementation of the distribution API
|
||||
specification. It aims to fully implement the entire specification.
|
||||
|
||||
### Releases
|
||||
#### 2.0.1 (_in development_)
|
||||
Implements API 2.0.1
|
||||
|
||||
_Known Issues_
|
||||
- No resumable push support
|
||||
- Content ranges ignored
|
||||
- Blob upload status will always return a starting range of 0
|
||||
|
||||
#### 2.0.0
|
||||
Implements API 2.0.0
|
||||
|
||||
_Known Issues_
|
||||
- No resumable push support
|
||||
- No PATCH implementation for blob upload
|
||||
- Content ranges ignored
|
||||
|
12
gateway/vendor/github.com/docker/distribution/docs/spec/index.md
generated
vendored
12
gateway/vendor/github.com/docker/distribution/docs/spec/index.md
generated
vendored
@ -1,12 +0,0 @@
|
||||
---
|
||||
title: "Reference Overview"
|
||||
description: "Explains registry JSON objects"
|
||||
keywords: ["registry, service, images, repository, json"]
|
||||
---
|
||||
|
||||
# Docker Registry Reference
|
||||
|
||||
* [HTTP API V2](api.md)
|
||||
* [Storage Driver](../storage-drivers/index.md)
|
||||
* [Token Authentication Specification](auth/token.md)
|
||||
* [Token Authentication Implementation](auth/jwt.md)
|
90
gateway/vendor/github.com/docker/distribution/docs/spec/json.md
generated
vendored
90
gateway/vendor/github.com/docker/distribution/docs/spec/json.md
generated
vendored
@ -1,90 +0,0 @@
|
||||
---
|
||||
published: false
|
||||
title: "Docker Distribution JSON Canonicalization"
|
||||
description: "Explains registry JSON objects"
|
||||
keywords: ["registry, service, images, repository, json"]
|
||||
---
|
||||
|
||||
|
||||
|
||||
# Docker Distribution JSON Canonicalization
|
||||
|
||||
To provide consistent content hashing of JSON objects throughout Docker
|
||||
Distribution APIs, the specification defines a canonical JSON format. Adopting
|
||||
such a canonicalization also aids in caching JSON responses.
|
||||
|
||||
Note that protocols should not be designed to depend on identical JSON being
|
||||
generated across different versions or clients. The canonicalization rules are
|
||||
merely useful for caching and consistency.
|
||||
|
||||
## Rules
|
||||
|
||||
Compliant JSON should conform to the following rules:
|
||||
|
||||
1. All generated JSON should comply with [RFC
|
||||
7159](http://www.ietf.org/rfc/rfc7159.txt).
|
||||
2. Resulting "JSON text" shall always be encoded in UTF-8.
|
||||
3. Unless a canonical key order is defined for a particular schema, object
|
||||
keys shall always appear in lexically sorted order.
|
||||
4. All whitespace between tokens should be removed.
|
||||
5. No "trailing commas" are allowed in object or array definitions.
|
||||
6. The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e".
|
||||
Ampersand "&" is escaped to "\u0026".
|
||||
|
||||
## Examples
|
||||
|
||||
The following is a simple example of a canonicalized JSON string:
|
||||
|
||||
```json
|
||||
{"asdf":1,"qwer":[],"zxcv":[{},true,1000000000,"tyui"]}
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
### Other Canonicalizations
|
||||
|
||||
The OLPC project specifies [Canonical
|
||||
JSON](http://wiki.laptop.org/go/Canonical_JSON). While this is used in
|
||||
[TUF](http://theupdateframework.com/), which may be used with other
|
||||
distribution-related protocols, this alternative format has been proposed in
|
||||
case the original source changes. Specifications complying with either this
|
||||
specification or an alternative should explicitly call out the
|
||||
canonicalization format. Except for key ordering, this specification is mostly
|
||||
compatible.
|
||||
|
||||
### Go
|
||||
|
||||
In Go, the [`encoding/json`](http://golang.org/pkg/encoding/json/) library
|
||||
will emit canonical JSON by default. Simply using `json.Marshal` will suffice
|
||||
in most cases:
|
||||
|
||||
```go
|
||||
incoming := map[string]interface{}{
|
||||
"asdf": 1,
|
||||
"qwer": []interface{}{},
|
||||
"zxcv": []interface{}{
|
||||
map[string]interface{}{},
|
||||
true,
|
||||
int(1e9),
|
||||
"tyui",
|
||||
},
|
||||
}
|
||||
|
||||
canonical, err := json.Marshal(incoming)
|
||||
if err != nil {
|
||||
// ... handle error
|
||||
}
|
||||
```
|
||||
|
||||
To apply canonical JSON format spacing to an existing serialized JSON buffer, one
|
||||
can use
|
||||
[`json.Indent`](http://golang.org/src/encoding/json/indent.go?s=1918:1989#L65)
|
||||
with the following arguments:
|
||||
|
||||
```go
|
||||
incoming := getBytes()
|
||||
var canonical bytes.Buffer
|
||||
if err := json.Indent(&canonical, incoming, "", ""); err != nil {
|
||||
// ... handle error
|
||||
}
|
||||
```
|
163
gateway/vendor/github.com/docker/distribution/docs/spec/manifest-v2-1.md
generated
vendored
163
gateway/vendor/github.com/docker/distribution/docs/spec/manifest-v2-1.md
generated
vendored
@ -1,163 +0,0 @@
|
||||
---
|
||||
title: "Image Manifest V 2, Schema 1 "
|
||||
description: "image manifest for the Registry."
|
||||
keywords: ["registry, on-prem, images, tags, repository, distribution, api, advanced, manifest"]
|
||||
---
|
||||
|
||||
# Image Manifest Version 2, Schema 1
|
||||
|
||||
This document outlines the format of the V2 image manifest. The image
|
||||
manifest described herein was introduced in the Docker daemon in the [v1.3.0
|
||||
release](https://github.com/docker/docker/commit/9f482a66ab37ec396ac61ed0c00d59122ac07453).
|
||||
It is a provisional manifest to provide a compatibility with the [V1 Image
|
||||
format](https://github.com/docker/docker/blob/master/image/spec/v1.md), as the
|
||||
requirements are defined for the [V2 Schema 2
|
||||
image](https://github.com/docker/distribution/pull/62).
|
||||
|
||||
|
||||
Image manifests describe the various constituents of a docker image. Image
|
||||
manifests can be serialized to JSON format with the following media types:
|
||||
|
||||
Manifest Type | Media Type
|
||||
------------- | -------------
|
||||
manifest | "application/vnd.docker.distribution.manifest.v1+json"
|
||||
signed manifest | "application/vnd.docker.distribution.manifest.v1+prettyjws"
|
||||
|
||||
*Note that "application/json" will also be accepted for schema 1.*
|
||||
|
||||
References:
|
||||
|
||||
- [Proposal: JSON Registry API V2.1](https://github.com/docker/docker/issues/9015)
|
||||
- [Proposal: Provenance step 1 - Transform images for validation and verification](https://github.com/docker/docker/issues/8093)
|
||||
|
||||
## *Manifest* Field Descriptions
|
||||
|
||||
Manifest provides the base accessible fields for working with V2 image format
|
||||
in the registry.
|
||||
|
||||
- **`name`** *string*
|
||||
|
||||
name is the name of the image's repository
|
||||
|
||||
- **`tag`** *string*
|
||||
|
||||
tag is the tag of the image
|
||||
|
||||
- **`architecture`** *string*
|
||||
|
||||
architecture is the host architecture on which this image is intended to
|
||||
run. This is for information purposes and not currently used by the engine
|
||||
|
||||
- **`fsLayers`** *array*
|
||||
|
||||
fsLayers is a list of filesystem layer blob sums contained in this image.
|
||||
|
||||
An fsLayer is a struct consisting of the following fields
|
||||
- **`blobSum`** *digest.Digest*
|
||||
|
||||
blobSum is the digest of the referenced filesystem image layer. A
|
||||
digest must be a sha256 hash.
|
||||
|
||||
|
||||
- **`history`** *array*
|
||||
|
||||
history is a list of unstructured historical data for v1 compatibility. It
|
||||
contains ID of the image layer and ID of the layer's parent layers.
|
||||
|
||||
history is a struct consisting of the following fields
|
||||
- **`v1Compatibility`** string
|
||||
|
||||
V1Compatibility is the raw V1 compatibility information. This will
|
||||
contain the JSON object describing the V1 of this image.
|
||||
|
||||
- **`schemaVersion`** *int*
|
||||
|
||||
SchemaVersion is the image manifest schema that this image follows.
|
||||
|
||||
>**Note**:the length of `history` must be equal to the length of `fsLayers` and
|
||||
>entries in each are correlated by index.
|
||||
|
||||
## Signed Manifests
|
||||
|
||||
Signed manifests provides an envelope for a signed image manifest. A signed
|
||||
manifest consists of an image manifest along with an additional field
|
||||
containing the signature of the manifest.
|
||||
|
||||
The docker client can verify signed manifests and displays a message to the user.
|
||||
|
||||
### Signing Manifests
|
||||
|
||||
Image manifests can be signed in two different ways: with a *libtrust* private
|
||||
key or an x509 certificate chain. When signing with an x509 certificate chain,
|
||||
the public key of the first element in the chain must be the public key
|
||||
corresponding with the sign key.
|
||||
|
||||
### Signed Manifest Field Description
|
||||
|
||||
Signed manifests include an image manifest and a list of signatures generated
|
||||
by *libtrust*. A signature consists of the following fields:
|
||||
|
||||
|
||||
- **`header`** *[JOSE](http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2)*
|
||||
|
||||
A [JSON Web Signature](http://self-issued.info/docs/draft-ietf-jose-json-web-signature.html)
|
||||
|
||||
- **`signature`** *string*
|
||||
|
||||
A signature for the image manifest, signed by a *libtrust* private key
|
||||
|
||||
- **`protected`** *string*
|
||||
|
||||
The signed protected header
|
||||
|
||||
## Example Manifest
|
||||
|
||||
*Example showing the official 'hello-world' image manifest.*
|
||||
|
||||
```
|
||||
{
|
||||
"name": "hello-world",
|
||||
"tag": "latest",
|
||||
"architecture": "amd64",
|
||||
"fsLayers": [
|
||||
{
|
||||
"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
},
|
||||
{
|
||||
"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
},
|
||||
{
|
||||
"blobSum": "sha256:cc8567d70002e957612902a8e985ea129d831ebe04057d88fb644857caa45d11"
|
||||
},
|
||||
{
|
||||
"blobSum": "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
||||
}
|
||||
],
|
||||
"history": [
|
||||
{
|
||||
"v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n"
|
||||
},
|
||||
{
|
||||
"v1Compatibility": "{\"id\":\"e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5\",\"parent\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"created\":\"2014-12-31T22:57:59.178729048Z\",\"container\":\"27b45f8fb11795b52e9605b686159729b0d9ca92f76d40fb4f05a62e19c46b4f\",\"container_config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) CMD [/hello]\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"docker_version\":\"1.4.1\",\"config\":{\"Hostname\":\"8ce6509d66e2\",\"Domainname\":\"\",\"User\":\"\",\"Memory\":0,\"MemorySwap\":0,\"CpuShares\":0,\"Cpuset\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"PortSpecs\":null,\"ExposedPorts\":null,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/hello\"],\"Image\":\"31cbccb51277105ba3ae35ce33c22b69c9e3f1002e76e4c736a2e8ebff9d7b5d\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"NetworkDisabled\":false,\"MacAddress\":\"\",\"OnBuild\":[],\"SecurityOpt\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":0}\n"
|
||||
},
|
||||
],
|
||||
"schemaVersion": 1,
|
||||
"signatures": [
|
||||
{
|
||||
"header": {
|
||||
"jwk": {
|
||||
"crv": "P-256",
|
||||
"kid": "OD6I:6DRK:JXEJ:KBM4:255X:NSAA:MUSF:E4VM:ZI6W:CUN2:L4Z6:LSF4",
|
||||
"kty": "EC",
|
||||
"x": "3gAwX48IQ5oaYQAYSxor6rYYc_6yjuLCjtQ9LUakg4A",
|
||||
"y": "t72ge6kIA1XOjqjVoEOiPPAURltJFBMGDSQvEGVB010"
|
||||
},
|
||||
"alg": "ES256"
|
||||
},
|
||||
"signature": "XREm0L8WNn27Ga_iE_vRnTxVMhhYY0Zst_FfkKopg6gWSoTOZTuW4rK0fg_IqnKkEKlbD83tD46LKEGi5aIVFg",
|
||||
"protected": "eyJmb3JtYXRMZW5ndGgiOjY2MjgsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNS0wNC0wOFQxODo1Mjo1OVoifQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
295
gateway/vendor/github.com/docker/distribution/docs/spec/manifest-v2-2.md
generated
vendored
295
gateway/vendor/github.com/docker/distribution/docs/spec/manifest-v2-2.md
generated
vendored
@ -1,295 +0,0 @@
|
||||
---
|
||||
title: "Image Manifest V 2, Schema 2 "
|
||||
description: "image manifest for the Registry."
|
||||
keywords: ["registry, on-prem, images, tags, repository, distribution, api, advanced, manifest"]
|
||||
---
|
||||
|
||||
# Image Manifest Version 2, Schema 2
|
||||
|
||||
This document outlines the format of the V2 image manifest, schema version 2.
|
||||
The original (and provisional) image manifest for V2 (schema 1), was introduced
|
||||
in the Docker daemon in the [v1.3.0
|
||||
release](https://github.com/docker/docker/commit/9f482a66ab37ec396ac61ed0c00d59122ac07453)
|
||||
and is specified in the [schema 1 manifest definition](manifest-v2-1.md)
|
||||
|
||||
This second schema version has two primary goals. The first is to allow
|
||||
multi-architecture images, through a "fat manifest" which references image
|
||||
manifests for platform-specific versions of an image. The second is to
|
||||
move the Docker engine towards content-addressable images, by supporting
|
||||
an image model where the image's configuration can be hashed to generate
|
||||
an ID for the image.
|
||||
|
||||
# Media Types
|
||||
|
||||
The following media types are used by the manifest formats described here, and
|
||||
the resources they reference:
|
||||
|
||||
- `application/vnd.docker.distribution.manifest.v1+json`: schema1 (existing manifest format)
|
||||
- `application/vnd.docker.distribution.manifest.v2+json`: New image manifest format (schemaVersion = 2)
|
||||
- `application/vnd.docker.distribution.manifest.list.v2+json`: Manifest list, aka "fat manifest"
|
||||
- `application/vnd.docker.container.image.v1+json`: Container config JSON
|
||||
- `application/vnd.docker.image.rootfs.diff.tar.gzip`: "Layer", as a gzipped tar
|
||||
- `application/vnd.docker.image.rootfs.foreign.diff.tar.gzip`: "Layer", as a gzipped tar that should never be pushed
|
||||
- `application/vnd.docker.plugin.v1+json`: Plugin config JSON
|
||||
|
||||
## Manifest List
|
||||
|
||||
The manifest list is the "fat manifest" which points to specific image manifests
|
||||
for one or more platforms. Its use is optional, and relatively few images will
|
||||
use one of these manifests. A client will distinguish a manifest list from an
|
||||
image manifest based on the Content-Type returned in the HTTP response.
|
||||
|
||||
## *Manifest List* Field Descriptions
|
||||
|
||||
- **`schemaVersion`** *int*
|
||||
|
||||
This field specifies the image manifest schema version as an integer. This
|
||||
schema uses the version `2`.
|
||||
|
||||
- **`mediaType`** *string*
|
||||
|
||||
The MIME type of the manifest list. This should be set to
|
||||
`application/vnd.docker.distribution.manifest.list.v2+json`.
|
||||
|
||||
- **`manifests`** *array*
|
||||
|
||||
The manifests field contains a list of manifests for specific platforms.
|
||||
|
||||
Fields of an object in the manifests list are:
|
||||
|
||||
- **`mediaType`** *string*
|
||||
|
||||
The MIME type of the referenced object. This will generally be
|
||||
`application/vnd.docker.image.manifest.v2+json`, but it could also
|
||||
be `application/vnd.docker.image.manifest.v1+json` if the manifest
|
||||
list references a legacy schema-1 manifest.
|
||||
|
||||
- **`size`** *int*
|
||||
|
||||
The size in bytes of the object. This field exists so that a client
|
||||
will have an expected size for the content before validating. If the
|
||||
length of the retrieved content does not match the specified length,
|
||||
the content should not be trusted.
|
||||
|
||||
- **`digest`** *string*
|
||||
|
||||
The digest of the content, as defined by the
|
||||
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
|
||||
|
||||
- **`platform`** *object*
|
||||
|
||||
The platform object describes the platform which the image in the
|
||||
manifest runs on. A full list of valid operating system and architecture
|
||||
values are listed in the [Go language documentation for `$GOOS` and
|
||||
`$GOARCH`](https://golang.org/doc/install/source#environment)
|
||||
|
||||
- **`architecture`** *string*
|
||||
|
||||
The architecture field specifies the CPU architecture, for example
|
||||
`amd64` or `ppc64le`.
|
||||
|
||||
- **`os`** *string*
|
||||
|
||||
The os field specifies the operating system, for example
|
||||
`linux` or `windows`.
|
||||
|
||||
- **`os.version`** *string*
|
||||
|
||||
The optional os.version field specifies the operating system version,
|
||||
for example `10.0.10586`.
|
||||
|
||||
- **`os.features`** *array*
|
||||
|
||||
The optional os.features field specifies an array of strings,
|
||||
each listing a required OS feature (for example on Windows
|
||||
`win32k`).
|
||||
|
||||
- **`variant`** *string*
|
||||
|
||||
The optional variant field specifies a variant of the CPU, for
|
||||
example `armv6l` to specify a particular CPU variant of the ARM CPU.
|
||||
|
||||
- **`features`** *array*
|
||||
|
||||
The optional features field specifies an array of strings, each
|
||||
listing a required CPU feature (for example `sse4` or `aes`).
|
||||
|
||||
## Example Manifest List
|
||||
|
||||
*Example showing a simple manifest list pointing to image manifests for two platforms:*
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
||||
"size": 7143,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||||
"platform": {
|
||||
"architecture": "ppc64le",
|
||||
"os": "linux",
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.manifest.v2+json",
|
||||
"size": 7682,
|
||||
"digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"features": [
|
||||
"sse4"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Image Manifest
|
||||
|
||||
The image manifest provides a configuration and a set of layers for a container
|
||||
image. It's the direct replacement for the schema-1 manifest.
|
||||
|
||||
## *Image Manifest* Field Descriptions
|
||||
|
||||
- **`schemaVersion`** *int*
|
||||
|
||||
This field specifies the image manifest schema version as an integer. This
|
||||
schema uses version `2`.
|
||||
|
||||
- **`mediaType`** *string*
|
||||
|
||||
The MIME type of the manifest. This should be set to
|
||||
`application/vnd.docker.distribution.manifest.v2+json`.
|
||||
|
||||
- **`config`** *object*
|
||||
|
||||
The config field references a configuration object for a container, by
|
||||
digest. This configuration item is a JSON blob that the runtime uses
|
||||
to set up the container. This new schema uses a tweaked version
|
||||
of this configuration to allow image content-addressability on the
|
||||
daemon side.
|
||||
|
||||
Fields of a config object are:
|
||||
|
||||
- **`mediaType`** *string*
|
||||
|
||||
The MIME type of the referenced object. This should generally be
|
||||
`application/vnd.docker.container.image.v1+json`.
|
||||
|
||||
- **`size`** *int*
|
||||
|
||||
The size in bytes of the object. This field exists so that a client
|
||||
will have an expected size for the content before validating. If the
|
||||
length of the retrieved content does not match the specified length,
|
||||
the content should not be trusted.
|
||||
|
||||
- **`digest`** *string*
|
||||
|
||||
The digest of the content, as defined by the
|
||||
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
|
||||
|
||||
- **`layers`** *array*
|
||||
|
||||
The layer list is ordered starting from the base image (opposite order of schema1).
|
||||
|
||||
Fields of an item in the layers list are:
|
||||
|
||||
- **`mediaType`** *string*
|
||||
|
||||
The MIME type of the referenced object. This should
|
||||
generally be `application/vnd.docker.image.rootfs.diff.tar.gzip`.
|
||||
Layers of type
|
||||
`application/vnd.docker.image.rootfs.foreign.diff.tar.gzip` may be
|
||||
pulled from a remote location but they should never be pushed.
|
||||
|
||||
- **`size`** *int*
|
||||
|
||||
The size in bytes of the object. This field exists so that a client
|
||||
will have an expected size for the content before validating. If the
|
||||
length of the retrieved content does not match the specified length,
|
||||
the content should not be trusted.
|
||||
|
||||
- **`digest`** *string*
|
||||
|
||||
The digest of the content, as defined by the
|
||||
[Registry V2 HTTP API Specificiation](api.md#digest-parameter).
|
||||
|
||||
- **`urls`** *array*
|
||||
|
||||
Provides a list of URLs from which the content may be fetched. Content
|
||||
should be verified against the `digest` and `size`. This field is
|
||||
optional and uncommon.
|
||||
|
||||
## Example Image Manifest
|
||||
|
||||
*Example showing an image manifest:*
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.docker.container.image.v1+json",
|
||||
"size": 7023,
|
||||
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 32654,
|
||||
"digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 16724,
|
||||
"digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"size": 73109,
|
||||
"digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Backward compatibility
|
||||
|
||||
The registry will continue to accept uploads of manifests in both the old and
|
||||
new formats.
|
||||
|
||||
When pushing images, clients which support the new manifest format should first
|
||||
construct a manifest in the new format. If uploading this manifest fails,
|
||||
presumably because the registry only supports the old format, the client may
|
||||
fall back to uploading a manifest in the old format.
|
||||
|
||||
When pulling images, clients indicate support for this new version of the
|
||||
manifest format by sending the
|
||||
`application/vnd.docker.distribution.manifest.v2+json` and
|
||||
`application/vnd.docker.distribution.manifest.list.v2+json` media types in an
|
||||
`Accept` header when making a request to the `manifests` endpoint. Updated
|
||||
clients should check the `Content-Type` header to see whether the manifest
|
||||
returned from the endpoint is in the old format, or is an image manifest or
|
||||
manifest list in the new format.
|
||||
|
||||
If the manifest being requested uses the new format, and the appropriate media
|
||||
type is not present in an `Accept` header, the registry will assume that the
|
||||
client cannot handle the manifest as-is, and rewrite it on the fly into the old
|
||||
format. If the object that would otherwise be returned is a manifest list, the
|
||||
registry will look up the appropriate manifest for the amd64 platform and
|
||||
linux OS, rewrite that manifest into the old format if necessary, and return
|
||||
the result to the client. If no suitable manifest is found in the manifest
|
||||
list, the registry will return a 404 error.
|
||||
|
||||
One of the challenges in rewriting manifests to the old format is that the old
|
||||
format involves an image configuration for each layer in the manifest, but the
|
||||
new format only provides one image configuration. To work around this, the
|
||||
registry will create synthetic image configurations for all layers except the
|
||||
top layer. These image configurations will not result in runnable images on
|
||||
their own, but only serve to fill in the parent chain in a compatible way.
|
||||
The IDs in these synthetic configurations will be derived from hashes of their
|
||||
respective blobs. The registry will create these configurations and their IDs
|
||||
using the same scheme as Docker 1.10 when it creates a legacy manifest to push
|
||||
to a registry which doesn't support the new format.
|
7
gateway/vendor/github.com/docker/distribution/docs/spec/menu.md
generated
vendored
7
gateway/vendor/github.com/docker/distribution/docs/spec/menu.md
generated
vendored
@ -1,7 +0,0 @@
|
||||
---
|
||||
title: "Reference"
|
||||
description: "Explains registry JSON objects"
|
||||
keywords: ["registry, service, images, repository, json"]
|
||||
type: "menu"
|
||||
identifier: "smn_registry_ref"
|
||||
---
|
37
gateway/vendor/github.com/docker/distribution/health/api/api.go
generated
vendored
37
gateway/vendor/github.com/docker/distribution/health/api/api.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/health"
|
||||
)
|
||||
|
||||
var (
|
||||
updater = health.NewStatusUpdater()
|
||||
)
|
||||
|
||||
// DownHandler registers a manual_http_status that always returns an Error
|
||||
func DownHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "POST" {
|
||||
updater.Update(errors.New("Manual Check"))
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
// UpHandler registers a manual_http_status that always returns nil
|
||||
func UpHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "POST" {
|
||||
updater.Update(nil)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
// init sets up the two endpoints to bring the service up and down
|
||||
func init() {
|
||||
health.Register("manual_http_status", updater)
|
||||
http.HandleFunc("/debug/health/down", DownHandler)
|
||||
http.HandleFunc("/debug/health/up", UpHandler)
|
||||
}
|
86
gateway/vendor/github.com/docker/distribution/health/api/api_test.go
generated
vendored
86
gateway/vendor/github.com/docker/distribution/health/api/api_test.go
generated
vendored
@ -1,86 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/health"
|
||||
)
|
||||
|
||||
// TestGETDownHandlerDoesNotChangeStatus ensures that calling the endpoint
|
||||
// /debug/health/down with METHOD GET returns a 404
|
||||
func TestGETDownHandlerDoesNotChangeStatus(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("GET", "https://fakeurl.com/debug/health/down", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create request.")
|
||||
}
|
||||
|
||||
DownHandler(recorder, req)
|
||||
|
||||
if recorder.Code != 404 {
|
||||
t.Errorf("Did not get a 404.")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGETUpHandlerDoesNotChangeStatus ensures that calling the endpoint
|
||||
// /debug/health/down with METHOD GET returns a 404
|
||||
func TestGETUpHandlerDoesNotChangeStatus(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("GET", "https://fakeurl.com/debug/health/up", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create request.")
|
||||
}
|
||||
|
||||
DownHandler(recorder, req)
|
||||
|
||||
if recorder.Code != 404 {
|
||||
t.Errorf("Did not get a 404.")
|
||||
}
|
||||
}
|
||||
|
||||
// TestPOSTDownHandlerChangeStatus ensures the endpoint /debug/health/down changes
|
||||
// the status code of the response to 503
|
||||
// This test is order dependent, and should come before TestPOSTUpHandlerChangeStatus
|
||||
func TestPOSTDownHandlerChangeStatus(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("POST", "https://fakeurl.com/debug/health/down", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create request.")
|
||||
}
|
||||
|
||||
DownHandler(recorder, req)
|
||||
|
||||
if recorder.Code != 200 {
|
||||
t.Errorf("Did not get a 200.")
|
||||
}
|
||||
|
||||
if len(health.CheckStatus()) != 1 {
|
||||
t.Errorf("DownHandler didn't add an error check.")
|
||||
}
|
||||
}
|
||||
|
||||
// TestPOSTUpHandlerChangeStatus ensures the endpoint /debug/health/up changes
|
||||
// the status code of the response to 200
|
||||
func TestPOSTUpHandlerChangeStatus(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("POST", "https://fakeurl.com/debug/health/up", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create request.")
|
||||
}
|
||||
|
||||
UpHandler(recorder, req)
|
||||
|
||||
if recorder.Code != 200 {
|
||||
t.Errorf("Did not get a 200.")
|
||||
}
|
||||
|
||||
if len(health.CheckStatus()) != 0 {
|
||||
t.Errorf("UpHandler didn't remove the error check.")
|
||||
}
|
||||
}
|
73
gateway/vendor/github.com/docker/distribution/health/checks/checks.go
generated
vendored
73
gateway/vendor/github.com/docker/distribution/health/checks/checks.go
generated
vendored
@ -1,73 +0,0 @@
|
||||
package checks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/health"
|
||||
)
|
||||
|
||||
// FileChecker checks the existence of a file and returns an error
|
||||
// if the file exists.
|
||||
func FileChecker(f string) health.Checker {
|
||||
return health.CheckFunc(func() error {
|
||||
absoluteFilePath, err := filepath.Abs(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get absolute path for %q: %v", f, err)
|
||||
}
|
||||
|
||||
_, err = os.Stat(absoluteFilePath)
|
||||
if err == nil {
|
||||
return errors.New("file exists")
|
||||
} else if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// HTTPChecker does a HEAD request and verifies that the HTTP status code
|
||||
// returned matches statusCode.
|
||||
func HTTPChecker(r string, statusCode int, timeout time.Duration, headers http.Header) health.Checker {
|
||||
return health.CheckFunc(func() error {
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
req, err := http.NewRequest("HEAD", r, nil)
|
||||
if err != nil {
|
||||
return errors.New("error creating request: " + r)
|
||||
}
|
||||
for headerName, headerValues := range headers {
|
||||
for _, headerValue := range headerValues {
|
||||
req.Header.Add(headerName, headerValue)
|
||||
}
|
||||
}
|
||||
response, err := client.Do(req)
|
||||
if err != nil {
|
||||
return errors.New("error while checking: " + r)
|
||||
}
|
||||
if response.StatusCode != statusCode {
|
||||
return errors.New("downstream service returned unexpected status: " + strconv.Itoa(response.StatusCode))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// TCPChecker attempts to open a TCP connection.
|
||||
func TCPChecker(addr string, timeout time.Duration) health.Checker {
|
||||
return health.CheckFunc(func() error {
|
||||
conn, err := net.DialTimeout("tcp", addr, timeout)
|
||||
if err != nil {
|
||||
return errors.New("connection to " + addr + " failed")
|
||||
}
|
||||
conn.Close()
|
||||
return nil
|
||||
})
|
||||
}
|
25
gateway/vendor/github.com/docker/distribution/health/checks/checks_test.go
generated
vendored
25
gateway/vendor/github.com/docker/distribution/health/checks/checks_test.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
package checks
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileChecker(t *testing.T) {
|
||||
if err := FileChecker("/tmp").Check(); err == nil {
|
||||
t.Errorf("/tmp was expected as exists")
|
||||
}
|
||||
|
||||
if err := FileChecker("NoSuchFileFromMoon").Check(); err != nil {
|
||||
t.Errorf("NoSuchFileFromMoon was expected as not exists, error:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPChecker(t *testing.T) {
|
||||
if err := HTTPChecker("https://www.google.cybertron", 200, 0, nil).Check(); err == nil {
|
||||
t.Errorf("Google on Cybertron was expected as not exists")
|
||||
}
|
||||
|
||||
if err := HTTPChecker("https://www.google.pt", 200, 0, nil).Check(); err != nil {
|
||||
t.Errorf("Google at Portugal was expected as exists, error:%v", err)
|
||||
}
|
||||
}
|
136
gateway/vendor/github.com/docker/distribution/health/doc.go
generated
vendored
136
gateway/vendor/github.com/docker/distribution/health/doc.go
generated
vendored
@ -1,136 +0,0 @@
|
||||
// Package health provides a generic health checking framework.
|
||||
// The health package works expvar style. By importing the package the debug
|
||||
// server is getting a "/debug/health" endpoint that returns the current
|
||||
// status of the application.
|
||||
// If there are no errors, "/debug/health" will return an HTTP 200 status,
|
||||
// together with an empty JSON reply "{}". If there are any checks
|
||||
// with errors, the JSON reply will include all the failed checks, and the
|
||||
// response will be have an HTTP 503 status.
|
||||
//
|
||||
// A Check can either be run synchronously, or asynchronously. We recommend
|
||||
// that most checks are registered as an asynchronous check, so a call to the
|
||||
// "/debug/health" endpoint always returns immediately. This pattern is
|
||||
// particularly useful for checks that verify upstream connectivity or
|
||||
// database status, since they might take a long time to return/timeout.
|
||||
//
|
||||
// Installing
|
||||
//
|
||||
// To install health, just import it in your application:
|
||||
//
|
||||
// import "github.com/docker/distribution/health"
|
||||
//
|
||||
// You can also (optionally) import "health/api" that will add two convenience
|
||||
// endpoints: "/debug/health/down" and "/debug/health/up". These endpoints add
|
||||
// "manual" checks that allow the service to quickly be brought in/out of
|
||||
// rotation.
|
||||
//
|
||||
// import _ "github.com/docker/distribution/health/api"
|
||||
//
|
||||
// # curl localhost:5001/debug/health
|
||||
// {}
|
||||
// # curl -X POST localhost:5001/debug/health/down
|
||||
// # curl localhost:5001/debug/health
|
||||
// {"manual_http_status":"Manual Check"}
|
||||
//
|
||||
// After importing these packages to your main application, you can start
|
||||
// registering checks.
|
||||
//
|
||||
// Registering Checks
|
||||
//
|
||||
// The recommended way of registering checks is using a periodic Check.
|
||||
// PeriodicChecks run on a certain schedule and asynchronously update the
|
||||
// status of the check. This allows CheckStatus to return without blocking
|
||||
// on an expensive check.
|
||||
//
|
||||
// A trivial example of a check that runs every 5 seconds and shuts down our
|
||||
// server if the current minute is even, could be added as follows:
|
||||
//
|
||||
// func currentMinuteEvenCheck() error {
|
||||
// m := time.Now().Minute()
|
||||
// if m%2 == 0 {
|
||||
// return errors.New("Current minute is even!")
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// health.RegisterPeriodicFunc("minute_even", currentMinuteEvenCheck, time.Second*5)
|
||||
//
|
||||
// Alternatively, you can also make use of "RegisterPeriodicThresholdFunc" to
|
||||
// implement the exact same check, but add a threshold of failures after which
|
||||
// the check will be unhealthy. This is particularly useful for flaky Checks,
|
||||
// ensuring some stability of the service when handling them.
|
||||
//
|
||||
// health.RegisterPeriodicThresholdFunc("minute_even", currentMinuteEvenCheck, time.Second*5, 4)
|
||||
//
|
||||
// The lowest-level way to interact with the health package is calling
|
||||
// "Register" directly. Register allows you to pass in an arbitrary string and
|
||||
// something that implements "Checker" and runs your check. If your method
|
||||
// returns an error with nil, it is considered a healthy check, otherwise it
|
||||
// will make the health check endpoint "/debug/health" start returning a 503
|
||||
// and list the specific check that failed.
|
||||
//
|
||||
// Assuming you wish to register a method called "currentMinuteEvenCheck()
|
||||
// error" you could do that by doing:
|
||||
//
|
||||
// health.Register("even_minute", health.CheckFunc(currentMinuteEvenCheck))
|
||||
//
|
||||
// CheckFunc is a convenience type that implements Checker.
|
||||
//
|
||||
// Another way of registering a check could be by using an anonymous function
|
||||
// and the convenience method RegisterFunc. An example that makes the status
|
||||
// endpoint always return an error:
|
||||
//
|
||||
// health.RegisterFunc("my_check", func() error {
|
||||
// return Errors.new("This is an error!")
|
||||
// }))
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// You could also use the health checker mechanism to ensure your application
|
||||
// only comes up if certain conditions are met, or to allow the developer to
|
||||
// take the service out of rotation immediately. An example that checks
|
||||
// database connectivity and immediately takes the server out of rotation on
|
||||
// err:
|
||||
//
|
||||
// updater = health.NewStatusUpdater()
|
||||
// health.RegisterFunc("database_check", func() error {
|
||||
// return updater.Check()
|
||||
// }))
|
||||
//
|
||||
// conn, err := Connect(...) // database call here
|
||||
// if err != nil {
|
||||
// updater.Update(errors.New("Error connecting to the database: " + err.Error()))
|
||||
// }
|
||||
//
|
||||
// You can also use the predefined Checkers that come included with the health
|
||||
// package. First, import the checks:
|
||||
//
|
||||
// import "github.com/docker/distribution/health/checks
|
||||
//
|
||||
// After that you can make use of any of the provided checks. An example of
|
||||
// using a `FileChecker` to take the application out of rotation if a certain
|
||||
// file exists can be done as follows:
|
||||
//
|
||||
// health.Register("fileChecker", health.PeriodicChecker(checks.FileChecker("/tmp/disable"), time.Second*5))
|
||||
//
|
||||
// After registering the check, it is trivial to take an application out of
|
||||
// rotation from the console:
|
||||
//
|
||||
// # curl localhost:5001/debug/health
|
||||
// {}
|
||||
// # touch /tmp/disable
|
||||
// # curl localhost:5001/debug/health
|
||||
// {"fileChecker":"file exists"}
|
||||
//
|
||||
// FileChecker only accepts absolute or relative file path. It does not work
|
||||
// properly with tilde(~). You should make sure that the application has
|
||||
// proper permission(read and execute permission for directory along with
|
||||
// the specified file path). Otherwise, the FileChecker will report error
|
||||
// and file health check is not ok.
|
||||
//
|
||||
// You could also test the connectivity to a downstream service by using a
|
||||
// "HTTPChecker", but ensure that you only mark the test unhealthy if there
|
||||
// are a minimum of two failures in a row:
|
||||
//
|
||||
// health.Register("httpChecker", health.PeriodicThresholdChecker(checks.HTTPChecker("https://www.google.pt"), time.Second*5, 2))
|
||||
package health
|
306
gateway/vendor/github.com/docker/distribution/health/health.go
generated
vendored
306
gateway/vendor/github.com/docker/distribution/health/health.go
generated
vendored
@ -1,306 +0,0 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
// A Registry is a collection of checks. Most applications will use the global
|
||||
// registry defined in DefaultRegistry. However, unit tests may need to create
|
||||
// separate registries to isolate themselves from other tests.
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
registeredChecks map[string]Checker
|
||||
}
|
||||
|
||||
// NewRegistry creates a new registry. This isn't necessary for normal use of
|
||||
// the package, but may be useful for unit tests so individual tests have their
|
||||
// own set of checks.
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
registeredChecks: make(map[string]Checker),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRegistry is the default registry where checks are registered. It is
|
||||
// the registry used by the HTTP handler.
|
||||
var DefaultRegistry *Registry
|
||||
|
||||
// Checker is the interface for a Health Checker
|
||||
type Checker interface {
|
||||
// Check returns nil if the service is okay.
|
||||
Check() error
|
||||
}
|
||||
|
||||
// CheckFunc is a convenience type to create functions that implement
|
||||
// the Checker interface
|
||||
type CheckFunc func() error
|
||||
|
||||
// Check Implements the Checker interface to allow for any func() error method
|
||||
// to be passed as a Checker
|
||||
func (cf CheckFunc) Check() error {
|
||||
return cf()
|
||||
}
|
||||
|
||||
// Updater implements a health check that is explicitly set.
|
||||
type Updater interface {
|
||||
Checker
|
||||
|
||||
// Update updates the current status of the health check.
|
||||
Update(status error)
|
||||
}
|
||||
|
||||
// updater implements Checker and Updater, providing an asynchronous Update
|
||||
// method.
|
||||
// This allows us to have a Checker that returns the Check() call immediately
|
||||
// not blocking on a potentially expensive check.
|
||||
type updater struct {
|
||||
mu sync.Mutex
|
||||
status error
|
||||
}
|
||||
|
||||
// Check implements the Checker interface
|
||||
func (u *updater) Check() error {
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
|
||||
return u.status
|
||||
}
|
||||
|
||||
// Update implements the Updater interface, allowing asynchronous access to
|
||||
// the status of a Checker.
|
||||
func (u *updater) Update(status error) {
|
||||
u.mu.Lock()
|
||||
defer u.mu.Unlock()
|
||||
|
||||
u.status = status
|
||||
}
|
||||
|
||||
// NewStatusUpdater returns a new updater
|
||||
func NewStatusUpdater() Updater {
|
||||
return &updater{}
|
||||
}
|
||||
|
||||
// thresholdUpdater implements Checker and Updater, providing an asynchronous Update
|
||||
// method.
|
||||
// This allows us to have a Checker that returns the Check() call immediately
|
||||
// not blocking on a potentially expensive check.
|
||||
type thresholdUpdater struct {
|
||||
mu sync.Mutex
|
||||
status error
|
||||
threshold int
|
||||
count int
|
||||
}
|
||||
|
||||
// Check implements the Checker interface
|
||||
func (tu *thresholdUpdater) Check() error {
|
||||
tu.mu.Lock()
|
||||
defer tu.mu.Unlock()
|
||||
|
||||
if tu.count >= tu.threshold {
|
||||
return tu.status
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// thresholdUpdater implements the Updater interface, allowing asynchronous
|
||||
// access to the status of a Checker.
|
||||
func (tu *thresholdUpdater) Update(status error) {
|
||||
tu.mu.Lock()
|
||||
defer tu.mu.Unlock()
|
||||
|
||||
if status == nil {
|
||||
tu.count = 0
|
||||
} else if tu.count < tu.threshold {
|
||||
tu.count++
|
||||
}
|
||||
|
||||
tu.status = status
|
||||
}
|
||||
|
||||
// NewThresholdStatusUpdater returns a new thresholdUpdater
|
||||
func NewThresholdStatusUpdater(t int) Updater {
|
||||
return &thresholdUpdater{threshold: t}
|
||||
}
|
||||
|
||||
// PeriodicChecker wraps an updater to provide a periodic checker
|
||||
func PeriodicChecker(check Checker, period time.Duration) Checker {
|
||||
u := NewStatusUpdater()
|
||||
go func() {
|
||||
t := time.NewTicker(period)
|
||||
for {
|
||||
<-t.C
|
||||
u.Update(check.Check())
|
||||
}
|
||||
}()
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
// PeriodicThresholdChecker wraps an updater to provide a periodic checker that
|
||||
// uses a threshold before it changes status
|
||||
func PeriodicThresholdChecker(check Checker, period time.Duration, threshold int) Checker {
|
||||
tu := NewThresholdStatusUpdater(threshold)
|
||||
go func() {
|
||||
t := time.NewTicker(period)
|
||||
for {
|
||||
<-t.C
|
||||
tu.Update(check.Check())
|
||||
}
|
||||
}()
|
||||
|
||||
return tu
|
||||
}
|
||||
|
||||
// CheckStatus returns a map with all the current health check errors
|
||||
func (registry *Registry) CheckStatus() map[string]string { // TODO(stevvooe) this needs a proper type
|
||||
registry.mu.RLock()
|
||||
defer registry.mu.RUnlock()
|
||||
statusKeys := make(map[string]string)
|
||||
for k, v := range registry.registeredChecks {
|
||||
err := v.Check()
|
||||
if err != nil {
|
||||
statusKeys[k] = err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return statusKeys
|
||||
}
|
||||
|
||||
// CheckStatus returns a map with all the current health check errors from the
|
||||
// default registry.
|
||||
func CheckStatus() map[string]string {
|
||||
return DefaultRegistry.CheckStatus()
|
||||
}
|
||||
|
||||
// Register associates the checker with the provided name.
|
||||
func (registry *Registry) Register(name string, check Checker) {
|
||||
if registry == nil {
|
||||
registry = DefaultRegistry
|
||||
}
|
||||
registry.mu.Lock()
|
||||
defer registry.mu.Unlock()
|
||||
_, ok := registry.registeredChecks[name]
|
||||
if ok {
|
||||
panic("Check already exists: " + name)
|
||||
}
|
||||
registry.registeredChecks[name] = check
|
||||
}
|
||||
|
||||
// Register associates the checker with the provided name in the default
|
||||
// registry.
|
||||
func Register(name string, check Checker) {
|
||||
DefaultRegistry.Register(name, check)
|
||||
}
|
||||
|
||||
// RegisterFunc allows the convenience of registering a checker directly from
|
||||
// an arbitrary func() error.
|
||||
func (registry *Registry) RegisterFunc(name string, check func() error) {
|
||||
registry.Register(name, CheckFunc(check))
|
||||
}
|
||||
|
||||
// RegisterFunc allows the convenience of registering a checker in the default
|
||||
// registry directly from an arbitrary func() error.
|
||||
func RegisterFunc(name string, check func() error) {
|
||||
DefaultRegistry.RegisterFunc(name, check)
|
||||
}
|
||||
|
||||
// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
|
||||
// from an arbitrary func() error.
|
||||
func (registry *Registry) RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
|
||||
registry.Register(name, PeriodicChecker(CheckFunc(check), period))
|
||||
}
|
||||
|
||||
// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
|
||||
// in the default registry from an arbitrary func() error.
|
||||
func RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
|
||||
DefaultRegistry.RegisterPeriodicFunc(name, period, check)
|
||||
}
|
||||
|
||||
// RegisterPeriodicThresholdFunc allows the convenience of registering a
|
||||
// PeriodicChecker from an arbitrary func() error.
|
||||
func (registry *Registry) RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
|
||||
registry.Register(name, PeriodicThresholdChecker(CheckFunc(check), period, threshold))
|
||||
}
|
||||
|
||||
// RegisterPeriodicThresholdFunc allows the convenience of registering a
|
||||
// PeriodicChecker in the default registry from an arbitrary func() error.
|
||||
func RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
|
||||
DefaultRegistry.RegisterPeriodicThresholdFunc(name, period, threshold, check)
|
||||
}
|
||||
|
||||
// StatusHandler returns a JSON blob with all the currently registered Health Checks
|
||||
// and their corresponding status.
|
||||
// Returns 503 if any Error status exists, 200 otherwise
|
||||
func StatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "GET" {
|
||||
checks := CheckStatus()
|
||||
status := http.StatusOK
|
||||
|
||||
// If there is an error, return 503
|
||||
if len(checks) != 0 {
|
||||
status = http.StatusServiceUnavailable
|
||||
}
|
||||
|
||||
statusResponse(w, r, status, checks)
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// Handler returns a handler that will return 503 response code if the health
|
||||
// checks have failed. If everything is okay with the health checks, the
|
||||
// handler will pass through to the provided handler. Use this handler to
|
||||
// disable a web application when the health checks fail.
|
||||
func Handler(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
checks := CheckStatus()
|
||||
if len(checks) != 0 {
|
||||
errcode.ServeJSON(w, errcode.ErrorCodeUnavailable.
|
||||
WithDetail("health check failed: please see /debug/health"))
|
||||
return
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r) // pass through
|
||||
})
|
||||
}
|
||||
|
||||
// statusResponse completes the request with a response describing the health
|
||||
// of the service.
|
||||
func statusResponse(w http.ResponseWriter, r *http.Request, status int, checks map[string]string) {
|
||||
p, err := json.Marshal(checks)
|
||||
if err != nil {
|
||||
context.GetLogger(context.Background()).Errorf("error serializing health status: %v", err)
|
||||
p, err = json.Marshal(struct {
|
||||
ServerError string `json:"server_error"`
|
||||
}{
|
||||
ServerError: "Could not parse error message",
|
||||
})
|
||||
status = http.StatusInternalServerError
|
||||
|
||||
if err != nil {
|
||||
context.GetLogger(context.Background()).Errorf("error serializing health status failure message: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("Content-Length", fmt.Sprint(len(p)))
|
||||
w.WriteHeader(status)
|
||||
if _, err := w.Write(p); err != nil {
|
||||
context.GetLogger(context.Background()).Errorf("error writing health status response body: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Registers global /debug/health api endpoint, creates default registry
|
||||
func init() {
|
||||
DefaultRegistry = NewRegistry()
|
||||
http.HandleFunc("/debug/health", StatusHandler)
|
||||
}
|
107
gateway/vendor/github.com/docker/distribution/health/health_test.go
generated
vendored
107
gateway/vendor/github.com/docker/distribution/health/health_test.go
generated
vendored
@ -1,107 +0,0 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestReturns200IfThereAreNoChecks ensures that the result code of the health
|
||||
// endpoint is 200 if there are not currently registered checks.
|
||||
func TestReturns200IfThereAreNoChecks(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("GET", "https://fakeurl.com/debug/health", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create request.")
|
||||
}
|
||||
|
||||
StatusHandler(recorder, req)
|
||||
|
||||
if recorder.Code != 200 {
|
||||
t.Errorf("Did not get a 200.")
|
||||
}
|
||||
}
|
||||
|
||||
// TestReturns503IfThereAreErrorChecks ensures that the result code of the
|
||||
// health endpoint is 503 if there are health checks with errors.
|
||||
func TestReturns503IfThereAreErrorChecks(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
req, err := http.NewRequest("GET", "https://fakeurl.com/debug/health", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create request.")
|
||||
}
|
||||
|
||||
// Create a manual error
|
||||
Register("some_check", CheckFunc(func() error {
|
||||
return errors.New("This Check did not succeed")
|
||||
}))
|
||||
|
||||
StatusHandler(recorder, req)
|
||||
|
||||
if recorder.Code != 503 {
|
||||
t.Errorf("Did not get a 503.")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHealthHandler ensures that our handler implementation correct protects
|
||||
// the web application when things aren't so healthy.
|
||||
func TestHealthHandler(t *testing.T) {
|
||||
// clear out existing checks.
|
||||
DefaultRegistry = NewRegistry()
|
||||
|
||||
// protect an http server
|
||||
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
|
||||
// wrap it in our health handler
|
||||
handler = Handler(handler)
|
||||
|
||||
// use this swap check status
|
||||
updater := NewStatusUpdater()
|
||||
Register("test_check", updater)
|
||||
|
||||
// now, create a test server
|
||||
server := httptest.NewServer(handler)
|
||||
|
||||
checkUp := func(t *testing.T, message string) {
|
||||
resp, err := http.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting success status: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
t.Fatalf("unexpected response code from server when %s: %d != %d", message, resp.StatusCode, http.StatusNoContent)
|
||||
}
|
||||
// NOTE(stevvooe): we really don't care about the body -- the format is
|
||||
// not standardized or supported, yet.
|
||||
}
|
||||
|
||||
checkDown := func(t *testing.T, message string) {
|
||||
resp, err := http.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting down status: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusServiceUnavailable {
|
||||
t.Fatalf("unexpected response code from server when %s: %d != %d", message, resp.StatusCode, http.StatusServiceUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
// server should be up
|
||||
checkUp(t, "initial health check")
|
||||
|
||||
// now, we fail the health check
|
||||
updater.Update(fmt.Errorf("the server is now out of commission"))
|
||||
checkDown(t, "server should be down") // should be down
|
||||
|
||||
// bring server back up
|
||||
updater.Update(nil)
|
||||
checkUp(t, "when server is back up") // now we should be back up.
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user