mirror of
https://github.com/openfaas/faas.git
synced 2025-06-20 13:06:40 +00:00
Merge master into breakout_swarm
Signed-off-by: Alex Ellis <alexellis2@gmail.com>
This commit is contained in:
contrib
docker-compose.armhf.ymldocker-compose.ymlgateway
DockerfileGopkg.lockGopkg.toml
handlers
alerthandler.gocreate_handler.godeletehandler.goproxy.goqueueproxy.goreader.goservice_query.goupdate_handler.go
metrics
server.gotests
vendor.confvendor
github.com
Azure
go-ansiterm
Microsoft
go-winio
Nvveen
Sirupsen
logrus
beorn7
perks
docker
cli
LICENSENOTICEREADME.md
opts
env.gohosts.gohosts_unix.gohosts_windows.goip.gomount.goopts.goopts_unix.goopts_windows.goport.goquotedstring.goruntime.gosecret.gothrottledevice.goulimit.goweightdevice.go
vendor.confdistribution
.gitignore.mailmapAUTHORSBUILDING.mdCHANGELOG.mdCONTRIBUTING.mdDockerfilecoverpkg.sh
Godeps
MAINTAINERSMakefileREADME.mdRELEASE-CHECKLIST.mdROADMAP.mdblobs.gocircle.ymlcmd
digest
registry-api-descriptor-template
registry
configuration
context
contrib
apache
compose
docker-integration
DockerfileREADME.mddocker-compose.ymlgolem.confhelpers.bashinstall_certs.sh
malevolent-certs
malevolent.batsnginx
Dockerfiledocker-registry-v2.confnginx.confregistry-basic.confregistry-noauth.confregistry.conftest.passwd
run_multiversion.shtls.batstoken.batsv1
tokenserver-oauth
.htpasswdDockerfile
certs
auth.localregistry.certauth.localregistry.keylocalregistry.certlocalregistry.keysigning.certsigning.key
registry-config-notls.ymlregistry-config.ymltokenserver
token-server
digest
digest.godigest_test.godigester.godigester_resumable_test.godoc.goset.goset_test.goverifiers.goverifiers_test.go
docs
errors.gohealth
manifest
doc.go
manifests.gomanifestlist
schema1
config_builder.goconfig_builder_test.gomanifest.gomanifest_test.goreference_builder.goreference_builder_test.gosign.goverify.go
schema2
versioned.gonotifications
bridge.gobridge_test.goendpoint.goevent.goevent_test.gohttp.gohttp_test.golistener.golistener_test.gometrics.gosinks.gosinks_test.go
project
reference
registry.goregistry
api
auth
client
doc.gohandlers
api_test.goapp.goapp_test.gobasicauth.gobasicauth_prego14.goblob.goblobupload.gocatalog.gocontext.gohealth_test.gohelpers.gohmac.gohmac_test.gohooks.goimages.gomail.gotags.go
listener
middleware
proxy
proxyauth.goproxyblobstore.goproxyblobstore_test.goproxymanifeststore.goproxymanifeststore_test.goproxymetrics.goproxyregistry.goproxytagservice.goproxytagservice_test.go
registry.goregistry_test.goroot.goscheduler
storage
blob_test.goblobcachemetrics.goblobserver.goblobstore.goblobwriter.goblobwriter_nonresumable.goblobwriter_resumable.gofilereader.gofilereader_test.gogarbagecollect.gogarbagecollect_test.goio.golinkedblobstore.golinkedblobstore_test.gomanifestlisthandler.gomanifeststore.gomanifeststore_test.gopaths.gopaths_test.gopurgeuploads.gopurgeuploads_test.goregistry.goschema2manifesthandler.goschema2manifesthandler_test.gosignedmanifesthandler.gotagstore.gotagstore_test.goutil.govacuum.gowalk.gowalk_test.go
cache
catalog.gocatalog_test.godoc.godriver
azure
base
factory
fileinfo.gofilesystem
gcs
inmemory
middleware
oss
s3-aws
s3-goamz
storagedriver.goswift
testdriver
testsuites
testutil
uuid
vendor.confversion
docker
LICENSENOTICEREADME.md
api
README.mdcommon.gocommon_unix.gocommon_windows.gonames.go
types
auth.go
blkiodev
client.goconfigs.gocontainer
config.gocontainer_changes.gocontainer_create.gocontainer_top.gocontainer_update.gocontainer_wait.gohost_config.gohostconfig_unix.gohostconfig_windows.go
error_response.goevents
filters
graph_driver_data.goid_response.goimage
image_delete_response_item.goimage_summary.gomount
network
plugin.goplugin_device.goplugin_env.goplugin_interface_type.goplugin_mount.goplugin_responses.goport.goregistry
seccomp.goservice_update_response.gostats.gostrslice
swarm
time
types.goversions
volume.govolume
client
README.mdcheckpoint_create.gocheckpoint_delete.gocheckpoint_list.goclient.goclient_unix.goclient_windows.gocontainer_attach.gocontainer_commit.gocontainer_copy.gocontainer_create.gocontainer_diff.gocontainer_exec.gocontainer_export.gocontainer_inspect.gocontainer_kill.gocontainer_list.gocontainer_logs.gocontainer_pause.gocontainer_prune.gocontainer_remove.gocontainer_rename.gocontainer_resize.gocontainer_restart.gocontainer_start.gocontainer_stats.gocontainer_stop.gocontainer_top.gocontainer_unpause.gocontainer_update.gocontainer_wait.godisk_usage.goerrors.goevents.gohijack.goimage_build.goimage_create.goimage_history.goimage_import.goimage_inspect.goimage_list.goimage_load.goimage_prune.goimage_pull.goimage_push.goimage_remove.goimage_save.goimage_search.goimage_tag.goinfo.gointerface.gointerface_experimental.gointerface_stable.gologin.gonetwork_connect.gonetwork_create.gonetwork_disconnect.gonetwork_inspect.gonetwork_list.gonetwork_prune.gonetwork_remove.gonode_inspect.gonode_list.gonode_remove.gonode_update.goping.goplugin_create.goplugin_disable.goplugin_enable.goplugin_inspect.goplugin_install.goplugin_list.goplugin_push.goplugin_remove.goplugin_set.goplugin_upgrade.gorequest.gosecret_create.gosecret_inspect.gosecret_list.gosecret_remove.gosecret_update.goservice_create.goservice_inspect.goservice_list.goservice_logs.goservice_remove.goservice_update.goswarm_get_unlock_key.goswarm_init.goswarm_inspect.goswarm_join.goswarm_leave.goswarm_unlock.goswarm_update.gotask_inspect.gotask_list.gotask_logs.gotransport.goutils.goversion.govolume_create.govolume_inspect.govolume_list.govolume_prune.govolume_remove.go
opts
env.gohosts.gohosts_unix.gohosts_windows.goip.gomount.goopts.goopts_unix.goopts_windows.goport.goquotedstring.goruntime.gosecret.gothrottledevice.goulimit.goweightdevice.go
pkg
README.md
httputils
ioutils
buffer.gobytespipe.gofmt.gofswriters.gomultireader.goreaders.gotemp_unix.gotemp_windows.gowriteflusher.gowriters.go
jsonlog
jsonmessage
longpath
random
stringid
system
chtimes.gochtimes_unix.gochtimes_windows.goerrors.goevents_windows.goexitcode.gofilesys.gofilesys_windows.golstat_unix.golstat_windows.gomeminfo.gomeminfo_linux.gomeminfo_solaris.gomeminfo_unsupported.gomeminfo_windows.gomknod.gomknod_windows.gopath_unix.gopath_windows.goprocess_unix.gostat_darwin.gostat_freebsd.gostat_linux.gostat_openbsd.gostat_solaris.gostat_unix.gostat_windows.gosyscall_unix.gosyscall_windows.goumask.goumask_windows.goutimes_freebsd.goutimes_linux.goutimes_unsupported.goxattrs_linux.goxattrs_unsupported.go
tarsum
term
ascii.gotc_linux_cgo.gotc_other.gotc_solaris_cgo.goterm.goterm_solaris.goterm_unix.goterm_windows.gotermios_darwin.gotermios_freebsd.gotermios_linux.gotermios_openbsd.go
windows
tlsconfig
registry
auth.goconfig.goconfig_unix.goconfig_windows.goendpoint_v1.goregistry.goservice.goservice_v1.goservice_v2.gosession.gotypes.go
vendor.confgo-connections
go-units
libtrust
gogo
protobuf
.gitignore.mailmap.travis.ymlAUTHORSCONTRIBUTORSGOLANG_CONTRIBUTORSMakefileREADMEReadme.md
_conformance
bench.mdcodec
custom_types.mdextensions.mdgogoproto
gogoreplace
install-protobuf.shio
jsonpb
plugin
compare
defaultcheck
description
embedcheck
enumstringer
equal
face
gostring
marshalto
oneofcheck
populate
size
stringer
testgen
union
unmarshal
proto
Makefileall_test.goany_test.goclone_test.godecode_test.goencode.goencode_test.goequal_test.goextensions_test.golib.gomap_test.gomessage_set_test.goproperties.go
proto3_proto
proto3_test.gosize2_test.gosize_test.gotestdata
text.gotext_parser.gotext_parser_test.gotext_test.goprotobuf
protoc-gen-combo
protoc-gen-gofast
protoc-gen-gogo
protoc-gen-gogofast
protoc-gen-gogofaster
protoc-gen-gogoslick
protoc-gen-gogotypes
protoc-gen-gostring
protoc-min-version
sortkeys
test
.gitignoreMakefile
asymetric-issue125
bug_test.gocasttype
castvalue
combos
both
marshaler
unmarshaler
custom-dash-type
custom
custombytesnonstruct
dashfilename
data
defaultconflict
embedconflict
empty-issue70
enumcustomname
enumdecl
enumdecl_all
enumprefix
enumstringer
example
extension_test.gofiledotname
fuzztests
group
importdedup
indeximport-issue72
int64support
issue260
issue261
issue262
issue312
issue322
issue330
issue34
issue42order
issue8
jsonpb-gogo
mapdefaults
mapsproto2
Makefiledoc.goheader.protomapsproto2.protomapsproto2_test.go.in
combos
both
marshaler
neither
unmarshaler
mixbench
.gitignoremarshal.txtmarshaler.txtmixbench.gooldmarshaler.txtoldunmarshaler.txtunmarshal.txtunmarshaler.txtunsafe_marshaler.txtunsafe_unmarshaler.txt
moredefaults
nopackage
oneof
oneof3
oneofembed
packed
proto3extension
protosize
registration
required
sizerconflict
sizeunderscore
stdtypes
t.gotags
theproto3
Makefiledoc.gofooter.protoheader.protomaps.protoproto3_test.go.intheproto3.proto
thetest.pb.gothetest.protothetestpb_test.gocombos
both
marshaler
neither
unmarshaler
typedecl
typedecl_all
typedeclimport
types
unmarshalmerge
unrecognized
unrecognizedgroup
uuid.gouuid_test.gotypes
Makefileany.goany.pb.goany_test.godoc.goduration.goduration.pb.goduration_gogo.goduration_test.goempty.pb.gofield_mask.pb.gostruct.pb.gotimestamp.gotimestamp.pb.gotimestamp_gogo.gotimestamp_test.gowrappers.pb.go
vanity
version
golang
protobuf
.gitignore.travis.ymlAUTHORSCONTRIBUTORSMake.protobufMakefileREADME.md
_conformance
descriptor
jsonpb
proto
Makefileall_test.goany_test.goclone_test.godecode_test.goencode_test.goequal_test.goextensions_test.gomap_test.gomessage_set_test.go
proto3_proto
proto3_test.gosize2_test.gosize_test.gotestdata
text_parser_test.gotext_test.goprotoc-gen-go
ptypes
gorilla
context
mux
matttproud
golang_protobuf_extensions
nats-io
go-nats-streaming
go-nats
.gitignore.travis.ymlLICENSETODO.mdexample_test.go
bench
enc_test.goencoders
protobuf
examples
nats.gonats_test.goscripts
staticcheck.ignoretest
auth_test.gobasic_test.gobench_test.gocluster_test.go
timer_test.goconfigs
conn_test.gocontext_test.goenc_test.gogob_test.gohelper_test.gojson_test.gomain.gonetchan_test.goprotobuf_test.goreconnect_test.gosub_test.goutil
nuid
opencontainers
openfaas
nats-queue-worker
pkg
prometheus
client_golang
.gitignore.travis.ymlAUTHORS.mdCHANGELOG.mdCONTRIBUTING.mdREADME.mdVERSION
api
prometheus
examples
prometheus
.gitignorebenchmark_test.gocounter.gocounter_test.godesc.godoc.goexample_clustermanager_test.goexamples_test.goexpvar_collector_test.gogauge.gogauge_test.gogo_collector.gogo_collector_test.gohistogram.gohistogram_test.gohttp.gohttp_test.gometric_test.goobserver.goprocess_collector.goprocess_collector_test.go
promhttp
push
registry.goregistry_test.gosummary.gosummary_test.gotimer.gountyped.govalue.govec.govec_test.goclient_model
.gitignoreCONTRIBUTING.mdMAINTAINERS.mdMakefile
cpp
metrics.protopom.xmlpython
prometheus
ruby
setup.pysrc
main
java
io
prometheus
client
common
.travis.ymlCONTRIBUTING.mdMAINTAINERS.md
config
config.gohttp_config.gohttp_config_test.go
testdata
http.conf.bearer-token-and-file-set.bad.ymlhttp.conf.empty.bad.ymlhttp.conf.good.ymlhttp.conf.invalid-bearer-token-file.bad.ymltls_config.cert_no_key.bad.ymltls_config.empty.good.ymltls_config.insecure.good.ymltls_config.invalid_field.bad.ymltls_config.key_no_cert.bad.yml
tls_config_test.goexpfmt
bench_test.godecode_test.go
fuzz
corpus
from_test_parse_0from_test_parse_1from_test_parse_2from_test_parse_3from_test_parse_4from_test_parse_error_0from_test_parse_error_1from_test_parse_error_10from_test_parse_error_11from_test_parse_error_12from_test_parse_error_13from_test_parse_error_14from_test_parse_error_15from_test_parse_error_16from_test_parse_error_17from_test_parse_error_18from_test_parse_error_19from_test_parse_error_2from_test_parse_error_3from_test_parse_error_4from_test_parse_error_5from_test_parse_error_6from_test_parse_error_7from_test_parse_error_8from_test_parse_error_9minimal
testdata
text_create_test.gotext_parse_test.gointernal
bitbucket.org
ww
goautoneg
log
model
alert_test.golabels_test.gometric_test.gosignature_test.gosilence_test.gotime.gotime_test.govalue_test.go
promlog
route
version
procfs
.travis.ymlCONTRIBUTING.mdMAINTAINERS.mdMakefile
bcache
buddyinfo.gobuddyinfo_test.gofixtures
fs_test.goipvs.goipvs_test.gomdstat_test.gomountstats_test.gonet_dev.gonet_dev_test.goproc_io.goproc_io_test.goproc_limits.goproc_limits_test.goproc_ns.goproc_ns_test.goproc_stat_test.goproc_test.gostat_test.gosysfs
ttarxfrm_test.goxfs
spf13
golang.org
x
net
sys
LICENSEPATENTSREADME
unix
README.mdasm_darwin_386.sasm_darwin_amd64.sasm_darwin_arm.sasm_darwin_arm64.sasm_dragonfly_amd64.sasm_freebsd_386.sasm_freebsd_amd64.sasm_freebsd_arm.sasm_linux_386.sasm_linux_amd64.sasm_linux_arm.sasm_linux_arm64.sasm_linux_mips64x.sasm_linux_mipsx.sasm_linux_ppc64x.sasm_linux_s390x.sasm_netbsd_386.sasm_netbsd_amd64.sasm_netbsd_arm.sasm_openbsd_386.sasm_openbsd_amd64.sasm_solaris_amd64.sbluetooth_linux.goconstants.godirent.goendian_big.goendian_little.goenv_unix.goenv_unset.goflock.goflock_linux_32bit.gogccgo.gogccgo_c.cgccgo_linux_amd64.gogccgo_linux_sparc64.goopenbsd_pledge.gorace.gorace0.gosockcmsg_linux.gosockcmsg_unix.gostr.gosyscall.gosyscall_bsd.gosyscall_darwin.gosyscall_darwin_386.gosyscall_darwin_amd64.gosyscall_darwin_arm.gosyscall_darwin_arm64.gosyscall_dragonfly.gosyscall_dragonfly_amd64.gosyscall_freebsd.gosyscall_freebsd_386.gosyscall_freebsd_amd64.gosyscall_freebsd_arm.gosyscall_linux.gosyscall_linux_386.gosyscall_linux_amd64.gosyscall_linux_amd64_gc.gosyscall_linux_arm.gosyscall_linux_arm64.gosyscall_linux_mips64x.gosyscall_linux_mipsx.gosyscall_linux_ppc64x.gosyscall_linux_s390x.gosyscall_linux_sparc64.gosyscall_netbsd.gosyscall_netbsd_386.gosyscall_netbsd_amd64.gosyscall_netbsd_arm.gosyscall_no_getwd.gosyscall_openbsd.gosyscall_openbsd_386.gosyscall_openbsd_amd64.gosyscall_solaris.gosyscall_solaris_amd64.gosyscall_unix.gosyscall_unix_gc.gozerrors_darwin_386.gozerrors_darwin_amd64.gozerrors_darwin_arm.gozerrors_darwin_arm64.gozerrors_dragonfly_amd64.gozerrors_freebsd_386.gozerrors_freebsd_amd64.gozerrors_freebsd_arm.gozerrors_linux_386.gozerrors_linux_amd64.gozerrors_linux_arm.gozerrors_linux_arm64.gozerrors_linux_mips.gozerrors_linux_mips64.gozerrors_linux_mips64le.gozerrors_linux_mipsle.gozerrors_linux_ppc64.gozerrors_linux_ppc64le.gozerrors_linux_s390x.gozerrors_linux_sparc64.gozerrors_netbsd_386.gozerrors_netbsd_amd64.gozerrors_netbsd_arm.gozerrors_openbsd_386.gozerrors_openbsd_amd64.gozerrors_solaris_amd64.gozsyscall_darwin_386.gozsyscall_darwin_amd64.gozsyscall_darwin_arm.gozsyscall_darwin_arm64.gozsyscall_dragonfly_amd64.gozsyscall_freebsd_386.gozsyscall_freebsd_amd64.gozsyscall_freebsd_arm.gozsyscall_linux_386.gozsyscall_linux_amd64.gozsyscall_linux_arm.gozsyscall_linux_arm64.gozsyscall_linux_mips.gozsyscall_linux_mips64.gozsyscall_linux_mips64le.gozsyscall_linux_mipsle.gozsyscall_linux_ppc64.gozsyscall_linux_ppc64le.gozsyscall_linux_s390x.gozsyscall_linux_sparc64.gozsyscall_netbsd_386.gozsyscall_netbsd_amd64.gozsyscall_netbsd_arm.gozsyscall_openbsd_386.gozsyscall_openbsd_amd64.gozsyscall_solaris_amd64.gozsysctl_openbsd.gozsysnum_darwin_386.gozsysnum_darwin_amd64.gozsysnum_darwin_arm.gozsysnum_darwin_arm64.gozsysnum_dragonfly_amd64.gozsysnum_freebsd_386.gozsysnum_freebsd_amd64.gozsysnum_freebsd_arm.gozsysnum_linux_386.gozsysnum_linux_amd64.gozsysnum_linux_arm.gozsysnum_linux_arm64.gozsysnum_linux_mips.gozsysnum_linux_mips64.gozsysnum_linux_mips64le.gozsysnum_linux_mipsle.gozsysnum_linux_ppc64.gozsysnum_linux_ppc64le.gozsysnum_linux_s390x.gozsysnum_linux_sparc64.gozsysnum_netbsd_386.gozsysnum_netbsd_amd64.gozsysnum_netbsd_arm.gozsysnum_openbsd_386.gozsysnum_openbsd_amd64.gozsysnum_solaris_amd64.goztypes_darwin_386.goztypes_darwin_amd64.goztypes_darwin_arm.goztypes_darwin_arm64.goztypes_dragonfly_amd64.goztypes_freebsd_386.goztypes_freebsd_amd64.goztypes_freebsd_arm.goztypes_linux_386.goztypes_linux_amd64.goztypes_linux_arm.goztypes_linux_arm64.goztypes_linux_mips.goztypes_linux_mips64.goztypes_linux_mips64le.goztypes_linux_mipsle.goztypes_linux_ppc64.goztypes_linux_ppc64le.goztypes_linux_s390x.goztypes_linux_sparc64.goztypes_netbsd_386.goztypes_netbsd_amd64.goztypes_netbsd_arm.goztypes_openbsd_386.goztypes_openbsd_amd64.goztypes_solaris_amd64.go
windows
prometheus
37
gateway/vendor/github.com/docker/distribution/.gitignore
generated
vendored
Normal file
37
gateway/vendor/github.com/docker/distribution/.gitignore
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# 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
|
19
gateway/vendor/github.com/docker/distribution/.mailmap
generated
vendored
Normal file
19
gateway/vendor/github.com/docker/distribution/.mailmap
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
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>
|
||||
Misty Stanley-Jones <misty@docker.com> Misty Stanley-Jones <misty@apache.org>
|
182
gateway/vendor/github.com/docker/distribution/AUTHORS
generated
vendored
Normal file
182
gateway/vendor/github.com/docker/distribution/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
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>
|
||||
Antonio Mercado <amercado@thinknode.com>
|
||||
Antonio Murdaca <runcom@redhat.com>
|
||||
Anton Tiurin <noxiouz@yandex.ru>
|
||||
Anusha Ragunathan <anusha@docker.com>
|
||||
a-palchikov <deemok@gmail.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>
|
||||
davidli <wenquan.li@hp.com>
|
||||
David Verhasselt <david@crowdway.com>
|
||||
David Xia <dxia@spotify.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>
|
||||
Hua Wang <wanghua.humble@gmail.com>
|
||||
Hu Keping <hukeping@huawei.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>
|
||||
Jonathan Boulle <jonathanboulle@gmail.com>
|
||||
Jon Johnson <jonjohnson@google.com>
|
||||
Jon Poler <jonathan.poler@apcera.com>
|
||||
Jordan Liggitt <jliggitt@redhat.com>
|
||||
Josh Chorlton <josh.chorlton@docker.com>
|
||||
Josh Hawn <josh.hawn@docker.com>
|
||||
Julien Fernandez <julien.fernandez@gmail.com>
|
||||
Keerthan Mala <kmala@engineyard.com>
|
||||
Kelsey Hightower <kelsey.hightower@gmail.com>
|
||||
Kenneth Lim <kennethlimcp@gmail.com>
|
||||
Kenny Leung <kleung@google.com>
|
||||
Ke Xu <leonhartx.k@gmail.com>
|
||||
liuchang0812 <liuchang0812@gmail.com>
|
||||
Liu Hua <sdu.liu@huawei.com>
|
||||
Li Yi <denverdino@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>
|
||||
Matthew Green <greenmr@live.co.uk>
|
||||
Matt Moore <mattmoor@google.com>
|
||||
Matt Robenolt <matt@ydekproductions.com>
|
||||
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@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>
|
||||
Victoria Bialas <victoria.bialas@docker.com>
|
||||
Victor Vieux <vieux@docker.com>
|
||||
Vincent Batts <vbatts@redhat.com>
|
||||
Vincent Demeester <vincent@sbr.pm>
|
||||
Vincent Giersch <vincent.giersch@ovh.net>
|
||||
weiyuan.yl <weiyuan.yl@alibaba-inc.com>
|
||||
W. Trevor King <wking@tremily.us>
|
||||
xg.song <xg.song@venusource.com>
|
||||
xiekeyang <xiekeyang@huawei.com>
|
||||
Yann ROBERT <yann.robert@anantaplex.fr>
|
||||
yaoyao.xyy <yaoyao.xyy@alibaba-inc.com>
|
||||
yixi zhang <yixi@memsql.com>
|
||||
yuexiao-wang <wang.yuexiao@zte.com.cn>
|
||||
yuzou <zouyu7@huawei.com>
|
||||
zhouhaibing089 <zhouhaibing089@gmail.com>
|
||||
姜继忠 <jizhong.jiangjz@alibaba-inc.com>
|
119
gateway/vendor/github.com/docker/distribution/BUILDING.md
generated
vendored
Normal file
119
gateway/vendor/github.com/docker/distribution/BUILDING.md
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
|
||||
# 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/tools/godep github.com/golang/lint/golint
|
||||
|
||||
**TODO(stevvooe):** Add a `make setup` command to Makefile to run this. Have to think about how to interact with Godeps properly.
|
||||
|
||||
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 vendored
|
||||
Godeps 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`.
|
114
gateway/vendor/github.com/docker/distribution/CHANGELOG.md
generated
vendored
Normal file
114
gateway/vendor/github.com/docker/distribution/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
# Changelog
|
||||
|
||||
## 2.6.1 (2017-04-05)
|
||||
|
||||
#### Registry
|
||||
- Fix `Forwarded` header handling, revert use of `X-Forwarded-Port`
|
||||
- Use driver `Stat` for registry health check
|
||||
|
||||
## 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
Normal file
140
gateway/vendor/github.com/docker/distribution/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
# 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
Normal file
18
gateway/vendor/github.com/docker/distribution/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
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"]
|
458
gateway/vendor/github.com/docker/distribution/Godeps/Godeps.json
generated
vendored
Normal file
458
gateway/vendor/github.com/docker/distribution/Godeps/Godeps.json
generated
vendored
Normal file
@ -0,0 +1,458 @@
|
||||
{
|
||||
"ImportPath": "github.com/docker/distribution",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v74",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/Azure/azure-sdk-for-go/storage",
|
||||
"Comment": "v5.0.0-beta-6-g0b5fe2a",
|
||||
"Rev": "0b5fe2abe0271ba07049eacaa65922d67c319543"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Sirupsen/logrus",
|
||||
"Comment": "v0.7.3",
|
||||
"Rev": "55eb11d21d2a31a3cc93838241d04800f52e823d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Sirupsen/logrus/formatters/logstash",
|
||||
"Comment": "v0.7.3",
|
||||
"Rev": "55eb11d21d2a31a3cc93838241d04800f52e823d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/signer/v4",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restxml",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudfront/sign",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/service/s3",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/vendor/github.com/go-ini/ini",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/vendor/github.com/jmespath/go-jmespath",
|
||||
"Comment": "v1.2.4",
|
||||
"Rev": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/bugsnag-go",
|
||||
"Comment": "v1.0.2-5-gb1d1530",
|
||||
"Rev": "b1d153021fcd90ca3f080db36bec96dc690fb274"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/bugsnag-go/errors",
|
||||
"Comment": "v1.0.2-5-gb1d1530",
|
||||
"Rev": "b1d153021fcd90ca3f080db36bec96dc690fb274"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/osext",
|
||||
"Rev": "0dd3f918b21bec95ace9dc86c7e70266cfc5c702"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/bugsnag/panicwrap",
|
||||
"Comment": "1.0.0-2-ge2c2850",
|
||||
"Rev": "e2c28503fcd0675329da73bf48b33404db873782"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/denverdino/aliyungo/common",
|
||||
"Rev": "afedced274aa9a7fcdd47ac97018f0f8db4e5de2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/denverdino/aliyungo/oss",
|
||||
"Rev": "afedced274aa9a7fcdd47ac97018f0f8db4e5de2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/denverdino/aliyungo/util",
|
||||
"Rev": "afedced274aa9a7fcdd47ac97018f0f8db4e5de2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/goamz/aws",
|
||||
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/goamz/s3",
|
||||
"Rev": "f0a21f5b2e12f83a505ecf79b633bb2035cf6f85"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/libtrust",
|
||||
"Rev": "fa567046d9b14f6aa788882a950d69651d230b21"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/garyburd/redigo/redis",
|
||||
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "8d92cf5fc15a4382f8964b08e1f42a75c0591aa3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/handlers",
|
||||
"Rev": "60c7bfde3e33c201519a200a4507a158cc03a17b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/mux",
|
||||
"Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/inconshreveable/mousetrap",
|
||||
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||
"Rev": "482a9fd5fa83e8c4e7817413b80f3eb8feec03ef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ncw/swift",
|
||||
"Rev": "ce444d6d47c51d4dda9202cd38f5094dd8e27e86"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ncw/swift/swifttest",
|
||||
"Rev": "ce444d6d47c51d4dda9202cd38f5094dd8e27e86"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/cobra",
|
||||
"Rev": "312092086bed4968099259622145a0c9ae280064"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/pflag",
|
||||
"Rev": "5644820622454e71517561946e3d94b9f9db6842"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stevvooe/resumable",
|
||||
"Rev": "51ad44105773cafcbe91927f70ac68e1bf78f8b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stevvooe/resumable/sha256",
|
||||
"Rev": "51ad44105773cafcbe91927f70ac68e1bf78f8b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stevvooe/resumable/sha512",
|
||||
"Rev": "51ad44105773cafcbe91927f70ac68e1bf78f8b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/yvasiyarov/go-metrics",
|
||||
"Rev": "57bccd1ccd43f94bb17fdd8bf3007059b802f85e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/yvasiyarov/gorelic",
|
||||
"Comment": "v0.0.6-8-ga9bba5b",
|
||||
"Rev": "a9bba5b9ab508a086f9a12b8c51fab68478e2128"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/yvasiyarov/newrelic_platform_go",
|
||||
"Rev": "b21fdbd4370f3717f3bbd2bf41c223bc273068e6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/ocsp",
|
||||
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/google",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/internal",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jws",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/oauth2/jwt",
|
||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/time/rate",
|
||||
"Rev": "a4bde12657593d5e90d0533a3e4fd95e635124cb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/gensupport",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi/internal/uritemplates",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/storage/v1",
|
||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/app_identity",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/base",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/datastore",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/log",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/modules",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/appengine/internal/remote_api",
|
||||
"Rev": "12d5545dc1cfa6047a286d5e853841b6471f4c19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/compute/metadata",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/internal/opts",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/cloud/storage",
|
||||
"Rev": "975617b05ea8a58727e6c1a06b6161ff4185a9f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/codes",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/credentials",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/grpclog",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/internal",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/metadata",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/naming",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/peer",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/grpc/transport",
|
||||
"Rev": "d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/check.v1",
|
||||
"Rev": "64131543e7896d5bcc6bd5a76287eb75ea96c673"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/yaml.v2",
|
||||
"Rev": "bef53efd0c76e49e6de55ead051f886bea7e9420"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json",
|
||||
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||
}
|
||||
]
|
||||
}
|
5
gateway/vendor/github.com/docker/distribution/Godeps/Readme
generated
vendored
Normal file
5
gateway/vendor/github.com/docker/distribution/Godeps/Readme
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
58
gateway/vendor/github.com/docker/distribution/MAINTAINERS
generated
vendored
Normal file
58
gateway/vendor/github.com/docker/distribution/MAINTAINERS
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
# 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"
|
109
gateway/vendor/github.com/docker/distribution/Makefile
generated
vendored
Normal file
109
gateway/vendor/github.com/docker/distribution/Makefile
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
# 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 '')
|
||||
GODEP=$(shell which godep || 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-save:
|
||||
@echo "+ $@"
|
||||
$(if $(GODEP), , \
|
||||
$(error Please install godep: go get github.com/tools/godep))
|
||||
@$(GODEP) save $(PKGS)
|
||||
|
||||
dep-restore:
|
||||
@echo "+ $@"
|
||||
$(if $(GODEP), , \
|
||||
$(error Please install godep: go get github.com/tools/godep))
|
||||
@$(GODEP) restore -v
|
||||
|
||||
dep-validate: dep-restore
|
||||
@echo "+ $@"
|
||||
@rm -Rf .vendor.bak
|
||||
@mv vendor .vendor.bak
|
||||
@rm -Rf Godeps
|
||||
@$(GODEP) save ./...
|
||||
@test -z "$$(diff -r vendor .vendor.bak 2>&1 | tee /dev/stderr)" || \
|
||||
(echo >&2 "+ borked dependencies! what you have in Godeps/Godeps.json does not match with what you have in vendor" && false)
|
||||
@rm -Rf .vendor.bak
|
3
gateway/vendor/github.com/docker/distribution/README.md
generated
vendored
3
gateway/vendor/github.com/docker/distribution/README.md
generated
vendored
@ -76,7 +76,8 @@ may be the better choice.
|
||||
For those who have previously deployed their own registry based on the Registry
|
||||
1.0 implementation and wish to deploy a Registry 2.0 while retaining images,
|
||||
data migration is required. A tool to assist with migration efforts has been
|
||||
created. For more information see [docker/migrator](https://github.com/docker/migrator).
|
||||
created. For more information see [docker/migrator]
|
||||
(https://github.com/docker/migrator).
|
||||
|
||||
## Contribute
|
||||
|
||||
|
36
gateway/vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md
generated
vendored
Normal file
36
gateway/vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
## Registry Release Checklist
|
||||
|
||||
10. Compile release notes detailing features and since the last release. Update the `CHANGELOG.md` file.
|
||||
|
||||
20. Update the version file: `https://github.com/docker/distribution/blob/master/version/version.go`
|
||||
|
||||
30. Update the `MAINTAINERS` (if necessary), `AUTHORS` and `.mailmap` files.
|
||||
|
||||
```
|
||||
make AUTHORS
|
||||
```
|
||||
|
||||
40. Create a signed tag.
|
||||
|
||||
Distribution uses semantic versioning. Tags are of the format `vx.y.z[-rcn]`
|
||||
You will need PGP installed and a PGP key which has been added to your Github account. The comment for the tag should include the release notes.
|
||||
|
||||
50. Push the signed tag
|
||||
|
||||
60. Create a new [release](https://github.com/docker/distribution/releases). In the case of a release candidate, tick the `pre-release` checkbox.
|
||||
|
||||
70. Update the registry binary in [distribution library image repo](https://github.com/docker/distribution-library-image) by running the update script and opening a pull request.
|
||||
|
||||
80. Update the official image. Add the new version in the [official images repo](https://github.com/docker-library/official-images) by appending a new version to the `registry/registry` file with the git hash pointed to by the signed tag. Update the major version to point to the latest version and the minor version to point to new patch release if necessary.
|
||||
e.g. to release `2.3.1`
|
||||
|
||||
`2.3.1 (new)`
|
||||
|
||||
`2.3.0 -> 2.3.0` can be removed
|
||||
|
||||
`2 -> 2.3.1`
|
||||
|
||||
`2.3 -> 2.3.1`
|
||||
|
||||
90. Build a new distribution/registry image on [Docker hub](https://hub.docker.com/u/distribution/dashboard) by adding a new automated build with the new tag and re-building the images.
|
||||
|
267
gateway/vendor/github.com/docker/distribution/ROADMAP.md
generated
vendored
Normal file
267
gateway/vendor/github.com/docker/distribution/ROADMAP.md
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
# 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.
|
||||
|
4
gateway/vendor/github.com/docker/distribution/blobs.go
generated
vendored
4
gateway/vendor/github.com/docker/distribution/blobs.go
generated
vendored
@ -8,8 +8,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/context"
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -152,7 +152,7 @@ type BlobProvider interface {
|
||||
|
||||
// BlobServer can serve blobs via http.
|
||||
type BlobServer interface {
|
||||
// ServeBlob attempts to serve the blob, identified by dgst, via http. The
|
||||
// ServeBlob attempts to serve the blob, identifed by dgst, via http. The
|
||||
// service may decide to redirect the client elsewhere or serve the data
|
||||
// directly.
|
||||
//
|
||||
|
93
gateway/vendor/github.com/docker/distribution/circle.yml
generated
vendored
Normal file
93
gateway/vendor/github.com/docker/distribution/circle.yml
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
# 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/tools/godep:
|
||||
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
|
||||
|
||||
# todo(richard): replace with a more robust vendoring solution. Removed due to a fundamental disagreement in godep philosophies.
|
||||
# Ensure validation of dependencies
|
||||
# - gvm use stable && if test -n "`git diff --stat=1000 master | grep -Ei \"vendor|godeps\"`"; 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 && godep 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={}; godep 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={}; godep 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
Normal file
97
gateway/vendor/github.com/docker/distribution/cmd/digest/main.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/docker/distribution/digest"
|
||||
"github.com/docker/distribution/version"
|
||||
)
|
||||
|
||||
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.Fprintf(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
Normal file
131
gateway/vendor/github.com/docker/distribution/cmd/registry-api-descriptor-template/main.go
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
// 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
Normal file
55
gateway/vendor/github.com/docker/distribution/cmd/registry/config-cache.yml
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
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
Normal file
66
gateway/vendor/github.com/docker/distribution/cmd/registry/config-dev.yml
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
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
Normal file
18
gateway/vendor/github.com/docker/distribution/cmd/registry/config-example.yml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
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
Normal file
25
gateway/vendor/github.com/docker/distribution/cmd/registry/main.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
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()
|
||||
}
|
643
gateway/vendor/github.com/docker/distribution/configuration/configuration.go
generated
vendored
Normal file
643
gateway/vendor/github.com/docker/distribution/configuration/configuration.go
generated
vendored
Normal file
@ -0,0 +1,643 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"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 wether 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.
|
||||
Enabled bool `yaml:"enabled,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 configurate 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, fmt.Errorf("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
Normal file
529
gateway/vendor/github.com/docker/distribution/configuration/configuration_test.go
generated
vendored
Normal file
@ -0,0 +1,529 @@
|
||||
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
Normal file
283
gateway/vendor/github.com/docker/distribution/configuration/parser.go
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
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
|
||||
}
|
2
gateway/vendor/github.com/docker/distribution/context/doc.go
generated
vendored
2
gateway/vendor/github.com/docker/distribution/context/doc.go
generated
vendored
@ -64,7 +64,7 @@
|
||||
// Note that this only affects the new context, the previous context, with the
|
||||
// version field, can be used independently. Put another way, the new logger,
|
||||
// added to the request context, is unique to that context and can have
|
||||
// request scoped variables.
|
||||
// request scoped varaibles.
|
||||
//
|
||||
// HTTP Requests
|
||||
//
|
||||
|
285
gateway/vendor/github.com/docker/distribution/context/http_test.go
generated
vendored
Normal file
285
gateway/vendor/github.com/docker/distribution/context/http_test.go
generated
vendored
Normal file
@ -0,0 +1,285 @@
|
||||
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
Normal file
85
gateway/vendor/github.com/docker/distribution/context/trace_test.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
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
Normal file
19
gateway/vendor/github.com/docker/distribution/context/version_test.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
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
Normal file
36
gateway/vendor/github.com/docker/distribution/contrib/apache/README.MD
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# 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
Normal file
127
gateway/vendor/github.com/docker/distribution/contrib/apache/apache.conf
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
#
|
||||
# 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
Normal file
147
gateway/vendor/github.com/docker/distribution/contrib/compose/README.md
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
# 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
Normal file
15
gateway/vendor/github.com/docker/distribution/contrib/compose/docker-compose.yml
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
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
Normal file
6
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
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
|
6
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/docker-registry-v2.conf
generated
vendored
Normal file
6
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/docker-registry-v2.conf
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
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;
|
7
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/docker-registry.conf
generated
vendored
Normal file
7
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/docker-registry.conf
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
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
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/nginx.conf
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
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
Normal file
41
gateway/vendor/github.com/docker/distribution/contrib/compose/nginx/registry.conf
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# 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
Normal file
9
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
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
Normal file
63
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/README.md
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
# 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 .`
|
||||
|
91
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/docker-compose.yml
generated
vendored
Normal file
91
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/docker-compose.yml
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
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"
|
||||
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
Normal file
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/golem.conf
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
[[suite]]
|
||||
dind=true
|
||||
images=[ "nginx:1.9", "dmcgowan/token-server:simple", "dmcgowan/token-server:oauth", "dmcgowan/malevolent:0.1.0" ]
|
||||
|
||||
[[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"
|
||||
|
101
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/helpers.bash
generated
vendored
Normal file
101
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/helpers.bash
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
# 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
|
||||
}
|
||||
|
||||
# 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
|
||||
run docker_t login -u $user -p $password -e $email $1
|
||||
if [ "$status" -ne 0 ]; then
|
||||
echo $output
|
||||
fi
|
||||
[ "$status" -eq 0 ]
|
||||
# First line is WARNING about credential save or email deprecation (maybe both)
|
||||
[ "${lines[2]}" = "Login Succeeded" -o "${lines[1]}" = "Login Succeeded" ]
|
||||
}
|
||||
|
||||
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 reates 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
|
||||
}
|
50
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/install_certs.sh
generated
vendored
Normal file
50
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/install_certs.sh
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
#!/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
|
||||
|
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/malevolent-certs/localregistry.cert
generated
vendored
Normal file
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/malevolent-certs/localregistry.cert
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
-----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-----
|
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/malevolent-certs/localregistry.key
generated
vendored
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/malevolent-certs/localregistry.key
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
-----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
Normal file
192
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/malevolent.bats
generated
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
#!/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 -f $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 -f $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 -f $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 ]
|
||||
}
|
10
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/Dockerfile
generated
vendored
Normal file
10
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
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
|
6
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/docker-registry-v2.conf
generated
vendored
Normal file
6
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/docker-registry-v2.conf
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
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;
|
61
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/nginx.conf
generated
vendored
Normal file
61
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/nginx.conf
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
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;
|
||||
}
|
||||
}
|
8
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/registry-basic.conf
generated
vendored
Normal file
8
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/registry-basic.conf
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
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;
|
||||
}
|
5
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/registry-noauth.conf
generated
vendored
Normal file
5
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/registry-noauth.conf
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
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
Normal file
260
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/registry.conf
generated
vendored
Normal file
@ -0,0 +1,260 @@
|
||||
# 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
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/test.passwd
generated
vendored
Normal file
1
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/test.passwd
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
testuser:$apr1$YmLhHjm6$AjP4z8J1WgcUNxU8J4ue5.
|
1
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/v1/search.json
generated
vendored
Normal file
1
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/nginx/v1/search.json
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"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}]}
|
64
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/run_multiversion.sh
generated
vendored
Executable file
64
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/run_multiversion.sh
generated
vendored
Executable file
@ -0,0 +1,64 @@
|
||||
#!/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 registry:0.9.1
|
||||
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
|
||||
|
||||
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" \
|
||||
$DIR
|
||||
|
109
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tls.bats
generated
vendored
Normal file
109
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tls.bats
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
#!/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"}
|
||||
email="distribution@docker.com"
|
||||
|
||||
function setup() {
|
||||
tempImage $image
|
||||
}
|
||||
|
||||
@test "Test valid certificates" {
|
||||
docker_t tag -f $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 -f $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 -f $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 -f $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 -f $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 -f $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 -f $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 -f $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 -f $image $hostname:5448/$image
|
||||
run docker_t push $hostname:5448/$image
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
135
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/token.bats
generated
vendored
Normal file
135
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/token.bats
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# This tests contacting a registry using a token server
|
||||
|
||||
load helpers
|
||||
|
||||
user="testuser"
|
||||
password="testpassword"
|
||||
email="a@nowhere.com"
|
||||
base="hello-world"
|
||||
|
||||
@test "Test token server login" {
|
||||
run docker_t login -u $user -p $password -e $email localregistry:5554
|
||||
echo $output
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# First line is WARNING about credential save or email deprecation
|
||||
[ "${lines[2]}" = "Login Succeeded" -o "${lines[1]}" = "Login Succeeded" ]
|
||||
}
|
||||
|
||||
@test "Test token server bad login" {
|
||||
run docker_t login -u "testuser" -p "badpassword" -e $email localregistry:5554
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
run docker_t login -u "baduser" -p "testpassword" -e $email 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"
|
||||
|
||||
run docker_t login -u "testuser" -p "badpassword" -e $email localregistry:5557
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
run 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
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/.htpasswd
generated
vendored
Normal file
1
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/.htpasswd
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
testuser:$2y$05$T2MlBvkN1R/yICNnLuf1leOlOfAY0DvybctbbWUFKlojfkShVgn4m
|
8
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/Dockerfile
generated
vendored
Normal file
8
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
FROM dmcgowan/token-server:oauth
|
||||
|
||||
WORKDIR /
|
||||
|
||||
COPY ./.htpasswd /.htpasswd
|
||||
COPY ./certs/auth.localregistry.cert /tls.cert
|
||||
COPY ./certs/auth.localregistry.key /tls.key
|
||||
COPY ./certs/signing.key /sign.key
|
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/auth.localregistry.cert
generated
vendored
Normal file
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/auth.localregistry.cert
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
-----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-----
|
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/auth.localregistry.key
generated
vendored
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/auth.localregistry.key
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/localregistry.cert
generated
vendored
Normal file
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/localregistry.cert
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
-----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-----
|
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/localregistry.key
generated
vendored
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/localregistry.key
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/signing.cert
generated
vendored
Normal file
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/signing.cert
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
-----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-----
|
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/signing.key
generated
vendored
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/certs/signing.key
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
15
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/registry-config-notls.yml
generated
vendored
Normal file
15
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/registry-config-notls.yml
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
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"
|
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/registry-config.yml
generated
vendored
Normal file
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver-oauth/registry-config.yml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
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
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/.htpasswd
generated
vendored
Normal file
1
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/.htpasswd
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
testuser:$2y$05$T2MlBvkN1R/yICNnLuf1leOlOfAY0DvybctbbWUFKlojfkShVgn4m
|
8
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/Dockerfile
generated
vendored
Normal file
8
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
FROM dmcgowan/token-server:simple
|
||||
|
||||
WORKDIR /
|
||||
|
||||
COPY ./.htpasswd /.htpasswd
|
||||
COPY ./certs/auth.localregistry.cert /tls.cert
|
||||
COPY ./certs/auth.localregistry.key /tls.key
|
||||
COPY ./certs/signing.key /sign.key
|
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/auth.localregistry.cert
generated
vendored
Normal file
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/auth.localregistry.cert
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
-----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-----
|
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/auth.localregistry.key
generated
vendored
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/auth.localregistry.key
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/localregistry.cert
generated
vendored
Normal file
19
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/localregistry.cert
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
-----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-----
|
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/localregistry.key
generated
vendored
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/localregistry.key
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/signing.cert
generated
vendored
Normal file
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/signing.cert
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
-----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-----
|
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/signing.key
generated
vendored
Normal file
27
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/certs/signing.key
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
-----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-----
|
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/registry-config.yml
generated
vendored
Normal file
18
gateway/vendor/github.com/docker/distribution/contrib/docker-integration/tokenserver/registry-config.yml
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
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
Normal file
38
gateway/vendor/github.com/docker/distribution/contrib/token-server/errors.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
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
Normal file
425
gateway/vendor/github.com/docker/distribution/contrib/token-server/main.go
generated
vendored
Normal file
@ -0,0 +1,425 @@
|
||||
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
Normal file
219
gateway/vendor/github.com/docker/distribution/contrib/token-server/token.go
generated
vendored
Normal file
@ -0,0 +1,219 @@
|
||||
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
Executable file
7
gateway/vendor/github.com/docker/distribution/coverpkg.sh
generated
vendored
Executable file
@ -0,0 +1,7 @@
|
||||
#!/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 ' ' ','
|
139
gateway/vendor/github.com/docker/distribution/digest/digest.go
generated
vendored
Normal file
139
gateway/vendor/github.com/docker/distribution/digest/digest.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// DigestSha256EmptyTar is the canonical sha256 digest of empty data
|
||||
DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
)
|
||||
|
||||
// Digest allows simple protection of hex formatted digest strings, prefixed
|
||||
// by their algorithm. Strings of type Digest have some guarantee of being in
|
||||
// the correct format and it provides quick access to the components of a
|
||||
// digest string.
|
||||
//
|
||||
// The following is an example of the contents of Digest types:
|
||||
//
|
||||
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
|
||||
//
|
||||
// This allows to abstract the digest behind this type and work only in those
|
||||
// terms.
|
||||
type Digest string
|
||||
|
||||
// NewDigest returns a Digest from alg and a hash.Hash object.
|
||||
func NewDigest(alg Algorithm, h hash.Hash) Digest {
|
||||
return NewDigestFromBytes(alg, h.Sum(nil))
|
||||
}
|
||||
|
||||
// NewDigestFromBytes returns a new digest from the byte contents of p.
|
||||
// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...)
|
||||
// functions. This is also useful for rebuilding digests from binary
|
||||
// serializations.
|
||||
func NewDigestFromBytes(alg Algorithm, p []byte) Digest {
|
||||
return Digest(fmt.Sprintf("%s:%x", alg, p))
|
||||
}
|
||||
|
||||
// NewDigestFromHex returns a Digest from alg and a the hex encoded digest.
|
||||
func NewDigestFromHex(alg, hex string) Digest {
|
||||
return Digest(fmt.Sprintf("%s:%s", alg, hex))
|
||||
}
|
||||
|
||||
// DigestRegexp matches valid digest types.
|
||||
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`)
|
||||
|
||||
// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
|
||||
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)
|
||||
|
||||
var (
|
||||
// ErrDigestInvalidFormat returned when digest format invalid.
|
||||
ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format")
|
||||
|
||||
// ErrDigestInvalidLength returned when digest has invalid length.
|
||||
ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length")
|
||||
|
||||
// ErrDigestUnsupported returned when the digest algorithm is unsupported.
|
||||
ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
|
||||
)
|
||||
|
||||
// ParseDigest parses s and returns the validated digest object. An error will
|
||||
// be returned if the format is invalid.
|
||||
func ParseDigest(s string) (Digest, error) {
|
||||
d := Digest(s)
|
||||
|
||||
return d, d.Validate()
|
||||
}
|
||||
|
||||
// FromReader returns the most valid digest for the underlying content using
|
||||
// the canonical digest algorithm.
|
||||
func FromReader(rd io.Reader) (Digest, error) {
|
||||
return Canonical.FromReader(rd)
|
||||
}
|
||||
|
||||
// FromBytes digests the input and returns a Digest.
|
||||
func FromBytes(p []byte) Digest {
|
||||
return Canonical.FromBytes(p)
|
||||
}
|
||||
|
||||
// Validate checks that the contents of d is a valid digest, returning an
|
||||
// error if not.
|
||||
func (d Digest) Validate() error {
|
||||
s := string(d)
|
||||
|
||||
if !DigestRegexpAnchored.MatchString(s) {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
i := strings.Index(s, ":")
|
||||
if i < 0 {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
// case: "sha256:" with no hex.
|
||||
if i+1 == len(s) {
|
||||
return ErrDigestInvalidFormat
|
||||
}
|
||||
|
||||
switch algorithm := Algorithm(s[:i]); algorithm {
|
||||
case SHA256, SHA384, SHA512:
|
||||
if algorithm.Size()*2 != len(s[i+1:]) {
|
||||
return ErrDigestInvalidLength
|
||||
}
|
||||
break
|
||||
default:
|
||||
return ErrDigestUnsupported
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Algorithm returns the algorithm portion of the digest. This will panic if
|
||||
// the underlying digest is not in a valid format.
|
||||
func (d Digest) Algorithm() Algorithm {
|
||||
return Algorithm(d[:d.sepIndex()])
|
||||
}
|
||||
|
||||
// Hex returns the hex digest portion of the digest. This will panic if the
|
||||
// underlying digest is not in a valid format.
|
||||
func (d Digest) Hex() string {
|
||||
return string(d[d.sepIndex()+1:])
|
||||
}
|
||||
|
||||
func (d Digest) String() string {
|
||||
return string(d)
|
||||
}
|
||||
|
||||
func (d Digest) sepIndex() int {
|
||||
i := strings.Index(string(d), ":")
|
||||
|
||||
if i < 0 {
|
||||
panic("could not find ':' in digest: " + d)
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
82
gateway/vendor/github.com/docker/distribution/digest/digest_test.go
generated
vendored
Normal file
82
gateway/vendor/github.com/docker/distribution/digest/digest_test.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseDigest(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
input string
|
||||
err error
|
||||
algorithm Algorithm
|
||||
hex string
|
||||
}{
|
||||
{
|
||||
input: "sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b",
|
||||
algorithm: "sha256",
|
||||
hex: "e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b",
|
||||
},
|
||||
{
|
||||
input: "sha384:d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d",
|
||||
algorithm: "sha384",
|
||||
hex: "d3fc7881460b7e22e3d172954463dddd7866d17597e7248453c48b3e9d26d9596bf9c4a9cf8072c9d5bad76e19af801d",
|
||||
},
|
||||
{
|
||||
// empty hex
|
||||
input: "sha256:",
|
||||
err: ErrDigestInvalidFormat,
|
||||
},
|
||||
{
|
||||
// just hex
|
||||
input: "d41d8cd98f00b204e9800998ecf8427e",
|
||||
err: ErrDigestInvalidFormat,
|
||||
},
|
||||
{
|
||||
// not hex
|
||||
input: "sha256:d41d8cd98f00b204e9800m98ecf8427e",
|
||||
err: ErrDigestInvalidFormat,
|
||||
},
|
||||
{
|
||||
// too short
|
||||
input: "sha256:abcdef0123456789",
|
||||
err: ErrDigestInvalidLength,
|
||||
},
|
||||
{
|
||||
// too short (from different algorithm)
|
||||
input: "sha512:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
|
||||
err: ErrDigestInvalidLength,
|
||||
},
|
||||
{
|
||||
input: "foo:d41d8cd98f00b204e9800998ecf8427e",
|
||||
err: ErrDigestUnsupported,
|
||||
},
|
||||
} {
|
||||
digest, err := ParseDigest(testcase.input)
|
||||
if err != testcase.err {
|
||||
t.Fatalf("error differed from expected while parsing %q: %v != %v", testcase.input, err, testcase.err)
|
||||
}
|
||||
|
||||
if testcase.err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if digest.Algorithm() != testcase.algorithm {
|
||||
t.Fatalf("incorrect algorithm for parsed digest: %q != %q", digest.Algorithm(), testcase.algorithm)
|
||||
}
|
||||
|
||||
if digest.Hex() != testcase.hex {
|
||||
t.Fatalf("incorrect hex for parsed digest: %q != %q", digest.Hex(), testcase.hex)
|
||||
}
|
||||
|
||||
// Parse string return value and check equality
|
||||
newParsed, err := ParseDigest(digest.String())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error parsing input %q: %v", testcase.input, err)
|
||||
}
|
||||
|
||||
if newParsed != digest {
|
||||
t.Fatalf("expected equal: %q != %q", newParsed, digest)
|
||||
}
|
||||
}
|
||||
}
|
155
gateway/vendor/github.com/docker/distribution/digest/digester.go
generated
vendored
Normal file
155
gateway/vendor/github.com/docker/distribution/digest/digester.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Algorithm identifies and implementation of a digester by an identifier.
|
||||
// Note the that this defines both the hash algorithm used and the string
|
||||
// encoding.
|
||||
type Algorithm string
|
||||
|
||||
// supported digest types
|
||||
const (
|
||||
SHA256 Algorithm = "sha256" // sha256 with hex encoding
|
||||
SHA384 Algorithm = "sha384" // sha384 with hex encoding
|
||||
SHA512 Algorithm = "sha512" // sha512 with hex encoding
|
||||
|
||||
// Canonical is the primary digest algorithm used with the distribution
|
||||
// project. Other digests may be used but this one is the primary storage
|
||||
// digest.
|
||||
Canonical = SHA256
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO(stevvooe): Follow the pattern of the standard crypto package for
|
||||
// registration of digests. Effectively, we are a registerable set and
|
||||
// common symbol access.
|
||||
|
||||
// algorithms maps values to hash.Hash implementations. Other algorithms
|
||||
// may be available but they cannot be calculated by the digest package.
|
||||
algorithms = map[Algorithm]crypto.Hash{
|
||||
SHA256: crypto.SHA256,
|
||||
SHA384: crypto.SHA384,
|
||||
SHA512: crypto.SHA512,
|
||||
}
|
||||
)
|
||||
|
||||
// Available returns true if the digest type is available for use. If this
|
||||
// returns false, New and Hash will return nil.
|
||||
func (a Algorithm) Available() bool {
|
||||
h, ok := algorithms[a]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// check availability of the hash, as well
|
||||
return h.Available()
|
||||
}
|
||||
|
||||
func (a Algorithm) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// Size returns number of bytes returned by the hash.
|
||||
func (a Algorithm) Size() int {
|
||||
h, ok := algorithms[a]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return h.Size()
|
||||
}
|
||||
|
||||
// Set implemented to allow use of Algorithm as a command line flag.
|
||||
func (a *Algorithm) Set(value string) error {
|
||||
if value == "" {
|
||||
*a = Canonical
|
||||
} else {
|
||||
// just do a type conversion, support is queried with Available.
|
||||
*a = Algorithm(value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new digester for the specified algorithm. If the algorithm
|
||||
// does not have a digester implementation, nil will be returned. This can be
|
||||
// checked by calling Available before calling New.
|
||||
func (a Algorithm) New() Digester {
|
||||
return &digester{
|
||||
alg: a,
|
||||
hash: a.Hash(),
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns a new hash as used by the algorithm. If not available, the
|
||||
// method will panic. Check Algorithm.Available() before calling.
|
||||
func (a Algorithm) Hash() hash.Hash {
|
||||
if !a.Available() {
|
||||
// NOTE(stevvooe): A missing hash is usually a programming error that
|
||||
// must be resolved at compile time. We don't import in the digest
|
||||
// package to allow users to choose their hash implementation (such as
|
||||
// when using stevvooe/resumable or a hardware accelerated package).
|
||||
//
|
||||
// Applications that may want to resolve the hash at runtime should
|
||||
// call Algorithm.Available before call Algorithm.Hash().
|
||||
panic(fmt.Sprintf("%v not available (make sure it is imported)", a))
|
||||
}
|
||||
|
||||
return algorithms[a].New()
|
||||
}
|
||||
|
||||
// FromReader returns the digest of the reader using the algorithm.
|
||||
func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
|
||||
digester := a.New()
|
||||
|
||||
if _, err := io.Copy(digester.Hash(), rd); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return digester.Digest(), nil
|
||||
}
|
||||
|
||||
// FromBytes digests the input and returns a Digest.
|
||||
func (a Algorithm) FromBytes(p []byte) Digest {
|
||||
digester := a.New()
|
||||
|
||||
if _, err := digester.Hash().Write(p); err != nil {
|
||||
// Writes to a Hash should never fail. None of the existing
|
||||
// hash implementations in the stdlib or hashes vendored
|
||||
// here can return errors from Write. Having a panic in this
|
||||
// condition instead of having FromBytes return an error value
|
||||
// avoids unnecessary error handling paths in all callers.
|
||||
panic("write to hash function returned error: " + err.Error())
|
||||
}
|
||||
|
||||
return digester.Digest()
|
||||
}
|
||||
|
||||
// TODO(stevvooe): Allow resolution of verifiers using the digest type and
|
||||
// this registration system.
|
||||
|
||||
// Digester calculates the digest of written data. Writes should go directly
|
||||
// to the return value of Hash, while calling Digest will return the current
|
||||
// value of the digest.
|
||||
type Digester interface {
|
||||
Hash() hash.Hash // provides direct access to underlying hash instance.
|
||||
Digest() Digest
|
||||
}
|
||||
|
||||
// digester provides a simple digester definition that embeds a hasher.
|
||||
type digester struct {
|
||||
alg Algorithm
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
func (d *digester) Hash() hash.Hash {
|
||||
return d.hash
|
||||
}
|
||||
|
||||
func (d *digester) Digest() Digest {
|
||||
return NewDigest(d.alg, d.hash)
|
||||
}
|
21
gateway/vendor/github.com/docker/distribution/digest/digester_resumable_test.go
generated
vendored
Normal file
21
gateway/vendor/github.com/docker/distribution/digest/digester_resumable_test.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// +build !noresumabledigest
|
||||
|
||||
package digest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stevvooe/resumable"
|
||||
_ "github.com/stevvooe/resumable/sha256"
|
||||
)
|
||||
|
||||
// TestResumableDetection just ensures that the resumable capability of a hash
|
||||
// is exposed through the digester type, which is just a hash plus a Digest
|
||||
// method.
|
||||
func TestResumableDetection(t *testing.T) {
|
||||
d := Canonical.New()
|
||||
|
||||
if _, ok := d.Hash().(resumable.Hash); !ok {
|
||||
t.Fatalf("expected digester to implement resumable.Hash: %#v, %v", d, d.Hash())
|
||||
}
|
||||
}
|
42
gateway/vendor/github.com/docker/distribution/digest/doc.go
generated
vendored
Normal file
42
gateway/vendor/github.com/docker/distribution/digest/doc.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Package digest provides a generalized type to opaquely represent message
|
||||
// digests and their operations within the registry. The Digest type is
|
||||
// designed to serve as a flexible identifier in a content-addressable system.
|
||||
// More importantly, it provides tools and wrappers to work with
|
||||
// hash.Hash-based digests with little effort.
|
||||
//
|
||||
// Basics
|
||||
//
|
||||
// The format of a digest is simply a string with two parts, dubbed the
|
||||
// "algorithm" and the "digest", separated by a colon:
|
||||
//
|
||||
// <algorithm>:<digest>
|
||||
//
|
||||
// An example of a sha256 digest representation follows:
|
||||
//
|
||||
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
|
||||
//
|
||||
// In this case, the string "sha256" is the algorithm and the hex bytes are
|
||||
// the "digest".
|
||||
//
|
||||
// Because the Digest type is simply a string, once a valid Digest is
|
||||
// obtained, comparisons are cheap, quick and simple to express with the
|
||||
// standard equality operator.
|
||||
//
|
||||
// Verification
|
||||
//
|
||||
// The main benefit of using the Digest type is simple verification against a
|
||||
// given digest. The Verifier interface, modeled after the stdlib hash.Hash
|
||||
// interface, provides a common write sink for digest verification. After
|
||||
// writing is complete, calling the Verifier.Verified method will indicate
|
||||
// whether or not the stream of bytes matches the target digest.
|
||||
//
|
||||
// Missing Features
|
||||
//
|
||||
// In addition to the above, we intend to add the following features to this
|
||||
// package:
|
||||
//
|
||||
// 1. A Digester type that supports write sink digest calculation.
|
||||
//
|
||||
// 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry.
|
||||
//
|
||||
package digest
|
30
gateway/vendor/github.com/docker/distribution/digestset/set.go → gateway/vendor/github.com/docker/distribution/digest/set.go
generated
vendored
30
gateway/vendor/github.com/docker/distribution/digestset/set.go → gateway/vendor/github.com/docker/distribution/digest/set.go
generated
vendored
@ -1,12 +1,10 @@
|
||||
package digestset
|
||||
package digest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -46,7 +44,7 @@ func NewSet() *Set {
|
||||
// values or short values. This function does not test equality,
|
||||
// rather whether the second value could match against the first
|
||||
// value.
|
||||
func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool {
|
||||
func checkShortMatch(alg Algorithm, hex, shortAlg, shortHex string) bool {
|
||||
if len(hex) == len(shortHex) {
|
||||
if hex != shortHex {
|
||||
return false
|
||||
@ -66,7 +64,7 @@ func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool
|
||||
// If no digests could be found ErrDigestNotFound will be returned
|
||||
// with an empty digest value. If multiple matches are found
|
||||
// ErrDigestAmbiguous will be returned with an empty digest value.
|
||||
func (dst *Set) Lookup(d string) (digest.Digest, error) {
|
||||
func (dst *Set) Lookup(d string) (Digest, error) {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
if len(dst.entries) == 0 {
|
||||
@ -74,11 +72,11 @@ func (dst *Set) Lookup(d string) (digest.Digest, error) {
|
||||
}
|
||||
var (
|
||||
searchFunc func(int) bool
|
||||
alg digest.Algorithm
|
||||
alg Algorithm
|
||||
hex string
|
||||
)
|
||||
dgst, err := digest.Parse(d)
|
||||
if err == digest.ErrDigestInvalidFormat {
|
||||
dgst, err := ParseDigest(d)
|
||||
if err == ErrDigestInvalidFormat {
|
||||
hex = d
|
||||
searchFunc = func(i int) bool {
|
||||
return dst.entries[i].val >= d
|
||||
@ -110,7 +108,7 @@ func (dst *Set) Lookup(d string) (digest.Digest, error) {
|
||||
// Add adds the given digest to the set. An error will be returned
|
||||
// if the given digest is invalid. If the digest already exists in the
|
||||
// set, this operation will be a no-op.
|
||||
func (dst *Set) Add(d digest.Digest) error {
|
||||
func (dst *Set) Add(d Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -141,7 +139,7 @@ func (dst *Set) Add(d digest.Digest) error {
|
||||
// Remove removes the given digest from the set. An err will be
|
||||
// returned if the given digest is invalid. If the digest does
|
||||
// not exist in the set, this operation will be a no-op.
|
||||
func (dst *Set) Remove(d digest.Digest) error {
|
||||
func (dst *Set) Remove(d Digest) error {
|
||||
if err := d.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -169,10 +167,10 @@ func (dst *Set) Remove(d digest.Digest) error {
|
||||
}
|
||||
|
||||
// All returns all the digests in the set
|
||||
func (dst *Set) All() []digest.Digest {
|
||||
func (dst *Set) All() []Digest {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
retValues := make([]digest.Digest, len(dst.entries))
|
||||
retValues := make([]Digest, len(dst.entries))
|
||||
for i := range dst.entries {
|
||||
retValues[i] = dst.entries[i].digest
|
||||
}
|
||||
@ -185,10 +183,10 @@ func (dst *Set) All() []digest.Digest {
|
||||
// entire value of digest if uniqueness cannot be achieved without the
|
||||
// full value. This function will attempt to make short codes as short
|
||||
// as possible to be unique.
|
||||
func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
|
||||
func ShortCodeTable(dst *Set, length int) map[Digest]string {
|
||||
dst.mutex.RLock()
|
||||
defer dst.mutex.RUnlock()
|
||||
m := make(map[digest.Digest]string, len(dst.entries))
|
||||
m := make(map[Digest]string, len(dst.entries))
|
||||
l := length
|
||||
resetIdx := 0
|
||||
for i := 0; i < len(dst.entries); i++ {
|
||||
@ -224,9 +222,9 @@ func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
|
||||
}
|
||||
|
||||
type digestEntry struct {
|
||||
alg digest.Algorithm
|
||||
alg Algorithm
|
||||
val string
|
||||
digest digest.Digest
|
||||
digest Digest
|
||||
}
|
||||
|
||||
type digestEntries []*digestEntry
|
368
gateway/vendor/github.com/docker/distribution/digest/set_test.go
generated
vendored
Normal file
368
gateway/vendor/github.com/docker/distribution/digest/set_test.go
generated
vendored
Normal file
@ -0,0 +1,368 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertEqualDigests(t *testing.T, d1, d2 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{
|
||||
"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{
|
||||
"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("sha256:1234511111111111111111111111111111111111111111111111111111111111")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(dset.entries) != 8 {
|
||||
t.Fatal("Duplicate digest insert allowed")
|
||||
}
|
||||
|
||||
if err := dset.Add(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]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{
|
||||
"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, error) {
|
||||
r := rand.New(rand.NewSource(25823))
|
||||
digests := make([]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] = 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)
|
||||
}
|
44
gateway/vendor/github.com/docker/distribution/digest/verifiers.go
generated
vendored
Normal file
44
gateway/vendor/github.com/docker/distribution/digest/verifiers.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Verifier presents a general verification interface to be used with message
|
||||
// digests and other byte stream verifications. Users instantiate a Verifier
|
||||
// from one of the various methods, write the data under test to it then check
|
||||
// the result with the Verified method.
|
||||
type Verifier interface {
|
||||
io.Writer
|
||||
|
||||
// Verified will return true if the content written to Verifier matches
|
||||
// the digest.
|
||||
Verified() bool
|
||||
}
|
||||
|
||||
// NewDigestVerifier returns a verifier that compares the written bytes
|
||||
// against a passed in digest.
|
||||
func NewDigestVerifier(d Digest) (Verifier, error) {
|
||||
if err := d.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hashVerifier{
|
||||
hash: d.Algorithm().Hash(),
|
||||
digest: d,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type hashVerifier struct {
|
||||
digest Digest
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
func (hv hashVerifier) Write(p []byte) (n int, err error) {
|
||||
return hv.hash.Write(p)
|
||||
}
|
||||
|
||||
func (hv hashVerifier) Verified() bool {
|
||||
return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash)
|
||||
}
|
49
gateway/vendor/github.com/docker/distribution/digest/verifiers_test.go
generated
vendored
Normal file
49
gateway/vendor/github.com/docker/distribution/digest/verifiers_test.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package digest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDigestVerifier(t *testing.T) {
|
||||
p := make([]byte, 1<<20)
|
||||
rand.Read(p)
|
||||
digest := FromBytes(p)
|
||||
|
||||
verifier, err := NewDigestVerifier(digest)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting digest verifier: %s", err)
|
||||
}
|
||||
|
||||
io.Copy(verifier, bytes.NewReader(p))
|
||||
|
||||
if !verifier.Verified() {
|
||||
t.Fatalf("bytes not verified")
|
||||
}
|
||||
}
|
||||
|
||||
// TestVerifierUnsupportedDigest ensures that unsupported digest validation is
|
||||
// flowing through verifier creation.
|
||||
func TestVerifierUnsupportedDigest(t *testing.T) {
|
||||
unsupported := Digest("bean:0123456789abcdef")
|
||||
|
||||
_, err := NewDigestVerifier(unsupported)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error when creating verifier")
|
||||
}
|
||||
|
||||
if err != ErrDigestUnsupported {
|
||||
t.Fatalf("incorrect error for unsupported digest: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(stevvooe): Add benchmarks to measure bytes/second throughput for
|
||||
// DigestVerifier.
|
||||
//
|
||||
// The relevant benchmark for comparison can be run with the following
|
||||
// commands:
|
||||
//
|
||||
// go test -bench . crypto/sha1
|
||||
//
|
16
gateway/vendor/github.com/docker/distribution/docs/README.md
generated
vendored
Normal file
16
gateway/vendor/github.com/docker/distribution/docs/README.md
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# 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!
|
52
gateway/vendor/github.com/docker/distribution/docs/architecture.md
generated
vendored
Normal file
52
gateway/vendor/github.com/docker/distribution/docs/architecture.md
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
published: false
|
||||
---
|
||||
|
||||
# Architecture
|
||||
|
||||
## Design
|
||||
**TODO(stevvooe):** Discuss the architecture of the registry, internally and externally, in a few different deployment scenarios.
|
||||
|
||||
### Eventual Consistency
|
||||
|
||||
> **NOTE:** This section belongs somewhere, perhaps in a design document. We
|
||||
> are leaving this here so the information is not lost.
|
||||
|
||||
Running the registry on eventually consistent backends has been part of the
|
||||
design from the beginning. This section covers some of the approaches to
|
||||
dealing with this reality.
|
||||
|
||||
There are a few classes of issues that we need to worry about when
|
||||
implementing something on top of the storage drivers:
|
||||
|
||||
1. Read-After-Write consistency (see this [article on
|
||||
s3](http://shlomoswidler.com/2009/12/read-after-write-consistency-in-amazon.html)).
|
||||
2. [Write-Write Conflicts](http://en.wikipedia.org/wiki/Write%E2%80%93write_conflict).
|
||||
|
||||
In reality, the registry must worry about these kinds of errors when doing the
|
||||
following:
|
||||
|
||||
1. Accepting data into a temporary upload file may not have latest data block
|
||||
yet (read-after-write).
|
||||
2. Moving uploaded data into its blob location (write-write race).
|
||||
3. Modifying the "current" manifest for given tag (write-write race).
|
||||
4. A whole slew of operations around deletes (read-after-write, delete-write
|
||||
races, garbage collection, etc.).
|
||||
|
||||
The backend path layout employs a few techniques to avoid these problems:
|
||||
|
||||
1. Large writes are done to private upload directories. This alleviates most
|
||||
of the corruption potential under multiple writers by avoiding multiple
|
||||
writers.
|
||||
2. Constraints in storage driver implementations, such as support for writing
|
||||
after the end of a file to extend it.
|
||||
3. Digest verification to avoid data corruption.
|
||||
4. Manifest files are stored by digest and cannot change.
|
||||
5. All other non-content files (links, hashes, etc.) are written as an atomic
|
||||
unit. Anything that requires additions and deletions is broken out into
|
||||
separate "files". Last writer still wins.
|
||||
|
||||
Unfortunately, one must play this game when trying to build something like
|
||||
this on top of eventually consistent storage systems. If we run into serious
|
||||
problems, we can wrap the storagedrivers in a shared consistency layer but
|
||||
that would increase complexity and hinder registry cluster performance.
|
1121
gateway/vendor/github.com/docker/distribution/docs/configuration.md
generated
vendored
Normal file
1121
gateway/vendor/github.com/docker/distribution/docs/configuration.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5485
gateway/vendor/github.com/docker/distribution/docs/spec/api.md
generated
vendored
Normal file
5485
gateway/vendor/github.com/docker/distribution/docs/spec/api.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1215
gateway/vendor/github.com/docker/distribution/docs/spec/api.md.tmpl
generated
vendored
Normal file
1215
gateway/vendor/github.com/docker/distribution/docs/spec/api.md.tmpl
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
gateway/vendor/github.com/docker/distribution/docs/spec/auth/index.md
generated
vendored
Normal file
12
gateway/vendor/github.com/docker/distribution/docs/spec/auth/index.md
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
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
Normal file
329
gateway/vendor/github.com/docker/distribution/docs/spec/auth/jwt.md
generated
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
---
|
||||
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
Normal file
190
gateway/vendor/github.com/docker/distribution/docs/spec/auth/oauth.md
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
---
|
||||
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
Normal file
148
gateway/vendor/github.com/docker/distribution/docs/spec/auth/scope.md
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
---
|
||||
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
Normal file
250
gateway/vendor/github.com/docker/distribution/docs/spec/auth/token.md
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
---
|
||||
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)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user