[Marathon] Bump go-marathon dep
attempt to remove glide from integration glide trim Revert "attempt to remove glide from integration" This reverts commit c5b42b6cdebb44e730080a0cf20a871c11ef095b.
This commit is contained in:
parent
bd6056c269
commit
4e238280bc
128
glide.lock
generated
128
glide.lock
generated
|
@ -1,5 +1,5 @@
|
||||||
hash: 0f1c54f36d88f4625c048686f733842d09d48525b1cf84dc05d4acc698cfd86f
|
hash: 4859757da2a6ffcb080f0d03b050754d7a8f42a38dec8e8b5a2b7ad98f9cb604
|
||||||
updated: 2017-04-11T17:02:21.540487905+02:00
|
updated: 2017-05-02T02:14:58.499276128Z
|
||||||
imports:
|
imports:
|
||||||
- name: cloud.google.com/go
|
- name: cloud.google.com/go
|
||||||
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
version: 2e6a95edb1071d750f6d7db777bf66cd2997af6c
|
||||||
|
@ -95,6 +95,8 @@ imports:
|
||||||
subpackages:
|
subpackages:
|
||||||
- Godeps/_workspace/src/github.com/coreos/go-systemd/journal
|
- Godeps/_workspace/src/github.com/coreos/go-systemd/journal
|
||||||
- Godeps/_workspace/src/github.com/coreos/pkg/capnslog
|
- Godeps/_workspace/src/github.com/coreos/pkg/capnslog
|
||||||
|
- Godeps/_workspace/src/github.com/ugorji/go/codec
|
||||||
|
- Godeps/_workspace/src/golang.org/x/net/context
|
||||||
- client
|
- client
|
||||||
- pkg/fileutil
|
- pkg/fileutil
|
||||||
- pkg/pathutil
|
- pkg/pathutil
|
||||||
|
@ -133,12 +135,70 @@ imports:
|
||||||
- name: github.com/docker/distribution
|
- name: github.com/docker/distribution
|
||||||
version: 325b0804fef3a66309d962357aac3c2ce3f4d329
|
version: 325b0804fef3a66309d962357aac3c2ce3f4d329
|
||||||
subpackages:
|
subpackages:
|
||||||
|
- context
|
||||||
- digest
|
- digest
|
||||||
- reference
|
- reference
|
||||||
|
- registry/api/errcode
|
||||||
|
- registry/api/v2
|
||||||
|
- registry/client
|
||||||
|
- registry/client/auth
|
||||||
|
- registry/client/auth/challenge
|
||||||
|
- registry/client/transport
|
||||||
|
- registry/storage/cache
|
||||||
|
- registry/storage/cache/memory
|
||||||
|
- uuid
|
||||||
- name: github.com/docker/docker
|
- name: github.com/docker/docker
|
||||||
version: 49bf474f9ed7ce7143a59d1964ff7b7fd9b52178
|
version: 4845c567eb35d68f35b0b1713a09b0c8d47fe67e
|
||||||
subpackages:
|
subpackages:
|
||||||
|
- api
|
||||||
|
- api/types
|
||||||
|
- api/types/blkiodev
|
||||||
|
- api/types/container
|
||||||
|
- api/types/events
|
||||||
|
- api/types/filters
|
||||||
|
- api/types/image
|
||||||
|
- api/types/mount
|
||||||
|
- api/types/network
|
||||||
|
- api/types/registry
|
||||||
|
- api/types/strslice
|
||||||
|
- api/types/swarm
|
||||||
|
- api/types/time
|
||||||
|
- api/types/versions
|
||||||
|
- api/types/volume
|
||||||
|
- builder/dockerignore
|
||||||
|
- cli/command/image/build
|
||||||
|
- cli/config
|
||||||
|
- cli/config/configfile
|
||||||
|
- client
|
||||||
- namesgenerator
|
- namesgenerator
|
||||||
|
- opts
|
||||||
|
- pkg/archive
|
||||||
|
- pkg/fileutils
|
||||||
|
- pkg/gitutils
|
||||||
|
- pkg/homedir
|
||||||
|
- pkg/httputils
|
||||||
|
- pkg/idtools
|
||||||
|
- pkg/ioutils
|
||||||
|
- pkg/jsonlog
|
||||||
|
- pkg/jsonmessage
|
||||||
|
- pkg/longpath
|
||||||
|
- pkg/namesgenerator
|
||||||
|
- pkg/pools
|
||||||
|
- pkg/progress
|
||||||
|
- pkg/promise
|
||||||
|
- pkg/random
|
||||||
|
- pkg/stdcopy
|
||||||
|
- pkg/streamformatter
|
||||||
|
- pkg/stringid
|
||||||
|
- pkg/symlink
|
||||||
|
- pkg/system
|
||||||
|
- pkg/tarsum
|
||||||
|
- pkg/term
|
||||||
|
- pkg/term/windows
|
||||||
|
- pkg/tlsconfig
|
||||||
|
- pkg/urlutil
|
||||||
|
- registry
|
||||||
|
- runconfig/opts
|
||||||
- name: github.com/docker/engine-api
|
- name: github.com/docker/engine-api
|
||||||
version: 3d1601b9d2436a70b0dfc045a23f6503d19195df
|
version: 3d1601b9d2436a70b0dfc045a23f6503d19195df
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -199,7 +259,7 @@ imports:
|
||||||
- name: github.com/fatih/color
|
- name: github.com/fatih/color
|
||||||
version: 9131ab34cf20d2f6d83fdc67168a5430d1c7dc23
|
version: 9131ab34cf20d2f6d83fdc67168a5430d1c7dc23
|
||||||
- name: github.com/gambol99/go-marathon
|
- name: github.com/gambol99/go-marathon
|
||||||
version: 6b00a5b651b1beb2c6821863f7c60df490bd46c8
|
version: d672c6fbb499596869d95146a26e7d0746c06c54
|
||||||
- name: github.com/ghodss/yaml
|
- name: github.com/ghodss/yaml
|
||||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||||
- name: github.com/go-ini/ini
|
- name: github.com/go-ini/ini
|
||||||
|
@ -324,6 +384,7 @@ imports:
|
||||||
- name: github.com/opencontainers/runc
|
- name: github.com/opencontainers/runc
|
||||||
version: 50401b5b4c2e01e4f1372b73a021742deeaf4e2d
|
version: 50401b5b4c2e01e4f1372b73a021742deeaf4e2d
|
||||||
subpackages:
|
subpackages:
|
||||||
|
- libcontainer/system
|
||||||
- libcontainer/user
|
- libcontainer/user
|
||||||
- name: github.com/ovh/go-ovh
|
- name: github.com/ovh/go-ovh
|
||||||
version: d2207178e10e4527e8f222fd8707982df8c3af17
|
version: d2207178e10e4527e8f222fd8707982df8c3af17
|
||||||
|
@ -667,4 +728,61 @@ imports:
|
||||||
- tools/clientcmd/api
|
- tools/clientcmd/api
|
||||||
- tools/metrics
|
- tools/metrics
|
||||||
- transport
|
- transport
|
||||||
testImports: []
|
testImports:
|
||||||
|
- name: github.com/Azure/go-ansiterm
|
||||||
|
version: fa152c58bc15761d0200cb75fe958b89a9d4888e
|
||||||
|
subpackages:
|
||||||
|
- winterm
|
||||||
|
- name: github.com/docker/libcompose
|
||||||
|
version: c6a7d4679d065a4f50e08d4d1fe13776062cf1ec
|
||||||
|
subpackages:
|
||||||
|
- config
|
||||||
|
- docker
|
||||||
|
- docker/auth
|
||||||
|
- docker/builder
|
||||||
|
- docker/client
|
||||||
|
- docker/container
|
||||||
|
- docker/ctx
|
||||||
|
- docker/image
|
||||||
|
- docker/network
|
||||||
|
- docker/service
|
||||||
|
- docker/volume
|
||||||
|
- labels
|
||||||
|
- logger
|
||||||
|
- lookup
|
||||||
|
- project
|
||||||
|
- project/events
|
||||||
|
- project/options
|
||||||
|
- utils
|
||||||
|
- version
|
||||||
|
- yaml
|
||||||
|
- name: github.com/docker/libtrust
|
||||||
|
version: fa567046d9b14f6aa788882a950d69651d230b21
|
||||||
|
- name: github.com/flynn/go-shlex
|
||||||
|
version: 3f9db97f856818214da2e1057f8ad84803971cff
|
||||||
|
- name: github.com/go-check/check
|
||||||
|
version: 11d3bc7aa68e238947792f30573146a3231fc0f1
|
||||||
|
- name: github.com/gorilla/mux
|
||||||
|
version: e444e69cbd2e2e3e0749a2f3c717cec491552bbf
|
||||||
|
- name: github.com/libkermit/compose
|
||||||
|
version: d18cc8ec55cd7033d6c11c0db3b8f521ddbe339a
|
||||||
|
subpackages:
|
||||||
|
- check
|
||||||
|
- name: github.com/libkermit/docker
|
||||||
|
version: 4585c8ba51a219e3c8df6b9d355438e4d540fe70
|
||||||
|
- name: github.com/libkermit/docker-check
|
||||||
|
version: 182e98fd61f66048b3b794f7645b0262104cbf84
|
||||||
|
- name: github.com/Nvveen/Gotty
|
||||||
|
version: cd527374f1e5bff4938207604a14f2e38a9cf512
|
||||||
|
- name: github.com/vdemeester/shakers
|
||||||
|
version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
|
||||||
|
- name: github.com/xeipuuv/gojsonpointer
|
||||||
|
version: 6fe8760cad3569743d51ddbb243b26f8456742dc
|
||||||
|
- name: github.com/xeipuuv/gojsonreference
|
||||||
|
version: e02fc20de94c78484cd5ffb007f8af96be030a45
|
||||||
|
- name: github.com/xeipuuv/gojsonschema
|
||||||
|
version: a55c211c418162597a32c74c7230f81adb5ad616
|
||||||
|
- name: golang.org/x/time
|
||||||
|
version: a4bde12657593d5e90d0533a3e4fd95e635124cb
|
||||||
|
subpackages:
|
||||||
|
- rate
|
||||||
|
|
|
@ -65,7 +65,7 @@ import:
|
||||||
- acme
|
- acme
|
||||||
- package: gopkg.in/fsnotify.v1
|
- package: gopkg.in/fsnotify.v1
|
||||||
- package: github.com/docker/docker
|
- package: github.com/docker/docker
|
||||||
version: v1.13.0
|
version: v17.04.0-ce
|
||||||
subpackages:
|
subpackages:
|
||||||
- namesgenerator
|
- namesgenerator
|
||||||
- package: github.com/mattn/go-shellwords
|
- package: github.com/mattn/go-shellwords
|
||||||
|
@ -91,7 +91,7 @@ import:
|
||||||
- package: k8s.io/client-go
|
- package: k8s.io/client-go
|
||||||
version: v2.0.0
|
version: v2.0.0
|
||||||
- package: github.com/gambol99/go-marathon
|
- package: github.com/gambol99/go-marathon
|
||||||
version: ^0.5.1
|
version: d672c6fbb499596869d95146a26e7d0746c06c54
|
||||||
- package: github.com/ArthurHlt/go-eureka-client
|
- package: github.com/ArthurHlt/go-eureka-client
|
||||||
subpackages:
|
subpackages:
|
||||||
- eureka
|
- eureka
|
||||||
|
|
85
vendor/github.com/docker/distribution/context/context.go
generated
vendored
Normal file
85
vendor/github.com/docker/distribution/context/context.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/uuid"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context is a copy of Context from the golang.org/x/net/context package.
|
||||||
|
type Context interface {
|
||||||
|
context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// instanceContext is a context that provides only an instance id. It is
|
||||||
|
// provided as the main background context.
|
||||||
|
type instanceContext struct {
|
||||||
|
Context
|
||||||
|
id string // id of context, logged as "instance.id"
|
||||||
|
once sync.Once // once protect generation of the id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ic *instanceContext) Value(key interface{}) interface{} {
|
||||||
|
if key == "instance.id" {
|
||||||
|
ic.once.Do(func() {
|
||||||
|
// We want to lazy initialize the UUID such that we don't
|
||||||
|
// call a random generator from the package initialization
|
||||||
|
// code. For various reasons random could not be available
|
||||||
|
// https://github.com/docker/distribution/issues/782
|
||||||
|
ic.id = uuid.Generate().String()
|
||||||
|
})
|
||||||
|
return ic.id
|
||||||
|
}
|
||||||
|
|
||||||
|
return ic.Context.Value(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
var background = &instanceContext{
|
||||||
|
Context: context.Background(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background returns a non-nil, empty Context. The background context
|
||||||
|
// provides a single key, "instance.id" that is globally unique to the
|
||||||
|
// process.
|
||||||
|
func Background() Context {
|
||||||
|
return background
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithValue returns a copy of parent in which the value associated with key is
|
||||||
|
// val. Use context Values only for request-scoped data that transits processes
|
||||||
|
// and APIs, not for passing optional parameters to functions.
|
||||||
|
func WithValue(parent Context, key, val interface{}) Context {
|
||||||
|
return context.WithValue(parent, key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringMapContext is a simple context implementation that checks a map for a
|
||||||
|
// key, falling back to a parent if not present.
|
||||||
|
type stringMapContext struct {
|
||||||
|
context.Context
|
||||||
|
m map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithValues returns a context that proxies lookups through a map. Only
|
||||||
|
// supports string keys.
|
||||||
|
func WithValues(ctx context.Context, m map[string]interface{}) context.Context {
|
||||||
|
mo := make(map[string]interface{}, len(m)) // make our own copy.
|
||||||
|
for k, v := range m {
|
||||||
|
mo[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringMapContext{
|
||||||
|
Context: ctx,
|
||||||
|
m: mo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smc stringMapContext) Value(key interface{}) interface{} {
|
||||||
|
if ks, ok := key.(string); ok {
|
||||||
|
if v, ok := smc.m[ks]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return smc.Context.Value(key)
|
||||||
|
}
|
89
vendor/github.com/docker/distribution/context/doc.go
generated
vendored
Normal file
89
vendor/github.com/docker/distribution/context/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// Package context provides several utilities for working with
|
||||||
|
// golang.org/x/net/context in http requests. Primarily, the focus is on
|
||||||
|
// logging relevant request information but this package is not limited to
|
||||||
|
// that purpose.
|
||||||
|
//
|
||||||
|
// The easiest way to get started is to get the background context:
|
||||||
|
//
|
||||||
|
// ctx := context.Background()
|
||||||
|
//
|
||||||
|
// The returned context should be passed around your application and be the
|
||||||
|
// root of all other context instances. If the application has a version, this
|
||||||
|
// line should be called before anything else:
|
||||||
|
//
|
||||||
|
// ctx := context.WithVersion(context.Background(), version)
|
||||||
|
//
|
||||||
|
// The above will store the version in the context and will be available to
|
||||||
|
// the logger.
|
||||||
|
//
|
||||||
|
// Logging
|
||||||
|
//
|
||||||
|
// The most useful aspect of this package is GetLogger. This function takes
|
||||||
|
// any context.Context interface and returns the current logger from the
|
||||||
|
// context. Canonical usage looks like this:
|
||||||
|
//
|
||||||
|
// GetLogger(ctx).Infof("something interesting happened")
|
||||||
|
//
|
||||||
|
// GetLogger also takes optional key arguments. The keys will be looked up in
|
||||||
|
// the context and reported with the logger. The following example would
|
||||||
|
// return a logger that prints the version with each log message:
|
||||||
|
//
|
||||||
|
// ctx := context.Context(context.Background(), "version", version)
|
||||||
|
// GetLogger(ctx, "version").Infof("this log message has a version field")
|
||||||
|
//
|
||||||
|
// The above would print out a log message like this:
|
||||||
|
//
|
||||||
|
// INFO[0000] this log message has a version field version=v2.0.0-alpha.2.m
|
||||||
|
//
|
||||||
|
// When used with WithLogger, we gain the ability to decorate the context with
|
||||||
|
// loggers that have information from disparate parts of the call stack.
|
||||||
|
// Following from the version example, we can build a new context with the
|
||||||
|
// configured logger such that we always print the version field:
|
||||||
|
//
|
||||||
|
// ctx = WithLogger(ctx, GetLogger(ctx, "version"))
|
||||||
|
//
|
||||||
|
// Since the logger has been pushed to the context, we can now get the version
|
||||||
|
// field for free with our log messages. Future calls to GetLogger on the new
|
||||||
|
// context will have the version field:
|
||||||
|
//
|
||||||
|
// GetLogger(ctx).Infof("this log message has a version field")
|
||||||
|
//
|
||||||
|
// This becomes more powerful when we start stacking loggers. Let's say we
|
||||||
|
// have the version logger from above but also want a request id. Using the
|
||||||
|
// context above, in our request scoped function, we place another logger in
|
||||||
|
// the context:
|
||||||
|
//
|
||||||
|
// ctx = context.WithValue(ctx, "http.request.id", "unique id") // called when building request context
|
||||||
|
// ctx = WithLogger(ctx, GetLogger(ctx, "http.request.id"))
|
||||||
|
//
|
||||||
|
// When GetLogger is called on the new context, "http.request.id" will be
|
||||||
|
// included as a logger field, along with the original "version" field:
|
||||||
|
//
|
||||||
|
// INFO[0000] this log message has a version field http.request.id=unique id version=v2.0.0-alpha.2.m
|
||||||
|
//
|
||||||
|
// 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 varaibles.
|
||||||
|
//
|
||||||
|
// HTTP Requests
|
||||||
|
//
|
||||||
|
// This package also contains several methods for working with http requests.
|
||||||
|
// The concepts are very similar to those described above. We simply place the
|
||||||
|
// request in the context using WithRequest. This makes the request variables
|
||||||
|
// available. GetRequestLogger can then be called to get request specific
|
||||||
|
// variables in a log line:
|
||||||
|
//
|
||||||
|
// ctx = WithRequest(ctx, req)
|
||||||
|
// GetRequestLogger(ctx).Infof("request variables")
|
||||||
|
//
|
||||||
|
// Like above, if we want to include the request data in all log messages in
|
||||||
|
// the context, we push the logger to a new context and use that one:
|
||||||
|
//
|
||||||
|
// ctx = WithLogger(ctx, GetRequestLogger(ctx))
|
||||||
|
//
|
||||||
|
// The concept is fairly powerful and ensures that calls throughout the stack
|
||||||
|
// can be traced in log messages. Using the fields like "http.request.id", one
|
||||||
|
// can analyze call flow for a particular request with a simple grep of the
|
||||||
|
// logs.
|
||||||
|
package context
|
366
vendor/github.com/docker/distribution/context/http.go
generated
vendored
Normal file
366
vendor/github.com/docker/distribution/context/http.go
generated
vendored
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/uuid"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Common errors used with this package.
|
||||||
|
var (
|
||||||
|
ErrNoRequestContext = errors.New("no http request in context")
|
||||||
|
ErrNoResponseWriterContext = errors.New("no http response in context")
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseIP(ipStr string) net.IP {
|
||||||
|
ip := net.ParseIP(ipStr)
|
||||||
|
if ip == nil {
|
||||||
|
log.Warnf("invalid remote IP address: %q", ipStr)
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr extracts the remote address of the request, taking into
|
||||||
|
// account proxy headers.
|
||||||
|
func RemoteAddr(r *http.Request) string {
|
||||||
|
if prior := r.Header.Get("X-Forwarded-For"); prior != "" {
|
||||||
|
proxies := strings.Split(prior, ",")
|
||||||
|
if len(proxies) > 0 {
|
||||||
|
remoteAddr := strings.Trim(proxies[0], " ")
|
||||||
|
if parseIP(remoteAddr) != nil {
|
||||||
|
return remoteAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// X-Real-Ip is less supported, but worth checking in the
|
||||||
|
// absence of X-Forwarded-For
|
||||||
|
if realIP := r.Header.Get("X-Real-Ip"); realIP != "" {
|
||||||
|
if parseIP(realIP) != nil {
|
||||||
|
return realIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.RemoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteIP extracts the remote IP of the request, taking into
|
||||||
|
// account proxy headers.
|
||||||
|
func RemoteIP(r *http.Request) string {
|
||||||
|
addr := RemoteAddr(r)
|
||||||
|
|
||||||
|
// Try parsing it as "IP:port"
|
||||||
|
if ip, _, err := net.SplitHostPort(addr); err == nil {
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRequest places the request on the context. The context of the request
|
||||||
|
// is assigned a unique id, available at "http.request.id". The request itself
|
||||||
|
// is available at "http.request". Other common attributes are available under
|
||||||
|
// the prefix "http.request.". If a request is already present on the context,
|
||||||
|
// this method will panic.
|
||||||
|
func WithRequest(ctx Context, r *http.Request) Context {
|
||||||
|
if ctx.Value("http.request") != nil {
|
||||||
|
// NOTE(stevvooe): This needs to be considered a programming error. It
|
||||||
|
// is unlikely that we'd want to have more than one request in
|
||||||
|
// context.
|
||||||
|
panic("only one request per context")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpRequestContext{
|
||||||
|
Context: ctx,
|
||||||
|
startedAt: time.Now(),
|
||||||
|
id: uuid.Generate().String(),
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequest returns the http request in the given context. Returns
|
||||||
|
// ErrNoRequestContext if the context does not have an http request associated
|
||||||
|
// with it.
|
||||||
|
func GetRequest(ctx Context) (*http.Request, error) {
|
||||||
|
if r, ok := ctx.Value("http.request").(*http.Request); r != nil && ok {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
return nil, ErrNoRequestContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequestID attempts to resolve the current request id, if possible. An
|
||||||
|
// error is return if it is not available on the context.
|
||||||
|
func GetRequestID(ctx Context) string {
|
||||||
|
return GetStringValue(ctx, "http.request.id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithResponseWriter returns a new context and response writer that makes
|
||||||
|
// interesting response statistics available within the context.
|
||||||
|
func WithResponseWriter(ctx Context, w http.ResponseWriter) (Context, http.ResponseWriter) {
|
||||||
|
if closeNotifier, ok := w.(http.CloseNotifier); ok {
|
||||||
|
irwCN := &instrumentedResponseWriterCN{
|
||||||
|
instrumentedResponseWriter: instrumentedResponseWriter{
|
||||||
|
ResponseWriter: w,
|
||||||
|
Context: ctx,
|
||||||
|
},
|
||||||
|
CloseNotifier: closeNotifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
return irwCN, irwCN
|
||||||
|
}
|
||||||
|
|
||||||
|
irw := instrumentedResponseWriter{
|
||||||
|
ResponseWriter: w,
|
||||||
|
Context: ctx,
|
||||||
|
}
|
||||||
|
return &irw, &irw
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResponseWriter returns the http.ResponseWriter from the provided
|
||||||
|
// context. If not present, ErrNoResponseWriterContext is returned. The
|
||||||
|
// returned instance provides instrumentation in the context.
|
||||||
|
func GetResponseWriter(ctx Context) (http.ResponseWriter, error) {
|
||||||
|
v := ctx.Value("http.response")
|
||||||
|
|
||||||
|
rw, ok := v.(http.ResponseWriter)
|
||||||
|
if !ok || rw == nil {
|
||||||
|
return nil, ErrNoResponseWriterContext
|
||||||
|
}
|
||||||
|
|
||||||
|
return rw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVarsFromRequest let's us change request vars implementation for testing
|
||||||
|
// and maybe future changes.
|
||||||
|
var getVarsFromRequest = mux.Vars
|
||||||
|
|
||||||
|
// WithVars extracts gorilla/mux vars and makes them available on the returned
|
||||||
|
// context. Variables are available at keys with the prefix "vars.". For
|
||||||
|
// example, if looking for the variable "name", it can be accessed as
|
||||||
|
// "vars.name". Implementations that are accessing values need not know that
|
||||||
|
// the underlying context is implemented with gorilla/mux vars.
|
||||||
|
func WithVars(ctx Context, r *http.Request) Context {
|
||||||
|
return &muxVarsContext{
|
||||||
|
Context: ctx,
|
||||||
|
vars: getVarsFromRequest(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequestLogger returns a logger that contains fields from the request in
|
||||||
|
// the current context. If the request is not available in the context, no
|
||||||
|
// fields will display. Request loggers can safely be pushed onto the context.
|
||||||
|
func GetRequestLogger(ctx Context) Logger {
|
||||||
|
return GetLogger(ctx,
|
||||||
|
"http.request.id",
|
||||||
|
"http.request.method",
|
||||||
|
"http.request.host",
|
||||||
|
"http.request.uri",
|
||||||
|
"http.request.referer",
|
||||||
|
"http.request.useragent",
|
||||||
|
"http.request.remoteaddr",
|
||||||
|
"http.request.contenttype")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResponseLogger reads the current response stats and builds a logger.
|
||||||
|
// Because the values are read at call time, pushing a logger returned from
|
||||||
|
// this function on the context will lead to missing or invalid data. Only
|
||||||
|
// call this at the end of a request, after the response has been written.
|
||||||
|
func GetResponseLogger(ctx Context) Logger {
|
||||||
|
l := getLogrusLogger(ctx,
|
||||||
|
"http.response.written",
|
||||||
|
"http.response.status",
|
||||||
|
"http.response.contenttype")
|
||||||
|
|
||||||
|
duration := Since(ctx, "http.request.startedat")
|
||||||
|
|
||||||
|
if duration > 0 {
|
||||||
|
l = l.WithField("http.response.duration", duration.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpRequestContext makes information about a request available to context.
|
||||||
|
type httpRequestContext struct {
|
||||||
|
Context
|
||||||
|
|
||||||
|
startedAt time.Time
|
||||||
|
id string
|
||||||
|
r *http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns a keyed element of the request for use in the context. To get
|
||||||
|
// the request itself, query "request". For other components, access them as
|
||||||
|
// "request.<component>". For example, r.RequestURI
|
||||||
|
func (ctx *httpRequestContext) Value(key interface{}) interface{} {
|
||||||
|
if keyStr, ok := key.(string); ok {
|
||||||
|
if keyStr == "http.request" {
|
||||||
|
return ctx.r
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(keyStr, "http.request.") {
|
||||||
|
goto fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(keyStr, ".")
|
||||||
|
|
||||||
|
if len(parts) != 3 {
|
||||||
|
goto fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
switch parts[2] {
|
||||||
|
case "uri":
|
||||||
|
return ctx.r.RequestURI
|
||||||
|
case "remoteaddr":
|
||||||
|
return RemoteAddr(ctx.r)
|
||||||
|
case "method":
|
||||||
|
return ctx.r.Method
|
||||||
|
case "host":
|
||||||
|
return ctx.r.Host
|
||||||
|
case "referer":
|
||||||
|
referer := ctx.r.Referer()
|
||||||
|
if referer != "" {
|
||||||
|
return referer
|
||||||
|
}
|
||||||
|
case "useragent":
|
||||||
|
return ctx.r.UserAgent()
|
||||||
|
case "id":
|
||||||
|
return ctx.id
|
||||||
|
case "startedat":
|
||||||
|
return ctx.startedAt
|
||||||
|
case "contenttype":
|
||||||
|
ct := ctx.r.Header.Get("Content-Type")
|
||||||
|
if ct != "" {
|
||||||
|
return ct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
return ctx.Context.Value(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
type muxVarsContext struct {
|
||||||
|
Context
|
||||||
|
vars map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *muxVarsContext) Value(key interface{}) interface{} {
|
||||||
|
if keyStr, ok := key.(string); ok {
|
||||||
|
if keyStr == "vars" {
|
||||||
|
return ctx.vars
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(keyStr, "vars.") {
|
||||||
|
keyStr = strings.TrimPrefix(keyStr, "vars.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := ctx.vars[keyStr]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.Context.Value(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// instrumentedResponseWriterCN provides response writer information in a
|
||||||
|
// context. It implements http.CloseNotifier so that users can detect
|
||||||
|
// early disconnects.
|
||||||
|
type instrumentedResponseWriterCN struct {
|
||||||
|
instrumentedResponseWriter
|
||||||
|
http.CloseNotifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// instrumentedResponseWriter provides response writer information in a
|
||||||
|
// context. This variant is only used in the case where CloseNotifier is not
|
||||||
|
// implemented by the parent ResponseWriter.
|
||||||
|
type instrumentedResponseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
Context
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
status int
|
||||||
|
written int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (irw *instrumentedResponseWriter) Write(p []byte) (n int, err error) {
|
||||||
|
n, err = irw.ResponseWriter.Write(p)
|
||||||
|
|
||||||
|
irw.mu.Lock()
|
||||||
|
irw.written += int64(n)
|
||||||
|
|
||||||
|
// Guess the likely status if not set.
|
||||||
|
if irw.status == 0 {
|
||||||
|
irw.status = http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
irw.mu.Unlock()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (irw *instrumentedResponseWriter) WriteHeader(status int) {
|
||||||
|
irw.ResponseWriter.WriteHeader(status)
|
||||||
|
|
||||||
|
irw.mu.Lock()
|
||||||
|
irw.status = status
|
||||||
|
irw.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (irw *instrumentedResponseWriter) Flush() {
|
||||||
|
if flusher, ok := irw.ResponseWriter.(http.Flusher); ok {
|
||||||
|
flusher.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (irw *instrumentedResponseWriter) Value(key interface{}) interface{} {
|
||||||
|
if keyStr, ok := key.(string); ok {
|
||||||
|
if keyStr == "http.response" {
|
||||||
|
return irw
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(keyStr, "http.response.") {
|
||||||
|
goto fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(keyStr, ".")
|
||||||
|
|
||||||
|
if len(parts) != 3 {
|
||||||
|
goto fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
irw.mu.Lock()
|
||||||
|
defer irw.mu.Unlock()
|
||||||
|
|
||||||
|
switch parts[2] {
|
||||||
|
case "written":
|
||||||
|
return irw.written
|
||||||
|
case "status":
|
||||||
|
return irw.status
|
||||||
|
case "contenttype":
|
||||||
|
contentType := irw.Header().Get("Content-Type")
|
||||||
|
if contentType != "" {
|
||||||
|
return contentType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
return irw.Context.Value(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (irw *instrumentedResponseWriterCN) Value(key interface{}) interface{} {
|
||||||
|
if keyStr, ok := key.(string); ok {
|
||||||
|
if keyStr == "http.response" {
|
||||||
|
return irw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return irw.instrumentedResponseWriter.Value(key)
|
||||||
|
}
|
116
vendor/github.com/docker/distribution/context/logger.go
generated
vendored
Normal file
116
vendor/github.com/docker/distribution/context/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger provides a leveled-logging interface.
|
||||||
|
type Logger interface {
|
||||||
|
// standard logger methods
|
||||||
|
Print(args ...interface{})
|
||||||
|
Printf(format string, args ...interface{})
|
||||||
|
Println(args ...interface{})
|
||||||
|
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
Fatalln(args ...interface{})
|
||||||
|
|
||||||
|
Panic(args ...interface{})
|
||||||
|
Panicf(format string, args ...interface{})
|
||||||
|
Panicln(args ...interface{})
|
||||||
|
|
||||||
|
// Leveled methods, from logrus
|
||||||
|
Debug(args ...interface{})
|
||||||
|
Debugf(format string, args ...interface{})
|
||||||
|
Debugln(args ...interface{})
|
||||||
|
|
||||||
|
Error(args ...interface{})
|
||||||
|
Errorf(format string, args ...interface{})
|
||||||
|
Errorln(args ...interface{})
|
||||||
|
|
||||||
|
Info(args ...interface{})
|
||||||
|
Infof(format string, args ...interface{})
|
||||||
|
Infoln(args ...interface{})
|
||||||
|
|
||||||
|
Warn(args ...interface{})
|
||||||
|
Warnf(format string, args ...interface{})
|
||||||
|
Warnln(args ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogger creates a new context with provided logger.
|
||||||
|
func WithLogger(ctx Context, logger Logger) Context {
|
||||||
|
return WithValue(ctx, "logger", logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoggerWithField returns a logger instance with the specified field key
|
||||||
|
// and value without affecting the context. Extra specified keys will be
|
||||||
|
// resolved from the context.
|
||||||
|
func GetLoggerWithField(ctx Context, key, value interface{}, keys ...interface{}) Logger {
|
||||||
|
return getLogrusLogger(ctx, keys...).WithField(fmt.Sprint(key), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoggerWithFields returns a logger instance with the specified fields
|
||||||
|
// without affecting the context. Extra specified keys will be resolved from
|
||||||
|
// the context.
|
||||||
|
func GetLoggerWithFields(ctx Context, fields map[interface{}]interface{}, keys ...interface{}) Logger {
|
||||||
|
// must convert from interface{} -> interface{} to string -> interface{} for logrus.
|
||||||
|
lfields := make(logrus.Fields, len(fields))
|
||||||
|
for key, value := range fields {
|
||||||
|
lfields[fmt.Sprint(key)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return getLogrusLogger(ctx, keys...).WithFields(lfields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogger returns the logger from the current context, if present. If one
|
||||||
|
// or more keys are provided, they will be resolved on the context and
|
||||||
|
// included in the logger. While context.Value takes an interface, any key
|
||||||
|
// argument passed to GetLogger will be passed to fmt.Sprint when expanded as
|
||||||
|
// a logging key field. If context keys are integer constants, for example,
|
||||||
|
// its recommended that a String method is implemented.
|
||||||
|
func GetLogger(ctx Context, keys ...interface{}) Logger {
|
||||||
|
return getLogrusLogger(ctx, keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogrusLogger returns the logrus logger for the context. If one more keys
|
||||||
|
// are provided, they will be resolved on the context and included in the
|
||||||
|
// logger. Only use this function if specific logrus functionality is
|
||||||
|
// required.
|
||||||
|
func getLogrusLogger(ctx Context, keys ...interface{}) *logrus.Entry {
|
||||||
|
var logger *logrus.Entry
|
||||||
|
|
||||||
|
// Get a logger, if it is present.
|
||||||
|
loggerInterface := ctx.Value("logger")
|
||||||
|
if loggerInterface != nil {
|
||||||
|
if lgr, ok := loggerInterface.(*logrus.Entry); ok {
|
||||||
|
logger = lgr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger == nil {
|
||||||
|
fields := logrus.Fields{}
|
||||||
|
|
||||||
|
// Fill in the instance id, if we have it.
|
||||||
|
instanceID := ctx.Value("instance.id")
|
||||||
|
if instanceID != nil {
|
||||||
|
fields["instance.id"] = instanceID
|
||||||
|
}
|
||||||
|
|
||||||
|
fields["go.version"] = runtime.Version()
|
||||||
|
// If no logger is found, just return the standard logger.
|
||||||
|
logger = logrus.StandardLogger().WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := logrus.Fields{}
|
||||||
|
for _, key := range keys {
|
||||||
|
v := ctx.Value(key)
|
||||||
|
if v != nil {
|
||||||
|
fields[fmt.Sprint(key)] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return logger.WithFields(fields)
|
||||||
|
}
|
104
vendor/github.com/docker/distribution/context/trace.go
generated
vendored
Normal file
104
vendor/github.com/docker/distribution/context/trace.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithTrace allocates a traced timing span in a new context. This allows a
|
||||||
|
// caller to track the time between calling WithTrace and the returned done
|
||||||
|
// function. When the done function is called, a log message is emitted with a
|
||||||
|
// "trace.duration" field, corresponding to the elapsed time and a
|
||||||
|
// "trace.func" field, corresponding to the function that called WithTrace.
|
||||||
|
//
|
||||||
|
// The logging keys "trace.id" and "trace.parent.id" are provided to implement
|
||||||
|
// dapper-like tracing. This function should be complemented with a WithSpan
|
||||||
|
// method that could be used for tracing distributed RPC calls.
|
||||||
|
//
|
||||||
|
// The main benefit of this function is to post-process log messages or
|
||||||
|
// intercept them in a hook to provide timing data. Trace ids and parent ids
|
||||||
|
// can also be linked to provide call tracing, if so required.
|
||||||
|
//
|
||||||
|
// Here is an example of the usage:
|
||||||
|
//
|
||||||
|
// func timedOperation(ctx Context) {
|
||||||
|
// ctx, done := WithTrace(ctx)
|
||||||
|
// defer done("this will be the log message")
|
||||||
|
// // ... function body ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If the function ran for roughly 1s, such a usage would emit a log message
|
||||||
|
// as follows:
|
||||||
|
//
|
||||||
|
// INFO[0001] this will be the log message trace.duration=1.004575763s trace.func=github.com/docker/distribution/context.traceOperation trace.id=<id> ...
|
||||||
|
//
|
||||||
|
// Notice that the function name is automatically resolved, along with the
|
||||||
|
// package and a trace id is emitted that can be linked with parent ids.
|
||||||
|
func WithTrace(ctx Context) (Context, func(format string, a ...interface{})) {
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
pc, file, line, _ := runtime.Caller(1)
|
||||||
|
f := runtime.FuncForPC(pc)
|
||||||
|
ctx = &traced{
|
||||||
|
Context: ctx,
|
||||||
|
id: uuid.Generate().String(),
|
||||||
|
start: time.Now(),
|
||||||
|
parent: GetStringValue(ctx, "trace.id"),
|
||||||
|
fnname: f.Name(),
|
||||||
|
file: file,
|
||||||
|
line: line,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, func(format string, a ...interface{}) {
|
||||||
|
GetLogger(ctx,
|
||||||
|
"trace.duration",
|
||||||
|
"trace.id",
|
||||||
|
"trace.parent.id",
|
||||||
|
"trace.func",
|
||||||
|
"trace.file",
|
||||||
|
"trace.line").
|
||||||
|
Debugf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// traced represents a context that is traced for function call timing. It
|
||||||
|
// also provides fast lookup for the various attributes that are available on
|
||||||
|
// the trace.
|
||||||
|
type traced struct {
|
||||||
|
Context
|
||||||
|
id string
|
||||||
|
parent string
|
||||||
|
start time.Time
|
||||||
|
fnname string
|
||||||
|
file string
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *traced) Value(key interface{}) interface{} {
|
||||||
|
switch key {
|
||||||
|
case "trace.start":
|
||||||
|
return ts.start
|
||||||
|
case "trace.duration":
|
||||||
|
return time.Since(ts.start)
|
||||||
|
case "trace.id":
|
||||||
|
return ts.id
|
||||||
|
case "trace.parent.id":
|
||||||
|
if ts.parent == "" {
|
||||||
|
return nil // must return nil to signal no parent.
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.parent
|
||||||
|
case "trace.func":
|
||||||
|
return ts.fnname
|
||||||
|
case "trace.file":
|
||||||
|
return ts.file
|
||||||
|
case "trace.line":
|
||||||
|
return ts.line
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.Context.Value(key)
|
||||||
|
}
|
24
vendor/github.com/docker/distribution/context/util.go
generated
vendored
Normal file
24
vendor/github.com/docker/distribution/context/util.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Since looks up key, which should be a time.Time, and returns the duration
|
||||||
|
// since that time. If the key is not found, the value returned will be zero.
|
||||||
|
// This is helpful when inferring metrics related to context execution times.
|
||||||
|
func Since(ctx Context, key interface{}) time.Duration {
|
||||||
|
if startedAt, ok := ctx.Value(key).(time.Time); ok {
|
||||||
|
return time.Since(startedAt)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringValue returns a string value from the context. The empty string
|
||||||
|
// will be returned if not found.
|
||||||
|
func GetStringValue(ctx Context, key interface{}) (value string) {
|
||||||
|
if valuev, ok := ctx.Value(key).(string); ok {
|
||||||
|
value = valuev
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
16
vendor/github.com/docker/distribution/context/version.go
generated
vendored
Normal file
16
vendor/github.com/docker/distribution/context/version.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
// WithVersion stores the application version in the context. The new context
|
||||||
|
// gets a logger to ensure log messages are marked with the application
|
||||||
|
// version.
|
||||||
|
func WithVersion(ctx Context, version string) Context {
|
||||||
|
ctx = WithValue(ctx, "version", version)
|
||||||
|
// push a new logger onto the stack
|
||||||
|
return WithLogger(ctx, GetLogger(ctx, "version"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion returns the application version from the context. An empty
|
||||||
|
// string may returned if the version was not set on the context.
|
||||||
|
func GetVersion(ctx Context) string {
|
||||||
|
return GetStringValue(ctx, "version")
|
||||||
|
}
|
267
vendor/github.com/docker/distribution/registry/api/errcode/errors.go
generated
vendored
Normal file
267
vendor/github.com/docker/distribution/registry/api/errcode/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
package errcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorCoder is the base interface for ErrorCode and Error allowing
|
||||||
|
// users of each to just call ErrorCode to get the real ID of each
|
||||||
|
type ErrorCoder interface {
|
||||||
|
ErrorCode() ErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCode represents the error type. The errors are serialized via strings
|
||||||
|
// and the integer format may change and should *never* be exported.
|
||||||
|
type ErrorCode int
|
||||||
|
|
||||||
|
var _ error = ErrorCode(0)
|
||||||
|
|
||||||
|
// ErrorCode just returns itself
|
||||||
|
func (ec ErrorCode) ErrorCode() ErrorCode {
|
||||||
|
return ec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the ID/Value
|
||||||
|
func (ec ErrorCode) Error() string {
|
||||||
|
// NOTE(stevvooe): Cannot use message here since it may have unpopulated args.
|
||||||
|
return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Descriptor returns the descriptor for the error code.
|
||||||
|
func (ec ErrorCode) Descriptor() ErrorDescriptor {
|
||||||
|
d, ok := errorCodeToDescriptors[ec]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return ErrorCodeUnknown.Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the canonical identifier for this error code.
|
||||||
|
func (ec ErrorCode) String() string {
|
||||||
|
return ec.Descriptor().Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message returned the human-readable error message for this error code.
|
||||||
|
func (ec ErrorCode) Message() string {
|
||||||
|
return ec.Descriptor().Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText encodes the receiver into UTF-8-encoded text and returns the
|
||||||
|
// result.
|
||||||
|
func (ec ErrorCode) MarshalText() (text []byte, err error) {
|
||||||
|
return []byte(ec.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText decodes the form generated by MarshalText.
|
||||||
|
func (ec *ErrorCode) UnmarshalText(text []byte) error {
|
||||||
|
desc, ok := idToDescriptors[string(text)]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
desc = ErrorCodeUnknown.Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
*ec = desc.Code
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMessage creates a new Error struct based on the passed-in info and
|
||||||
|
// overrides the Message property.
|
||||||
|
func (ec ErrorCode) WithMessage(message string) Error {
|
||||||
|
return Error{
|
||||||
|
Code: ec,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDetail creates a new Error struct based on the passed-in info and
|
||||||
|
// set the Detail property appropriately
|
||||||
|
func (ec ErrorCode) WithDetail(detail interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: ec,
|
||||||
|
Message: ec.Message(),
|
||||||
|
}.WithDetail(detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithArgs creates a new Error struct and sets the Args slice
|
||||||
|
func (ec ErrorCode) WithArgs(args ...interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: ec,
|
||||||
|
Message: ec.Message(),
|
||||||
|
}.WithArgs(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error provides a wrapper around ErrorCode with extra Details provided.
|
||||||
|
type Error struct {
|
||||||
|
Code ErrorCode `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Detail interface{} `json:"detail,omitempty"`
|
||||||
|
|
||||||
|
// TODO(duglin): See if we need an "args" property so we can do the
|
||||||
|
// variable substitution right before showing the message to the user
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = Error{}
|
||||||
|
|
||||||
|
// ErrorCode returns the ID/Value of this Error
|
||||||
|
func (e Error) ErrorCode() ErrorCode {
|
||||||
|
return e.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a human readable representation of the error.
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDetail will return a new Error, based on the current one, but with
|
||||||
|
// some Detail info added
|
||||||
|
func (e Error) WithDetail(detail interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: e.Code,
|
||||||
|
Message: e.Message,
|
||||||
|
Detail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithArgs uses the passed-in list of interface{} as the substitution
|
||||||
|
// variables in the Error's Message string, but returns a new Error
|
||||||
|
func (e Error) WithArgs(args ...interface{}) Error {
|
||||||
|
return Error{
|
||||||
|
Code: e.Code,
|
||||||
|
Message: fmt.Sprintf(e.Code.Message(), args...),
|
||||||
|
Detail: e.Detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorDescriptor provides relevant information about a given error code.
|
||||||
|
type ErrorDescriptor struct {
|
||||||
|
// Code is the error code that this descriptor describes.
|
||||||
|
Code ErrorCode
|
||||||
|
|
||||||
|
// Value provides a unique, string key, often captilized with
|
||||||
|
// underscores, to identify the error code. This value is used as the
|
||||||
|
// keyed value when serializing api errors.
|
||||||
|
Value string
|
||||||
|
|
||||||
|
// Message is a short, human readable decription of the error condition
|
||||||
|
// included in API responses.
|
||||||
|
Message string
|
||||||
|
|
||||||
|
// Description provides a complete account of the errors purpose, suitable
|
||||||
|
// for use in documentation.
|
||||||
|
Description string
|
||||||
|
|
||||||
|
// HTTPStatusCode provides the http status code that is associated with
|
||||||
|
// this error condition.
|
||||||
|
HTTPStatusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseErrorCode returns the value by the string error code.
|
||||||
|
// `ErrorCodeUnknown` will be returned if the error is not known.
|
||||||
|
func ParseErrorCode(value string) ErrorCode {
|
||||||
|
ed, ok := idToDescriptors[value]
|
||||||
|
if ok {
|
||||||
|
return ed.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCodeUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors provides the envelope for multiple errors and a few sugar methods
|
||||||
|
// for use within the application.
|
||||||
|
type Errors []error
|
||||||
|
|
||||||
|
var _ error = Errors{}
|
||||||
|
|
||||||
|
func (errs Errors) Error() string {
|
||||||
|
switch len(errs) {
|
||||||
|
case 0:
|
||||||
|
return "<nil>"
|
||||||
|
case 1:
|
||||||
|
return errs[0].Error()
|
||||||
|
default:
|
||||||
|
msg := "errors:\n"
|
||||||
|
for _, err := range errs {
|
||||||
|
msg += err.Error() + "\n"
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the current number of errors.
|
||||||
|
func (errs Errors) Len() int {
|
||||||
|
return len(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON converts slice of error, ErrorCode or Error into a
|
||||||
|
// slice of Error - then serializes
|
||||||
|
func (errs Errors) MarshalJSON() ([]byte, error) {
|
||||||
|
var tmpErrs struct {
|
||||||
|
Errors []Error `json:"errors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, daErr := range errs {
|
||||||
|
var err Error
|
||||||
|
|
||||||
|
switch daErr.(type) {
|
||||||
|
case ErrorCode:
|
||||||
|
err = daErr.(ErrorCode).WithDetail(nil)
|
||||||
|
case Error:
|
||||||
|
err = daErr.(Error)
|
||||||
|
default:
|
||||||
|
err = ErrorCodeUnknown.WithDetail(daErr)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the Error struct was setup and they forgot to set the
|
||||||
|
// Message field (meaning its "") then grab it from the ErrCode
|
||||||
|
msg := err.Message
|
||||||
|
if msg == "" {
|
||||||
|
msg = err.Code.Message()
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpErrs.Errors = append(tmpErrs.Errors, Error{
|
||||||
|
Code: err.Code,
|
||||||
|
Message: msg,
|
||||||
|
Detail: err.Detail,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(tmpErrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON deserializes []Error and then converts it into slice of
|
||||||
|
// Error or ErrorCode
|
||||||
|
func (errs *Errors) UnmarshalJSON(data []byte) error {
|
||||||
|
var tmpErrs struct {
|
||||||
|
Errors []Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &tmpErrs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var newErrs Errors
|
||||||
|
for _, daErr := range tmpErrs.Errors {
|
||||||
|
// If Message is empty or exactly matches the Code's message string
|
||||||
|
// then just use the Code, no need for a full Error struct
|
||||||
|
if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) {
|
||||||
|
// Error's w/o details get converted to ErrorCode
|
||||||
|
newErrs = append(newErrs, daErr.Code)
|
||||||
|
} else {
|
||||||
|
// Error's w/ details are untouched
|
||||||
|
newErrs = append(newErrs, Error{
|
||||||
|
Code: daErr.Code,
|
||||||
|
Message: daErr.Message,
|
||||||
|
Detail: daErr.Detail,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*errs = newErrs
|
||||||
|
return nil
|
||||||
|
}
|
44
vendor/github.com/docker/distribution/registry/api/errcode/handler.go
generated
vendored
Normal file
44
vendor/github.com/docker/distribution/registry/api/errcode/handler.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package errcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServeJSON attempts to serve the errcode in a JSON envelope. It marshals err
|
||||||
|
// and sets the content-type header to 'application/json'. It will handle
|
||||||
|
// ErrorCoder and Errors, and if necessary will create an envelope.
|
||||||
|
func ServeJSON(w http.ResponseWriter, err error) error {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
var sc int
|
||||||
|
|
||||||
|
switch errs := err.(type) {
|
||||||
|
case Errors:
|
||||||
|
if len(errs) < 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := errs[0].(ErrorCoder); ok {
|
||||||
|
sc = err.ErrorCode().Descriptor().HTTPStatusCode
|
||||||
|
}
|
||||||
|
case ErrorCoder:
|
||||||
|
sc = errs.ErrorCode().Descriptor().HTTPStatusCode
|
||||||
|
err = Errors{err} // create an envelope.
|
||||||
|
default:
|
||||||
|
// We just have an unhandled error type, so just place in an envelope
|
||||||
|
// and move along.
|
||||||
|
err = Errors{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sc == 0 {
|
||||||
|
sc = http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(sc)
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(err); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
138
vendor/github.com/docker/distribution/registry/api/errcode/register.go
generated
vendored
Normal file
138
vendor/github.com/docker/distribution/registry/api/errcode/register.go
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package errcode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{}
|
||||||
|
idToDescriptors = map[string]ErrorDescriptor{}
|
||||||
|
groupToDescriptors = map[string][]ErrorDescriptor{}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrorCodeUnknown is a generic error that can be used as a last
|
||||||
|
// resort if there is no situation-specific error message that can be used
|
||||||
|
ErrorCodeUnknown = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNKNOWN",
|
||||||
|
Message: "unknown error",
|
||||||
|
Description: `Generic error returned when the error does not have an
|
||||||
|
API classification.`,
|
||||||
|
HTTPStatusCode: http.StatusInternalServerError,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeUnsupported is returned when an operation is not supported.
|
||||||
|
ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNSUPPORTED",
|
||||||
|
Message: "The operation is unsupported.",
|
||||||
|
Description: `The operation was unsupported due to a missing
|
||||||
|
implementation or invalid set of parameters.`,
|
||||||
|
HTTPStatusCode: http.StatusMethodNotAllowed,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeUnauthorized is returned if a request requires
|
||||||
|
// authentication.
|
||||||
|
ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNAUTHORIZED",
|
||||||
|
Message: "authentication required",
|
||||||
|
Description: `The access controller was unable to authenticate
|
||||||
|
the client. Often this will be accompanied by a
|
||||||
|
Www-Authenticate HTTP response header indicating how to
|
||||||
|
authenticate.`,
|
||||||
|
HTTPStatusCode: http.StatusUnauthorized,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeDenied is returned if a client does not have sufficient
|
||||||
|
// permission to perform an action.
|
||||||
|
ErrorCodeDenied = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "DENIED",
|
||||||
|
Message: "requested access to the resource is denied",
|
||||||
|
Description: `The access controller denied access for the
|
||||||
|
operation on a resource.`,
|
||||||
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeUnavailable provides a common error to report unavailability
|
||||||
|
// of a service or endpoint.
|
||||||
|
ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "UNAVAILABLE",
|
||||||
|
Message: "service unavailable",
|
||||||
|
Description: "Returned when a service is not available",
|
||||||
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeTooManyRequests is returned if a client attempts too many
|
||||||
|
// times to contact a service endpoint.
|
||||||
|
ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{
|
||||||
|
Value: "TOOMANYREQUESTS",
|
||||||
|
Message: "too many requests",
|
||||||
|
Description: `Returned when a client attempts to contact a
|
||||||
|
service too many times`,
|
||||||
|
HTTPStatusCode: http.StatusTooManyRequests,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
var nextCode = 1000
|
||||||
|
var registerLock sync.Mutex
|
||||||
|
|
||||||
|
// Register will make the passed-in error known to the environment and
|
||||||
|
// return a new ErrorCode
|
||||||
|
func Register(group string, descriptor ErrorDescriptor) ErrorCode {
|
||||||
|
registerLock.Lock()
|
||||||
|
defer registerLock.Unlock()
|
||||||
|
|
||||||
|
descriptor.Code = ErrorCode(nextCode)
|
||||||
|
|
||||||
|
if _, ok := idToDescriptors[descriptor.Value]; ok {
|
||||||
|
panic(fmt.Sprintf("ErrorValue %q is already registered", descriptor.Value))
|
||||||
|
}
|
||||||
|
if _, ok := errorCodeToDescriptors[descriptor.Code]; ok {
|
||||||
|
panic(fmt.Sprintf("ErrorCode %v is already registered", descriptor.Code))
|
||||||
|
}
|
||||||
|
|
||||||
|
groupToDescriptors[group] = append(groupToDescriptors[group], descriptor)
|
||||||
|
errorCodeToDescriptors[descriptor.Code] = descriptor
|
||||||
|
idToDescriptors[descriptor.Value] = descriptor
|
||||||
|
|
||||||
|
nextCode++
|
||||||
|
return descriptor.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
type byValue []ErrorDescriptor
|
||||||
|
|
||||||
|
func (a byValue) Len() int { return len(a) }
|
||||||
|
func (a byValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a byValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||||
|
|
||||||
|
// GetGroupNames returns the list of Error group names that are registered
|
||||||
|
func GetGroupNames() []string {
|
||||||
|
keys := []string{}
|
||||||
|
|
||||||
|
for k := range groupToDescriptors {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetErrorCodeGroup returns the named group of error descriptors
|
||||||
|
func GetErrorCodeGroup(name string) []ErrorDescriptor {
|
||||||
|
desc := groupToDescriptors[name]
|
||||||
|
sort.Sort(byValue(desc))
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetErrorAllDescriptors returns a slice of all ErrorDescriptors that are
|
||||||
|
// registered, irrespective of what group they're in
|
||||||
|
func GetErrorAllDescriptors() []ErrorDescriptor {
|
||||||
|
result := []ErrorDescriptor{}
|
||||||
|
|
||||||
|
for _, group := range GetGroupNames() {
|
||||||
|
result = append(result, GetErrorCodeGroup(group)...)
|
||||||
|
}
|
||||||
|
sort.Sort(byValue(result))
|
||||||
|
return result
|
||||||
|
}
|
1596
vendor/github.com/docker/distribution/registry/api/v2/descriptors.go
generated
vendored
Normal file
1596
vendor/github.com/docker/distribution/registry/api/v2/descriptors.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
9
vendor/github.com/docker/distribution/registry/api/v2/doc.go
generated
vendored
Normal file
9
vendor/github.com/docker/distribution/registry/api/v2/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// Package v2 describes routes, urls and the error codes used in the Docker
|
||||||
|
// Registry JSON HTTP API V2. In addition to declarations, descriptors are
|
||||||
|
// provided for routes and error codes that can be used for implementation and
|
||||||
|
// automatically generating documentation.
|
||||||
|
//
|
||||||
|
// Definitions here are considered to be locked down for the V2 registry api.
|
||||||
|
// Any changes must be considered carefully and should not proceed without a
|
||||||
|
// change proposal in docker core.
|
||||||
|
package v2
|
136
vendor/github.com/docker/distribution/registry/api/v2/errors.go
generated
vendored
Normal file
136
vendor/github.com/docker/distribution/registry/api/v2/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
const errGroup = "registry.api.v2"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrorCodeDigestInvalid is returned when uploading a blob if the
|
||||||
|
// provided digest does not match the blob contents.
|
||||||
|
ErrorCodeDigestInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "DIGEST_INVALID",
|
||||||
|
Message: "provided digest did not match uploaded content",
|
||||||
|
Description: `When a blob is uploaded, the registry will check that
|
||||||
|
the content matches the digest provided by the client. The error may
|
||||||
|
include a detail structure with the key "digest", including the
|
||||||
|
invalid digest string. This error may also be returned when a manifest
|
||||||
|
includes an invalid layer digest.`,
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeSizeInvalid is returned when uploading a blob if the provided
|
||||||
|
ErrorCodeSizeInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "SIZE_INVALID",
|
||||||
|
Message: "provided length did not match content length",
|
||||||
|
Description: `When a layer is uploaded, the provided size will be
|
||||||
|
checked against the uploaded content. If they do not match, this error
|
||||||
|
will be returned.`,
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeNameInvalid is returned when the name in the manifest does not
|
||||||
|
// match the provided name.
|
||||||
|
ErrorCodeNameInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "NAME_INVALID",
|
||||||
|
Message: "invalid repository name",
|
||||||
|
Description: `Invalid repository name encountered either during
|
||||||
|
manifest validation or any API operation.`,
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeTagInvalid is returned when the tag in the manifest does not
|
||||||
|
// match the provided tag.
|
||||||
|
ErrorCodeTagInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "TAG_INVALID",
|
||||||
|
Message: "manifest tag did not match URI",
|
||||||
|
Description: `During a manifest upload, if the tag in the manifest
|
||||||
|
does not match the uri tag, this error will be returned.`,
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeNameUnknown when the repository name is not known.
|
||||||
|
ErrorCodeNameUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "NAME_UNKNOWN",
|
||||||
|
Message: "repository name not known to registry",
|
||||||
|
Description: `This is returned if the name used during an operation is
|
||||||
|
unknown to the registry.`,
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeManifestUnknown returned when image manifest is unknown.
|
||||||
|
ErrorCodeManifestUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "MANIFEST_UNKNOWN",
|
||||||
|
Message: "manifest unknown",
|
||||||
|
Description: `This error is returned when the manifest, identified by
|
||||||
|
name and tag is unknown to the repository.`,
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeManifestInvalid returned when an image manifest is invalid,
|
||||||
|
// typically during a PUT operation. This error encompasses all errors
|
||||||
|
// encountered during manifest validation that aren't signature errors.
|
||||||
|
ErrorCodeManifestInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "MANIFEST_INVALID",
|
||||||
|
Message: "manifest invalid",
|
||||||
|
Description: `During upload, manifests undergo several checks ensuring
|
||||||
|
validity. If those checks fail, this error may be returned, unless a
|
||||||
|
more specific error is included. The detail will contain information
|
||||||
|
the failed validation.`,
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeManifestUnverified is returned when the manifest fails
|
||||||
|
// signature verification.
|
||||||
|
ErrorCodeManifestUnverified = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "MANIFEST_UNVERIFIED",
|
||||||
|
Message: "manifest failed signature verification",
|
||||||
|
Description: `During manifest upload, if the manifest fails signature
|
||||||
|
verification, this error will be returned.`,
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeManifestBlobUnknown is returned when a manifest blob is
|
||||||
|
// unknown to the registry.
|
||||||
|
ErrorCodeManifestBlobUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "MANIFEST_BLOB_UNKNOWN",
|
||||||
|
Message: "blob unknown to registry",
|
||||||
|
Description: `This error may be returned when a manifest blob is
|
||||||
|
unknown to the registry.`,
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeBlobUnknown is returned when a blob is unknown to the
|
||||||
|
// registry. This can happen when the manifest references a nonexistent
|
||||||
|
// layer or the result is not found by a blob fetch.
|
||||||
|
ErrorCodeBlobUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "BLOB_UNKNOWN",
|
||||||
|
Message: "blob unknown to registry",
|
||||||
|
Description: `This error may be returned when a blob is unknown to the
|
||||||
|
registry in a specified repository. This can be returned with a
|
||||||
|
standard get or if a manifest references an unknown layer during
|
||||||
|
upload.`,
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeBlobUploadUnknown is returned when an upload is unknown.
|
||||||
|
ErrorCodeBlobUploadUnknown = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "BLOB_UPLOAD_UNKNOWN",
|
||||||
|
Message: "blob upload unknown to registry",
|
||||||
|
Description: `If a blob upload has been cancelled or was never
|
||||||
|
started, this error code may be returned.`,
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ErrorCodeBlobUploadInvalid is returned when an upload is invalid.
|
||||||
|
ErrorCodeBlobUploadInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
|
Value: "BLOB_UPLOAD_INVALID",
|
||||||
|
Message: "blob upload invalid",
|
||||||
|
Description: `The blob upload encountered an error and can no
|
||||||
|
longer proceed.`,
|
||||||
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
|
})
|
||||||
|
)
|
161
vendor/github.com/docker/distribution/registry/api/v2/headerparser.go
generated
vendored
Normal file
161
vendor/github.com/docker/distribution/registry/api/v2/headerparser.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// according to rfc7230
|
||||||
|
reToken = regexp.MustCompile(`^[^"(),/:;<=>?@[\]{}[:space:][:cntrl:]]+`)
|
||||||
|
reQuotedValue = regexp.MustCompile(`^[^\\"]+`)
|
||||||
|
reEscapedCharacter = regexp.MustCompile(`^[[:blank:][:graph:]]`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseForwardedHeader is a benevolent parser of Forwarded header defined in rfc7239. The header contains
|
||||||
|
// a comma-separated list of forwarding key-value pairs. Each list element is set by single proxy. The
|
||||||
|
// function parses only the first element of the list, which is set by the very first proxy. It returns a map
|
||||||
|
// of corresponding key-value pairs and an unparsed slice of the input string.
|
||||||
|
//
|
||||||
|
// Examples of Forwarded header values:
|
||||||
|
//
|
||||||
|
// 1. Forwarded: For=192.0.2.43; Proto=https,For="[2001:db8:cafe::17]",For=unknown
|
||||||
|
// 2. Forwarded: for="192.0.2.43:443"; host="registry.example.org", for="10.10.05.40:80"
|
||||||
|
//
|
||||||
|
// The first will be parsed into {"for": "192.0.2.43", "proto": "https"} while the second into
|
||||||
|
// {"for": "192.0.2.43:443", "host": "registry.example.org"}.
|
||||||
|
func parseForwardedHeader(forwarded string) (map[string]string, string, error) {
|
||||||
|
// Following are states of forwarded header parser. Any state could transition to a failure.
|
||||||
|
const (
|
||||||
|
// terminating state; can transition to Parameter
|
||||||
|
stateElement = iota
|
||||||
|
// terminating state; can transition to KeyValueDelimiter
|
||||||
|
stateParameter
|
||||||
|
// can transition to Value
|
||||||
|
stateKeyValueDelimiter
|
||||||
|
// can transition to one of { QuotedValue, PairEnd }
|
||||||
|
stateValue
|
||||||
|
// can transition to one of { EscapedCharacter, PairEnd }
|
||||||
|
stateQuotedValue
|
||||||
|
// can transition to one of { QuotedValue }
|
||||||
|
stateEscapedCharacter
|
||||||
|
// terminating state; can transition to one of { Parameter, Element }
|
||||||
|
statePairEnd
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
parameter string
|
||||||
|
value string
|
||||||
|
parse = forwarded[:]
|
||||||
|
res = map[string]string{}
|
||||||
|
state = stateElement
|
||||||
|
)
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
// skip spaces unless in quoted value
|
||||||
|
if state != stateQuotedValue && state != stateEscapedCharacter {
|
||||||
|
parse = strings.TrimLeftFunc(parse, unicode.IsSpace)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parse) == 0 {
|
||||||
|
if state != stateElement && state != statePairEnd && state != stateParameter {
|
||||||
|
return nil, parse, fmt.Errorf("unexpected end of input")
|
||||||
|
}
|
||||||
|
// terminating
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
// terminate at list element delimiter
|
||||||
|
case stateElement:
|
||||||
|
if parse[0] == ',' {
|
||||||
|
parse = parse[1:]
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
state = stateParameter
|
||||||
|
|
||||||
|
// parse parameter (the key of key-value pair)
|
||||||
|
case stateParameter:
|
||||||
|
match := reToken.FindString(parse)
|
||||||
|
if len(match) == 0 {
|
||||||
|
return nil, parse, fmt.Errorf("failed to parse token at position %d", len(forwarded)-len(parse))
|
||||||
|
}
|
||||||
|
parameter = strings.ToLower(match)
|
||||||
|
parse = parse[len(match):]
|
||||||
|
state = stateKeyValueDelimiter
|
||||||
|
|
||||||
|
// parse '='
|
||||||
|
case stateKeyValueDelimiter:
|
||||||
|
if parse[0] != '=' {
|
||||||
|
return nil, parse, fmt.Errorf("expected '=', not '%c' at position %d", parse[0], len(forwarded)-len(parse))
|
||||||
|
}
|
||||||
|
parse = parse[1:]
|
||||||
|
state = stateValue
|
||||||
|
|
||||||
|
// parse value or quoted value
|
||||||
|
case stateValue:
|
||||||
|
if parse[0] == '"' {
|
||||||
|
parse = parse[1:]
|
||||||
|
state = stateQuotedValue
|
||||||
|
} else {
|
||||||
|
value = reToken.FindString(parse)
|
||||||
|
if len(value) == 0 {
|
||||||
|
return nil, parse, fmt.Errorf("failed to parse value at position %d", len(forwarded)-len(parse))
|
||||||
|
}
|
||||||
|
if _, exists := res[parameter]; exists {
|
||||||
|
return nil, parse, fmt.Errorf("duplicate parameter %q at position %d", parameter, len(forwarded)-len(parse))
|
||||||
|
}
|
||||||
|
res[parameter] = value
|
||||||
|
parse = parse[len(value):]
|
||||||
|
value = ""
|
||||||
|
state = statePairEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse a part of quoted value until the first backslash
|
||||||
|
case stateQuotedValue:
|
||||||
|
match := reQuotedValue.FindString(parse)
|
||||||
|
value += match
|
||||||
|
parse = parse[len(match):]
|
||||||
|
switch {
|
||||||
|
case len(parse) == 0:
|
||||||
|
return nil, parse, fmt.Errorf("unterminated quoted string")
|
||||||
|
case parse[0] == '"':
|
||||||
|
res[parameter] = value
|
||||||
|
value = ""
|
||||||
|
parse = parse[1:]
|
||||||
|
state = statePairEnd
|
||||||
|
case parse[0] == '\\':
|
||||||
|
parse = parse[1:]
|
||||||
|
state = stateEscapedCharacter
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse escaped character in a quoted string, ignore the backslash
|
||||||
|
// transition back to QuotedValue state
|
||||||
|
case stateEscapedCharacter:
|
||||||
|
c := reEscapedCharacter.FindString(parse)
|
||||||
|
if len(c) == 0 {
|
||||||
|
return nil, parse, fmt.Errorf("invalid escape sequence at position %d", len(forwarded)-len(parse)-1)
|
||||||
|
}
|
||||||
|
value += c
|
||||||
|
parse = parse[1:]
|
||||||
|
state = stateQuotedValue
|
||||||
|
|
||||||
|
// expect either a new key-value pair, new list or end of input
|
||||||
|
case statePairEnd:
|
||||||
|
switch parse[0] {
|
||||||
|
case ';':
|
||||||
|
parse = parse[1:]
|
||||||
|
state = stateParameter
|
||||||
|
case ',':
|
||||||
|
state = stateElement
|
||||||
|
default:
|
||||||
|
return nil, parse, fmt.Errorf("expected ',' or ';', not %c at position %d", parse[0], len(forwarded)-len(parse))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, parse, nil
|
||||||
|
}
|
49
vendor/github.com/docker/distribution/registry/api/v2/routes.go
generated
vendored
Normal file
49
vendor/github.com/docker/distribution/registry/api/v2/routes.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import "github.com/gorilla/mux"
|
||||||
|
|
||||||
|
// The following are definitions of the name under which all V2 routes are
|
||||||
|
// registered. These symbols can be used to look up a route based on the name.
|
||||||
|
const (
|
||||||
|
RouteNameBase = "base"
|
||||||
|
RouteNameManifest = "manifest"
|
||||||
|
RouteNameTags = "tags"
|
||||||
|
RouteNameBlob = "blob"
|
||||||
|
RouteNameBlobUpload = "blob-upload"
|
||||||
|
RouteNameBlobUploadChunk = "blob-upload-chunk"
|
||||||
|
RouteNameCatalog = "catalog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var allEndpoints = []string{
|
||||||
|
RouteNameManifest,
|
||||||
|
RouteNameCatalog,
|
||||||
|
RouteNameTags,
|
||||||
|
RouteNameBlob,
|
||||||
|
RouteNameBlobUpload,
|
||||||
|
RouteNameBlobUploadChunk,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Router builds a gorilla router with named routes for the various API
|
||||||
|
// methods. This can be used directly by both server implementations and
|
||||||
|
// clients.
|
||||||
|
func Router() *mux.Router {
|
||||||
|
return RouterWithPrefix("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouterWithPrefix builds a gorilla router with a configured prefix
|
||||||
|
// on all routes.
|
||||||
|
func RouterWithPrefix(prefix string) *mux.Router {
|
||||||
|
rootRouter := mux.NewRouter()
|
||||||
|
router := rootRouter
|
||||||
|
if prefix != "" {
|
||||||
|
router = router.PathPrefix(prefix).Subrouter()
|
||||||
|
}
|
||||||
|
|
||||||
|
router.StrictSlash(true)
|
||||||
|
|
||||||
|
for _, descriptor := range routeDescriptors {
|
||||||
|
router.Path(descriptor.Path).Name(descriptor.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootRouter
|
||||||
|
}
|
314
vendor/github.com/docker/distribution/registry/api/v2/urls.go
generated
vendored
Normal file
314
vendor/github.com/docker/distribution/registry/api/v2/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// URLBuilder creates registry API urls from a single base endpoint. It can be
|
||||||
|
// used to create urls for use in a registry client or server.
|
||||||
|
//
|
||||||
|
// All urls will be created from the given base, including the api version.
|
||||||
|
// For example, if a root of "/foo/" is provided, urls generated will be fall
|
||||||
|
// under "/foo/v2/...". Most application will only provide a schema, host and
|
||||||
|
// port, such as "https://localhost:5000/".
|
||||||
|
type URLBuilder struct {
|
||||||
|
root *url.URL // url root (ie http://localhost/)
|
||||||
|
router *mux.Router
|
||||||
|
relative bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewURLBuilder creates a URLBuilder with provided root url object.
|
||||||
|
func NewURLBuilder(root *url.URL, relative bool) *URLBuilder {
|
||||||
|
return &URLBuilder{
|
||||||
|
root: root,
|
||||||
|
router: Router(),
|
||||||
|
relative: relative,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewURLBuilderFromString workes identically to NewURLBuilder except it takes
|
||||||
|
// a string argument for the root, returning an error if it is not a valid
|
||||||
|
// url.
|
||||||
|
func NewURLBuilderFromString(root string, relative bool) (*URLBuilder, error) {
|
||||||
|
u, err := url.Parse(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewURLBuilder(u, relative), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewURLBuilderFromRequest uses information from an *http.Request to
|
||||||
|
// construct the root url.
|
||||||
|
func NewURLBuilderFromRequest(r *http.Request, relative bool) *URLBuilder {
|
||||||
|
var scheme string
|
||||||
|
|
||||||
|
forwardedProto := r.Header.Get("X-Forwarded-Proto")
|
||||||
|
// TODO: log the error
|
||||||
|
forwardedHeader, _, _ := parseForwardedHeader(r.Header.Get("Forwarded"))
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(forwardedProto) > 0:
|
||||||
|
scheme = forwardedProto
|
||||||
|
case len(forwardedHeader["proto"]) > 0:
|
||||||
|
scheme = forwardedHeader["proto"]
|
||||||
|
case r.TLS != nil:
|
||||||
|
scheme = "https"
|
||||||
|
case len(r.URL.Scheme) > 0:
|
||||||
|
scheme = r.URL.Scheme
|
||||||
|
default:
|
||||||
|
scheme = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
host := r.Host
|
||||||
|
|
||||||
|
if forwardedHost := r.Header.Get("X-Forwarded-Host"); len(forwardedHost) > 0 {
|
||||||
|
// According to the Apache mod_proxy docs, X-Forwarded-Host can be a
|
||||||
|
// comma-separated list of hosts, to which each proxy appends the
|
||||||
|
// requested host. We want to grab the first from this comma-separated
|
||||||
|
// list.
|
||||||
|
hosts := strings.SplitN(forwardedHost, ",", 2)
|
||||||
|
host = strings.TrimSpace(hosts[0])
|
||||||
|
} else if addr, exists := forwardedHeader["for"]; exists {
|
||||||
|
host = addr
|
||||||
|
} else if h, exists := forwardedHeader["host"]; exists {
|
||||||
|
host = h
|
||||||
|
}
|
||||||
|
|
||||||
|
portLessHost, port := host, ""
|
||||||
|
if !isIPv6Address(portLessHost) {
|
||||||
|
// with go 1.6, this would treat the last part of IPv6 address as a port
|
||||||
|
portLessHost, port, _ = net.SplitHostPort(host)
|
||||||
|
}
|
||||||
|
if forwardedPort := r.Header.Get("X-Forwarded-Port"); len(port) == 0 && len(forwardedPort) > 0 {
|
||||||
|
ports := strings.SplitN(forwardedPort, ",", 2)
|
||||||
|
forwardedPort = strings.TrimSpace(ports[0])
|
||||||
|
if _, err := strconv.ParseInt(forwardedPort, 10, 32); err == nil {
|
||||||
|
port = forwardedPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(portLessHost) > 0 {
|
||||||
|
host = portLessHost
|
||||||
|
}
|
||||||
|
if len(port) > 0 {
|
||||||
|
// remove enclosing brackets of ipv6 address otherwise they will be duplicated
|
||||||
|
if len(host) > 1 && host[0] == '[' && host[len(host)-1] == ']' {
|
||||||
|
host = host[1 : len(host)-1]
|
||||||
|
}
|
||||||
|
// JoinHostPort properly encloses ipv6 addresses in square brackets
|
||||||
|
host = net.JoinHostPort(host, port)
|
||||||
|
} else if isIPv6Address(host) && host[0] != '[' {
|
||||||
|
// ipv6 needs to be enclosed in square brackets in urls
|
||||||
|
host = "[" + host + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
basePath := routeDescriptorsMap[RouteNameBase].Path
|
||||||
|
|
||||||
|
requestPath := r.URL.Path
|
||||||
|
index := strings.Index(requestPath, basePath)
|
||||||
|
|
||||||
|
u := &url.URL{
|
||||||
|
Scheme: scheme,
|
||||||
|
Host: host,
|
||||||
|
}
|
||||||
|
|
||||||
|
if index > 0 {
|
||||||
|
// N.B. index+1 is important because we want to include the trailing /
|
||||||
|
u.Path = requestPath[0 : index+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewURLBuilder(u, relative)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildBaseURL constructs a base url for the API, typically just "/v2/".
|
||||||
|
func (ub *URLBuilder) BuildBaseURL() (string, error) {
|
||||||
|
route := ub.cloneRoute(RouteNameBase)
|
||||||
|
|
||||||
|
baseURL, err := route.URL()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildCatalogURL constructs a url get a catalog of repositories
|
||||||
|
func (ub *URLBuilder) BuildCatalogURL(values ...url.Values) (string, error) {
|
||||||
|
route := ub.cloneRoute(RouteNameCatalog)
|
||||||
|
|
||||||
|
catalogURL, err := route.URL()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendValuesURL(catalogURL, values...).String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildTagsURL constructs a url to list the tags in the named repository.
|
||||||
|
func (ub *URLBuilder) BuildTagsURL(name reference.Named) (string, error) {
|
||||||
|
route := ub.cloneRoute(RouteNameTags)
|
||||||
|
|
||||||
|
tagsURL, err := route.URL("name", name.Name())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagsURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildManifestURL constructs a url for the manifest identified by name and
|
||||||
|
// reference. The argument reference may be either a tag or digest.
|
||||||
|
func (ub *URLBuilder) BuildManifestURL(ref reference.Named) (string, error) {
|
||||||
|
route := ub.cloneRoute(RouteNameManifest)
|
||||||
|
|
||||||
|
tagOrDigest := ""
|
||||||
|
switch v := ref.(type) {
|
||||||
|
case reference.Tagged:
|
||||||
|
tagOrDigest = v.Tag()
|
||||||
|
case reference.Digested:
|
||||||
|
tagOrDigest = v.Digest().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestURL, err := route.URL("name", ref.Name(), "reference", tagOrDigest)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifestURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildBlobURL constructs the url for the blob identified by name and dgst.
|
||||||
|
func (ub *URLBuilder) BuildBlobURL(ref reference.Canonical) (string, error) {
|
||||||
|
route := ub.cloneRoute(RouteNameBlob)
|
||||||
|
|
||||||
|
layerURL, err := route.URL("name", ref.Name(), "digest", ref.Digest().String())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildBlobUploadURL constructs a url to begin a blob upload in the
|
||||||
|
// repository identified by name.
|
||||||
|
func (ub *URLBuilder) BuildBlobUploadURL(name reference.Named, values ...url.Values) (string, error) {
|
||||||
|
route := ub.cloneRoute(RouteNameBlobUpload)
|
||||||
|
|
||||||
|
uploadURL, err := route.URL("name", name.Name())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendValuesURL(uploadURL, values...).String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildBlobUploadChunkURL constructs a url for the upload identified by uuid,
|
||||||
|
// including any url values. This should generally not be used by clients, as
|
||||||
|
// this url is provided by server implementations during the blob upload
|
||||||
|
// process.
|
||||||
|
func (ub *URLBuilder) BuildBlobUploadChunkURL(name reference.Named, uuid string, values ...url.Values) (string, error) {
|
||||||
|
route := ub.cloneRoute(RouteNameBlobUploadChunk)
|
||||||
|
|
||||||
|
uploadURL, err := route.URL("name", name.Name(), "uuid", uuid)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendValuesURL(uploadURL, values...).String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clondedRoute returns a clone of the named route from the router. Routes
|
||||||
|
// must be cloned to avoid modifying them during url generation.
|
||||||
|
func (ub *URLBuilder) cloneRoute(name string) clonedRoute {
|
||||||
|
route := new(mux.Route)
|
||||||
|
root := new(url.URL)
|
||||||
|
|
||||||
|
*route = *ub.router.GetRoute(name) // clone the route
|
||||||
|
*root = *ub.root
|
||||||
|
|
||||||
|
return clonedRoute{Route: route, root: root, relative: ub.relative}
|
||||||
|
}
|
||||||
|
|
||||||
|
type clonedRoute struct {
|
||||||
|
*mux.Route
|
||||||
|
root *url.URL
|
||||||
|
relative bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr clonedRoute) URL(pairs ...string) (*url.URL, error) {
|
||||||
|
routeURL, err := cr.Route.URL(pairs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cr.relative {
|
||||||
|
return routeURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if routeURL.Scheme == "" && routeURL.User == nil && routeURL.Host == "" {
|
||||||
|
routeURL.Path = routeURL.Path[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
url := cr.root.ResolveReference(routeURL)
|
||||||
|
url.Scheme = cr.root.Scheme
|
||||||
|
return url, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendValuesURL appends the parameters to the url.
|
||||||
|
func appendValuesURL(u *url.URL, values ...url.Values) *url.URL {
|
||||||
|
merged := u.Query()
|
||||||
|
|
||||||
|
for _, v := range values {
|
||||||
|
for k, vv := range v {
|
||||||
|
merged[k] = append(merged[k], vv...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u.RawQuery = merged.Encode()
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendValues appends the parameters to the url. Panics if the string is not
|
||||||
|
// a url.
|
||||||
|
func appendValues(u string, values ...url.Values) string {
|
||||||
|
up, err := url.Parse(u)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // should never happen
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendValuesURL(up, values...).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isIPv6Address returns true if given string is a valid IPv6 address. No port is allowed. The address may be
|
||||||
|
// enclosed in square brackets.
|
||||||
|
func isIPv6Address(host string) bool {
|
||||||
|
if len(host) > 1 && host[0] == '[' && host[len(host)-1] == ']' {
|
||||||
|
host = host[1 : len(host)-1]
|
||||||
|
}
|
||||||
|
// The IPv6 scoped addressing zone identifier starts after the last percent sign.
|
||||||
|
if i := strings.LastIndexByte(host, '%'); i > 0 {
|
||||||
|
host = host[:i]
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ip.To16() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ip.To4() == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// dot can be present in ipv4-mapped address, it needs to come after a colon though
|
||||||
|
i := strings.IndexAny(host, ":.")
|
||||||
|
return i >= 0 && host[i] == ':'
|
||||||
|
}
|
58
vendor/github.com/docker/distribution/registry/client/auth/api_version.go
generated
vendored
Normal file
58
vendor/github.com/docker/distribution/registry/client/auth/api_version.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIVersion represents a version of an API including its
|
||||||
|
// type and version number.
|
||||||
|
type APIVersion struct {
|
||||||
|
// Type refers to the name of a specific API specification
|
||||||
|
// such as "registry"
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// Version is the version of the API specification implemented,
|
||||||
|
// This may omit the revision number and only include
|
||||||
|
// the major and minor version, such as "2.0"
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string formatted API Version
|
||||||
|
func (v APIVersion) String() string {
|
||||||
|
return v.Type + "/" + v.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIVersions gets the API versions out of an HTTP response using the provided
|
||||||
|
// version header as the key for the HTTP header.
|
||||||
|
func APIVersions(resp *http.Response, versionHeader string) []APIVersion {
|
||||||
|
versions := []APIVersion{}
|
||||||
|
if versionHeader != "" {
|
||||||
|
for _, supportedVersions := range resp.Header[http.CanonicalHeaderKey(versionHeader)] {
|
||||||
|
for _, version := range strings.Fields(supportedVersions) {
|
||||||
|
versions = append(versions, ParseAPIVersion(version))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return versions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAPIVersion parses an API version string into an APIVersion
|
||||||
|
// Format (Expected, not enforced):
|
||||||
|
// API version string = <API type> '/' <API version>
|
||||||
|
// API type = [a-z][a-z0-9]*
|
||||||
|
// API version = [0-9]+(\.[0-9]+)?
|
||||||
|
// TODO(dmcgowan): Enforce format, add error condition, remove unknown type
|
||||||
|
func ParseAPIVersion(versionStr string) APIVersion {
|
||||||
|
idx := strings.IndexRune(versionStr, '/')
|
||||||
|
if idx == -1 {
|
||||||
|
return APIVersion{
|
||||||
|
Type: "unknown",
|
||||||
|
Version: versionStr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return APIVersion{
|
||||||
|
Type: strings.ToLower(versionStr[:idx]),
|
||||||
|
Version: versionStr[idx+1:],
|
||||||
|
}
|
||||||
|
}
|
27
vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go
generated
vendored
Normal file
27
vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package challenge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FROM: https://golang.org/src/net/http/http.go
|
||||||
|
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
|
||||||
|
// return true if the string includes a port.
|
||||||
|
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
|
||||||
|
|
||||||
|
// FROM: http://golang.org/src/net/http/transport.go
|
||||||
|
var portMap = map[string]string{
|
||||||
|
"http": "80",
|
||||||
|
"https": "443",
|
||||||
|
}
|
||||||
|
|
||||||
|
// canonicalAddr returns url.Host but always with a ":port" suffix
|
||||||
|
// FROM: http://golang.org/src/net/http/transport.go
|
||||||
|
func canonicalAddr(url *url.URL) string {
|
||||||
|
addr := url.Host
|
||||||
|
if !hasPort(addr) {
|
||||||
|
return addr + ":" + portMap[url.Scheme]
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
237
vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go
generated
vendored
Normal file
237
vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go
generated
vendored
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
package challenge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Challenge carries information from a WWW-Authenticate response header.
|
||||||
|
// See RFC 2617.
|
||||||
|
type Challenge struct {
|
||||||
|
// Scheme is the auth-scheme according to RFC 2617
|
||||||
|
Scheme string
|
||||||
|
|
||||||
|
// Parameters are the auth-params according to RFC 2617
|
||||||
|
Parameters map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manager manages the challenges for endpoints.
|
||||||
|
// The challenges are pulled out of HTTP responses. Only
|
||||||
|
// responses which expect challenges should be added to
|
||||||
|
// the manager, since a non-unauthorized request will be
|
||||||
|
// viewed as not requiring challenges.
|
||||||
|
type Manager interface {
|
||||||
|
// GetChallenges returns the challenges for the given
|
||||||
|
// endpoint URL.
|
||||||
|
GetChallenges(endpoint url.URL) ([]Challenge, error)
|
||||||
|
|
||||||
|
// AddResponse adds the response to the challenge
|
||||||
|
// manager. The challenges will be parsed out of
|
||||||
|
// the WWW-Authenicate headers and added to the
|
||||||
|
// URL which was produced the response. If the
|
||||||
|
// response was authorized, any challenges for the
|
||||||
|
// endpoint will be cleared.
|
||||||
|
AddResponse(resp *http.Response) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSimpleManager returns an instance of
|
||||||
|
// Manger which only maps endpoints to challenges
|
||||||
|
// based on the responses which have been added the
|
||||||
|
// manager. The simple manager will make no attempt to
|
||||||
|
// perform requests on the endpoints or cache the responses
|
||||||
|
// to a backend.
|
||||||
|
func NewSimpleManager() Manager {
|
||||||
|
return &simpleManager{
|
||||||
|
Challanges: make(map[string][]Challenge),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleManager struct {
|
||||||
|
sync.RWMutex
|
||||||
|
Challanges map[string][]Challenge
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeURL(endpoint *url.URL) {
|
||||||
|
endpoint.Host = strings.ToLower(endpoint.Host)
|
||||||
|
endpoint.Host = canonicalAddr(endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *simpleManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
|
||||||
|
normalizeURL(&endpoint)
|
||||||
|
|
||||||
|
m.RLock()
|
||||||
|
defer m.RUnlock()
|
||||||
|
challenges := m.Challanges[endpoint.String()]
|
||||||
|
return challenges, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *simpleManager) AddResponse(resp *http.Response) error {
|
||||||
|
challenges := ResponseChallenges(resp)
|
||||||
|
if resp.Request == nil {
|
||||||
|
return fmt.Errorf("missing request reference")
|
||||||
|
}
|
||||||
|
urlCopy := url.URL{
|
||||||
|
Path: resp.Request.URL.Path,
|
||||||
|
Host: resp.Request.URL.Host,
|
||||||
|
Scheme: resp.Request.URL.Scheme,
|
||||||
|
}
|
||||||
|
normalizeURL(&urlCopy)
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
m.Challanges[urlCopy.String()] = challenges
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Octet types from RFC 2616.
|
||||||
|
type octetType byte
|
||||||
|
|
||||||
|
var octetTypes [256]octetType
|
||||||
|
|
||||||
|
const (
|
||||||
|
isToken octetType = 1 << iota
|
||||||
|
isSpace
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// OCTET = <any 8-bit sequence of data>
|
||||||
|
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||||
|
// CR = <US-ASCII CR, carriage return (13)>
|
||||||
|
// LF = <US-ASCII LF, linefeed (10)>
|
||||||
|
// SP = <US-ASCII SP, space (32)>
|
||||||
|
// HT = <US-ASCII HT, horizontal-tab (9)>
|
||||||
|
// <"> = <US-ASCII double-quote mark (34)>
|
||||||
|
// CRLF = CR LF
|
||||||
|
// LWS = [CRLF] 1*( SP | HT )
|
||||||
|
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
|
||||||
|
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
|
||||||
|
// token = 1*<any CHAR except CTLs or separators>
|
||||||
|
// qdtext = <any TEXT except <">>
|
||||||
|
|
||||||
|
for c := 0; c < 256; c++ {
|
||||||
|
var t octetType
|
||||||
|
isCtl := c <= 31 || c == 127
|
||||||
|
isChar := 0 <= c && c <= 127
|
||||||
|
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
|
||||||
|
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
|
||||||
|
t |= isSpace
|
||||||
|
}
|
||||||
|
if isChar && !isCtl && !isSeparator {
|
||||||
|
t |= isToken
|
||||||
|
}
|
||||||
|
octetTypes[c] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseChallenges returns a list of authorization challenges
|
||||||
|
// for the given http Response. Challenges are only checked if
|
||||||
|
// the response status code was a 401.
|
||||||
|
func ResponseChallenges(resp *http.Response) []Challenge {
|
||||||
|
if resp.StatusCode == http.StatusUnauthorized {
|
||||||
|
// Parse the WWW-Authenticate Header and store the challenges
|
||||||
|
// on this endpoint object.
|
||||||
|
return parseAuthHeader(resp.Header)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAuthHeader(header http.Header) []Challenge {
|
||||||
|
challenges := []Challenge{}
|
||||||
|
for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] {
|
||||||
|
v, p := parseValueAndParams(h)
|
||||||
|
if v != "" {
|
||||||
|
challenges = append(challenges, Challenge{Scheme: v, Parameters: p})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return challenges
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseValueAndParams(header string) (value string, params map[string]string) {
|
||||||
|
params = make(map[string]string)
|
||||||
|
value, s := expectToken(header)
|
||||||
|
if value == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
value = strings.ToLower(value)
|
||||||
|
s = "," + skipSpace(s)
|
||||||
|
for strings.HasPrefix(s, ",") {
|
||||||
|
var pkey string
|
||||||
|
pkey, s = expectToken(skipSpace(s[1:]))
|
||||||
|
if pkey == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(s, "=") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pvalue string
|
||||||
|
pvalue, s = expectTokenOrQuoted(s[1:])
|
||||||
|
if pvalue == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pkey = strings.ToLower(pkey)
|
||||||
|
params[pkey] = pvalue
|
||||||
|
s = skipSpace(s)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipSpace(s string) (rest string) {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if octetTypes[s[i]]&isSpace == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectToken(s string) (token, rest string) {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(s); i++ {
|
||||||
|
if octetTypes[s[i]]&isToken == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s[:i], s[i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectTokenOrQuoted(s string) (value string, rest string) {
|
||||||
|
if !strings.HasPrefix(s, "\"") {
|
||||||
|
return expectToken(s)
|
||||||
|
}
|
||||||
|
s = s[1:]
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '"':
|
||||||
|
return s[:i], s[i+1:]
|
||||||
|
case '\\':
|
||||||
|
p := make([]byte, len(s)-1)
|
||||||
|
j := copy(p, s[:i])
|
||||||
|
escape := true
|
||||||
|
for i = i + 1; i < len(s); i++ {
|
||||||
|
b := s[i]
|
||||||
|
switch {
|
||||||
|
case escape:
|
||||||
|
escape = false
|
||||||
|
p[j] = b
|
||||||
|
j++
|
||||||
|
case b == '\\':
|
||||||
|
escape = true
|
||||||
|
case b == '"':
|
||||||
|
return string(p[:j]), s[i+1:]
|
||||||
|
default:
|
||||||
|
p[j] = b
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", ""
|
||||||
|
}
|
503
vendor/github.com/docker/distribution/registry/client/auth/session.go
generated
vendored
Normal file
503
vendor/github.com/docker/distribution/registry/client/auth/session.go
generated
vendored
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/distribution/registry/client"
|
||||||
|
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||||
|
"github.com/docker/distribution/registry/client/transport"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNoBasicAuthCredentials is returned if a request can't be authorized with
|
||||||
|
// basic auth due to lack of credentials.
|
||||||
|
ErrNoBasicAuthCredentials = errors.New("no basic auth credentials")
|
||||||
|
|
||||||
|
// ErrNoToken is returned if a request is successful but the body does not
|
||||||
|
// contain an authorization token.
|
||||||
|
ErrNoToken = errors.New("authorization server did not include a token in the response")
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultClientID = "registry-client"
|
||||||
|
|
||||||
|
// AuthenticationHandler is an interface for authorizing a request from
|
||||||
|
// params from a "WWW-Authenicate" header for a single scheme.
|
||||||
|
type AuthenticationHandler interface {
|
||||||
|
// Scheme returns the scheme as expected from the "WWW-Authenicate" header.
|
||||||
|
Scheme() string
|
||||||
|
|
||||||
|
// AuthorizeRequest adds the authorization header to a request (if needed)
|
||||||
|
// using the parameters from "WWW-Authenticate" method. The parameters
|
||||||
|
// values depend on the scheme.
|
||||||
|
AuthorizeRequest(req *http.Request, params map[string]string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredentialStore is an interface for getting credentials for
|
||||||
|
// a given URL
|
||||||
|
type CredentialStore interface {
|
||||||
|
// Basic returns basic auth for the given URL
|
||||||
|
Basic(*url.URL) (string, string)
|
||||||
|
|
||||||
|
// RefreshToken returns a refresh token for the
|
||||||
|
// given URL and service
|
||||||
|
RefreshToken(*url.URL, string) string
|
||||||
|
|
||||||
|
// SetRefreshToken sets the refresh token if none
|
||||||
|
// is provided for the given url and service
|
||||||
|
SetRefreshToken(realm *url.URL, service, token string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthorizer creates an authorizer which can handle multiple authentication
|
||||||
|
// schemes. The handlers are tried in order, the higher priority authentication
|
||||||
|
// methods should be first. The challengeMap holds a list of challenges for
|
||||||
|
// a given root API endpoint (for example "https://registry-1.docker.io/v2/").
|
||||||
|
func NewAuthorizer(manager challenge.Manager, handlers ...AuthenticationHandler) transport.RequestModifier {
|
||||||
|
return &endpointAuthorizer{
|
||||||
|
challenges: manager,
|
||||||
|
handlers: handlers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type endpointAuthorizer struct {
|
||||||
|
challenges challenge.Manager
|
||||||
|
handlers []AuthenticationHandler
|
||||||
|
transport http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error {
|
||||||
|
pingPath := req.URL.Path
|
||||||
|
if v2Root := strings.Index(req.URL.Path, "/v2/"); v2Root != -1 {
|
||||||
|
pingPath = pingPath[:v2Root+4]
|
||||||
|
} else if v1Root := strings.Index(req.URL.Path, "/v1/"); v1Root != -1 {
|
||||||
|
pingPath = pingPath[:v1Root] + "/v2/"
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ping := url.URL{
|
||||||
|
Host: req.URL.Host,
|
||||||
|
Scheme: req.URL.Scheme,
|
||||||
|
Path: pingPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
challenges, err := ea.challenges.GetChallenges(ping)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(challenges) > 0 {
|
||||||
|
for _, handler := range ea.handlers {
|
||||||
|
for _, c := range challenges {
|
||||||
|
if c.Scheme != handler.Scheme() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := handler.AuthorizeRequest(req, c.Parameters); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the minimum duration a token can last (in seconds).
|
||||||
|
// A token must not live less than 60 seconds because older versions
|
||||||
|
// of the Docker client didn't read their expiration from the token
|
||||||
|
// response and assumed 60 seconds. So to remain compatible with
|
||||||
|
// those implementations, a token must live at least this long.
|
||||||
|
const minimumTokenLifetimeSeconds = 60
|
||||||
|
|
||||||
|
// Private interface for time used by this package to enable tests to provide their own implementation.
|
||||||
|
type clock interface {
|
||||||
|
Now() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenHandler struct {
|
||||||
|
header http.Header
|
||||||
|
creds CredentialStore
|
||||||
|
transport http.RoundTripper
|
||||||
|
clock clock
|
||||||
|
|
||||||
|
offlineAccess bool
|
||||||
|
forceOAuth bool
|
||||||
|
clientID string
|
||||||
|
scopes []Scope
|
||||||
|
|
||||||
|
tokenLock sync.Mutex
|
||||||
|
tokenCache string
|
||||||
|
tokenExpiration time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scope is a type which is serializable to a string
|
||||||
|
// using the allow scope grammar.
|
||||||
|
type Scope interface {
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepositoryScope represents a token scope for access
|
||||||
|
// to a repository.
|
||||||
|
type RepositoryScope struct {
|
||||||
|
Repository string
|
||||||
|
Class string
|
||||||
|
Actions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the repository
|
||||||
|
// using the scope grammar
|
||||||
|
func (rs RepositoryScope) String() string {
|
||||||
|
repoType := "repository"
|
||||||
|
if rs.Class != "" {
|
||||||
|
repoType = fmt.Sprintf("%s(%s)", repoType, rs.Class)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%s:%s", repoType, rs.Repository, strings.Join(rs.Actions, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryScope represents a token scope for access
|
||||||
|
// to resources in the registry.
|
||||||
|
type RegistryScope struct {
|
||||||
|
Name string
|
||||||
|
Actions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the user
|
||||||
|
// using the scope grammar
|
||||||
|
func (rs RegistryScope) String() string {
|
||||||
|
return fmt.Sprintf("registry:%s:%s", rs.Name, strings.Join(rs.Actions, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenHandlerOptions is used to configure a new token handler
|
||||||
|
type TokenHandlerOptions struct {
|
||||||
|
Transport http.RoundTripper
|
||||||
|
Credentials CredentialStore
|
||||||
|
|
||||||
|
OfflineAccess bool
|
||||||
|
ForceOAuth bool
|
||||||
|
ClientID string
|
||||||
|
Scopes []Scope
|
||||||
|
}
|
||||||
|
|
||||||
|
// An implementation of clock for providing real time data.
|
||||||
|
type realClock struct{}
|
||||||
|
|
||||||
|
// Now implements clock
|
||||||
|
func (realClock) Now() time.Time { return time.Now() }
|
||||||
|
|
||||||
|
// NewTokenHandler creates a new AuthenicationHandler which supports
|
||||||
|
// fetching tokens from a remote token server.
|
||||||
|
func NewTokenHandler(transport http.RoundTripper, creds CredentialStore, scope string, actions ...string) AuthenticationHandler {
|
||||||
|
// Create options...
|
||||||
|
return NewTokenHandlerWithOptions(TokenHandlerOptions{
|
||||||
|
Transport: transport,
|
||||||
|
Credentials: creds,
|
||||||
|
Scopes: []Scope{
|
||||||
|
RepositoryScope{
|
||||||
|
Repository: scope,
|
||||||
|
Actions: actions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTokenHandlerWithOptions creates a new token handler using the provided
|
||||||
|
// options structure.
|
||||||
|
func NewTokenHandlerWithOptions(options TokenHandlerOptions) AuthenticationHandler {
|
||||||
|
handler := &tokenHandler{
|
||||||
|
transport: options.Transport,
|
||||||
|
creds: options.Credentials,
|
||||||
|
offlineAccess: options.OfflineAccess,
|
||||||
|
forceOAuth: options.ForceOAuth,
|
||||||
|
clientID: options.ClientID,
|
||||||
|
scopes: options.Scopes,
|
||||||
|
clock: realClock{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *tokenHandler) client() *http.Client {
|
||||||
|
return &http.Client{
|
||||||
|
Transport: th.transport,
|
||||||
|
Timeout: 15 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *tokenHandler) Scheme() string {
|
||||||
|
return "bearer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *tokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
||||||
|
var additionalScopes []string
|
||||||
|
if fromParam := req.URL.Query().Get("from"); fromParam != "" {
|
||||||
|
additionalScopes = append(additionalScopes, RepositoryScope{
|
||||||
|
Repository: fromParam,
|
||||||
|
Actions: []string{"pull"},
|
||||||
|
}.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := th.getToken(params, additionalScopes...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *tokenHandler) getToken(params map[string]string, additionalScopes ...string) (string, error) {
|
||||||
|
th.tokenLock.Lock()
|
||||||
|
defer th.tokenLock.Unlock()
|
||||||
|
scopes := make([]string, 0, len(th.scopes)+len(additionalScopes))
|
||||||
|
for _, scope := range th.scopes {
|
||||||
|
scopes = append(scopes, scope.String())
|
||||||
|
}
|
||||||
|
var addedScopes bool
|
||||||
|
for _, scope := range additionalScopes {
|
||||||
|
scopes = append(scopes, scope)
|
||||||
|
addedScopes = true
|
||||||
|
}
|
||||||
|
|
||||||
|
now := th.clock.Now()
|
||||||
|
if now.After(th.tokenExpiration) || addedScopes {
|
||||||
|
token, expiration, err := th.fetchToken(params, scopes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not update cache for added scope tokens
|
||||||
|
if !addedScopes {
|
||||||
|
th.tokenCache = token
|
||||||
|
th.tokenExpiration = expiration
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return th.tokenCache, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type postTokenResponse struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
IssuedAt time.Time `json:"issued_at"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *tokenHandler) fetchTokenWithOAuth(realm *url.URL, refreshToken, service string, scopes []string) (token string, expiration time.Time, err error) {
|
||||||
|
form := url.Values{}
|
||||||
|
form.Set("scope", strings.Join(scopes, " "))
|
||||||
|
form.Set("service", service)
|
||||||
|
|
||||||
|
clientID := th.clientID
|
||||||
|
if clientID == "" {
|
||||||
|
// Use default client, this is a required field
|
||||||
|
clientID = defaultClientID
|
||||||
|
}
|
||||||
|
form.Set("client_id", clientID)
|
||||||
|
|
||||||
|
if refreshToken != "" {
|
||||||
|
form.Set("grant_type", "refresh_token")
|
||||||
|
form.Set("refresh_token", refreshToken)
|
||||||
|
} else if th.creds != nil {
|
||||||
|
form.Set("grant_type", "password")
|
||||||
|
username, password := th.creds.Basic(realm)
|
||||||
|
form.Set("username", username)
|
||||||
|
form.Set("password", password)
|
||||||
|
|
||||||
|
// attempt to get a refresh token
|
||||||
|
form.Set("access_type", "offline")
|
||||||
|
} else {
|
||||||
|
// refuse to do oauth without a grant type
|
||||||
|
return "", time.Time{}, fmt.Errorf("no supported grant type")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := th.client().PostForm(realm.String(), form)
|
||||||
|
if err != nil {
|
||||||
|
return "", time.Time{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if !client.SuccessStatus(resp.StatusCode) {
|
||||||
|
err := client.HandleErrorResponse(resp)
|
||||||
|
return "", time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
|
||||||
|
var tr postTokenResponse
|
||||||
|
if err = decoder.Decode(&tr); err != nil {
|
||||||
|
return "", time.Time{}, fmt.Errorf("unable to decode token response: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.RefreshToken != "" && tr.RefreshToken != refreshToken {
|
||||||
|
th.creds.SetRefreshToken(realm, service, tr.RefreshToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.ExpiresIn < minimumTokenLifetimeSeconds {
|
||||||
|
// The default/minimum lifetime.
|
||||||
|
tr.ExpiresIn = minimumTokenLifetimeSeconds
|
||||||
|
logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.IssuedAt.IsZero() {
|
||||||
|
// issued_at is optional in the token response.
|
||||||
|
tr.IssuedAt = th.clock.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr.AccessToken, tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type getTokenResponse struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
IssuedAt time.Time `json:"issued_at"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *tokenHandler) fetchTokenWithBasicAuth(realm *url.URL, service string, scopes []string) (token string, expiration time.Time, err error) {
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", realm.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reqParams := req.URL.Query()
|
||||||
|
|
||||||
|
if service != "" {
|
||||||
|
reqParams.Add("service", service)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scope := range scopes {
|
||||||
|
reqParams.Add("scope", scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
if th.offlineAccess {
|
||||||
|
reqParams.Add("offline_token", "true")
|
||||||
|
clientID := th.clientID
|
||||||
|
if clientID == "" {
|
||||||
|
clientID = defaultClientID
|
||||||
|
}
|
||||||
|
reqParams.Add("client_id", clientID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if th.creds != nil {
|
||||||
|
username, password := th.creds.Basic(realm)
|
||||||
|
if username != "" && password != "" {
|
||||||
|
reqParams.Add("account", username)
|
||||||
|
req.SetBasicAuth(username, password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.URL.RawQuery = reqParams.Encode()
|
||||||
|
|
||||||
|
resp, err := th.client().Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", time.Time{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if !client.SuccessStatus(resp.StatusCode) {
|
||||||
|
err := client.HandleErrorResponse(resp)
|
||||||
|
return "", time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
|
||||||
|
var tr getTokenResponse
|
||||||
|
if err = decoder.Decode(&tr); err != nil {
|
||||||
|
return "", time.Time{}, fmt.Errorf("unable to decode token response: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.RefreshToken != "" && th.creds != nil {
|
||||||
|
th.creds.SetRefreshToken(realm, service, tr.RefreshToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `access_token` is equivalent to `token` and if both are specified
|
||||||
|
// the choice is undefined. Canonicalize `access_token` by sticking
|
||||||
|
// things in `token`.
|
||||||
|
if tr.AccessToken != "" {
|
||||||
|
tr.Token = tr.AccessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.Token == "" {
|
||||||
|
return "", time.Time{}, ErrNoToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.ExpiresIn < minimumTokenLifetimeSeconds {
|
||||||
|
// The default/minimum lifetime.
|
||||||
|
tr.ExpiresIn = minimumTokenLifetimeSeconds
|
||||||
|
logrus.Debugf("Increasing token expiration to: %d seconds", tr.ExpiresIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.IssuedAt.IsZero() {
|
||||||
|
// issued_at is optional in the token response.
|
||||||
|
tr.IssuedAt = th.clock.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr.Token, tr.IssuedAt.Add(time.Duration(tr.ExpiresIn) * time.Second), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *tokenHandler) fetchToken(params map[string]string, scopes []string) (token string, expiration time.Time, err error) {
|
||||||
|
realm, ok := params["realm"]
|
||||||
|
if !ok {
|
||||||
|
return "", time.Time{}, errors.New("no realm specified for token auth challenge")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dmcgowan): Handle empty scheme and relative realm
|
||||||
|
realmURL, err := url.Parse(realm)
|
||||||
|
if err != nil {
|
||||||
|
return "", time.Time{}, fmt.Errorf("invalid token auth challenge realm: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
service := params["service"]
|
||||||
|
|
||||||
|
var refreshToken string
|
||||||
|
|
||||||
|
if th.creds != nil {
|
||||||
|
refreshToken = th.creds.RefreshToken(realmURL, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
if refreshToken != "" || th.forceOAuth {
|
||||||
|
return th.fetchTokenWithOAuth(realmURL, refreshToken, service, scopes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return th.fetchTokenWithBasicAuth(realmURL, service, scopes)
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicHandler struct {
|
||||||
|
creds CredentialStore
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBasicHandler creaters a new authentiation handler which adds
|
||||||
|
// basic authentication credentials to a request.
|
||||||
|
func NewBasicHandler(creds CredentialStore) AuthenticationHandler {
|
||||||
|
return &basicHandler{
|
||||||
|
creds: creds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*basicHandler) Scheme() string {
|
||||||
|
return "basic"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bh *basicHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
|
||||||
|
if bh.creds != nil {
|
||||||
|
username, password := bh.creds.Basic(req.URL)
|
||||||
|
if username != "" && password != "" {
|
||||||
|
req.SetBasicAuth(username, password)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ErrNoBasicAuthCredentials
|
||||||
|
}
|
162
vendor/github.com/docker/distribution/registry/client/blob_writer.go
generated
vendored
Normal file
162
vendor/github.com/docker/distribution/registry/client/blob_writer.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type httpBlobUpload struct {
|
||||||
|
statter distribution.BlobStatter
|
||||||
|
client *http.Client
|
||||||
|
|
||||||
|
uuid string
|
||||||
|
startedAt time.Time
|
||||||
|
|
||||||
|
location string // always the last value of the location header.
|
||||||
|
offset int64
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) Reader() (io.ReadCloser, error) {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) handleErrorResponse(resp *http.Response) error {
|
||||||
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return distribution.ErrBlobUploadUnknown
|
||||||
|
}
|
||||||
|
return HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
req, err := http.NewRequest("PATCH", hbu.location, ioutil.NopCloser(r))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer req.Body.Close()
|
||||||
|
|
||||||
|
resp, err := hbu.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !SuccessStatus(resp.StatusCode) {
|
||||||
|
return 0, hbu.handleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
hbu.uuid = resp.Header.Get("Docker-Upload-UUID")
|
||||||
|
hbu.location, err = sanitizeLocation(resp.Header.Get("Location"), hbu.location)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rng := resp.Header.Get("Range")
|
||||||
|
var start, end int64
|
||||||
|
if n, err := fmt.Sscanf(rng, "%d-%d", &start, &end); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if n != 2 || end < start {
|
||||||
|
return 0, fmt.Errorf("bad range format: %s", rng)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (end - start + 1), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) Write(p []byte) (n int, err error) {
|
||||||
|
req, err := http.NewRequest("PATCH", hbu.location, bytes.NewReader(p))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Range", fmt.Sprintf("%d-%d", hbu.offset, hbu.offset+int64(len(p)-1)))
|
||||||
|
req.Header.Set("Content-Length", fmt.Sprintf("%d", len(p)))
|
||||||
|
req.Header.Set("Content-Type", "application/octet-stream")
|
||||||
|
|
||||||
|
resp, err := hbu.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !SuccessStatus(resp.StatusCode) {
|
||||||
|
return 0, hbu.handleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
hbu.uuid = resp.Header.Get("Docker-Upload-UUID")
|
||||||
|
hbu.location, err = sanitizeLocation(resp.Header.Get("Location"), hbu.location)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rng := resp.Header.Get("Range")
|
||||||
|
var start, end int
|
||||||
|
if n, err := fmt.Sscanf(rng, "%d-%d", &start, &end); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if n != 2 || end < start {
|
||||||
|
return 0, fmt.Errorf("bad range format: %s", rng)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (end - start + 1), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) Size() int64 {
|
||||||
|
return hbu.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) ID() string {
|
||||||
|
return hbu.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) StartedAt() time.Time {
|
||||||
|
return hbu.startedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) Commit(ctx context.Context, desc distribution.Descriptor) (distribution.Descriptor, error) {
|
||||||
|
// TODO(dmcgowan): Check if already finished, if so just fetch
|
||||||
|
req, err := http.NewRequest("PUT", hbu.location, nil)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
values := req.URL.Query()
|
||||||
|
values.Set("digest", desc.Digest.String())
|
||||||
|
req.URL.RawQuery = values.Encode()
|
||||||
|
|
||||||
|
resp, err := hbu.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if !SuccessStatus(resp.StatusCode) {
|
||||||
|
return distribution.Descriptor{}, hbu.handleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hbu.statter.Stat(ctx, desc.Digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) Cancel(ctx context.Context) error {
|
||||||
|
req, err := http.NewRequest("DELETE", hbu.location, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp, err := hbu.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusNotFound || SuccessStatus(resp.StatusCode) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return hbu.handleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hbu *httpBlobUpload) Close() error {
|
||||||
|
hbu.closed = true
|
||||||
|
return nil
|
||||||
|
}
|
139
vendor/github.com/docker/distribution/registry/client/errors.go
generated
vendored
Normal file
139
vendor/github.com/docker/distribution/registry/client/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
|
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrNoErrorsInBody is returned when an HTTP response body parses to an empty
|
||||||
|
// errcode.Errors slice.
|
||||||
|
var ErrNoErrorsInBody = errors.New("no error details found in HTTP response body")
|
||||||
|
|
||||||
|
// UnexpectedHTTPStatusError is returned when an unexpected HTTP status is
|
||||||
|
// returned when making a registry api call.
|
||||||
|
type UnexpectedHTTPStatusError struct {
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnexpectedHTTPStatusError) Error() string {
|
||||||
|
return fmt.Sprintf("received unexpected HTTP status: %s", e.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnexpectedHTTPResponseError is returned when an expected HTTP status code
|
||||||
|
// is returned, but the content was unexpected and failed to be parsed.
|
||||||
|
type UnexpectedHTTPResponseError struct {
|
||||||
|
ParseErr error
|
||||||
|
StatusCode int
|
||||||
|
Response []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnexpectedHTTPResponseError) Error() string {
|
||||||
|
return fmt.Sprintf("error parsing HTTP %d response body: %s: %q", e.StatusCode, e.ParseErr.Error(), string(e.Response))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHTTPErrorResponse(statusCode int, r io.Reader) error {
|
||||||
|
var errors errcode.Errors
|
||||||
|
body, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backward compatibility, handle irregularly formatted
|
||||||
|
// messages that contain a "details" field.
|
||||||
|
var detailsErr struct {
|
||||||
|
Details string `json:"details"`
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(body, &detailsErr)
|
||||||
|
if err == nil && detailsErr.Details != "" {
|
||||||
|
switch statusCode {
|
||||||
|
case http.StatusUnauthorized:
|
||||||
|
return errcode.ErrorCodeUnauthorized.WithMessage(detailsErr.Details)
|
||||||
|
case http.StatusTooManyRequests:
|
||||||
|
return errcode.ErrorCodeTooManyRequests.WithMessage(detailsErr.Details)
|
||||||
|
default:
|
||||||
|
return errcode.ErrorCodeUnknown.WithMessage(detailsErr.Details)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(body, &errors); err != nil {
|
||||||
|
return &UnexpectedHTTPResponseError{
|
||||||
|
ParseErr: err,
|
||||||
|
StatusCode: statusCode,
|
||||||
|
Response: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errors) == 0 {
|
||||||
|
// If there was no error specified in the body, return
|
||||||
|
// UnexpectedHTTPResponseError.
|
||||||
|
return &UnexpectedHTTPResponseError{
|
||||||
|
ParseErr: ErrNoErrorsInBody,
|
||||||
|
StatusCode: statusCode,
|
||||||
|
Response: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeErrorList(err error) []error {
|
||||||
|
if errL, ok := err.(errcode.Errors); ok {
|
||||||
|
return []error(errL)
|
||||||
|
}
|
||||||
|
return []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeErrors(err1, err2 error) error {
|
||||||
|
return errcode.Errors(append(makeErrorList(err1), makeErrorList(err2)...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleErrorResponse returns error parsed from HTTP response for an
|
||||||
|
// unsuccessful HTTP response code (in the range 400 - 499 inclusive). An
|
||||||
|
// UnexpectedHTTPStatusError returned for response code outside of expected
|
||||||
|
// range.
|
||||||
|
func HandleErrorResponse(resp *http.Response) error {
|
||||||
|
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
||||||
|
// Check for OAuth errors within the `WWW-Authenticate` header first
|
||||||
|
// See https://tools.ietf.org/html/rfc6750#section-3
|
||||||
|
for _, c := range challenge.ResponseChallenges(resp) {
|
||||||
|
if c.Scheme == "bearer" {
|
||||||
|
var err errcode.Error
|
||||||
|
// codes defined at https://tools.ietf.org/html/rfc6750#section-3.1
|
||||||
|
switch c.Parameters["error"] {
|
||||||
|
case "invalid_token":
|
||||||
|
err.Code = errcode.ErrorCodeUnauthorized
|
||||||
|
case "insufficient_scope":
|
||||||
|
err.Code = errcode.ErrorCodeDenied
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if description := c.Parameters["error_description"]; description != "" {
|
||||||
|
err.Message = description
|
||||||
|
} else {
|
||||||
|
err.Message = err.Code.Message()
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeErrors(err, parseHTTPErrorResponse(resp.StatusCode, resp.Body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := parseHTTPErrorResponse(resp.StatusCode, resp.Body)
|
||||||
|
if uErr, ok := err.(*UnexpectedHTTPResponseError); ok && resp.StatusCode == 401 {
|
||||||
|
return errcode.ErrorCodeUnauthorized.WithDetail(uErr.Response)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &UnexpectedHTTPStatusError{Status: resp.Status}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuccessStatus returns true if the argument is a successful HTTP response
|
||||||
|
// code (in the range 200 - 399 inclusive).
|
||||||
|
func SuccessStatus(status int) bool {
|
||||||
|
return status >= 200 && status <= 399
|
||||||
|
}
|
853
vendor/github.com/docker/distribution/registry/client/repository.go
generated
vendored
Normal file
853
vendor/github.com/docker/distribution/registry/client/repository.go
generated
vendored
Normal file
|
@ -0,0 +1,853 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/context"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/docker/distribution/registry/api/v2"
|
||||||
|
"github.com/docker/distribution/registry/client/transport"
|
||||||
|
"github.com/docker/distribution/registry/storage/cache"
|
||||||
|
"github.com/docker/distribution/registry/storage/cache/memory"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Registry provides an interface for calling Repositories, which returns a catalog of repositories.
|
||||||
|
type Registry interface {
|
||||||
|
Repositories(ctx context.Context, repos []string, last string) (n int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkHTTPRedirect is a callback that can manipulate redirected HTTP
|
||||||
|
// requests. It is used to preserve Accept and Range headers.
|
||||||
|
func checkHTTPRedirect(req *http.Request, via []*http.Request) error {
|
||||||
|
if len(via) >= 10 {
|
||||||
|
return errors.New("stopped after 10 redirects")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(via) > 0 {
|
||||||
|
for headerName, headerVals := range via[0].Header {
|
||||||
|
if headerName != "Accept" && headerName != "Range" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, val := range headerVals {
|
||||||
|
// Don't add to redirected request if redirected
|
||||||
|
// request already has a header with the same
|
||||||
|
// name and value.
|
||||||
|
hasValue := false
|
||||||
|
for _, existingVal := range req.Header[headerName] {
|
||||||
|
if existingVal == val {
|
||||||
|
hasValue = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasValue {
|
||||||
|
req.Header.Add(headerName, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRegistry creates a registry namespace which can be used to get a listing of repositories
|
||||||
|
func NewRegistry(ctx context.Context, baseURL string, transport http.RoundTripper) (Registry, error) {
|
||||||
|
ub, err := v2.NewURLBuilderFromString(baseURL, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
Timeout: 1 * time.Minute,
|
||||||
|
CheckRedirect: checkHTTPRedirect,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ®istry{
|
||||||
|
client: client,
|
||||||
|
ub: ub,
|
||||||
|
context: ctx,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type registry struct {
|
||||||
|
client *http.Client
|
||||||
|
ub *v2.URLBuilder
|
||||||
|
context context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repositories returns a lexigraphically sorted catalog given a base URL. The 'entries' slice will be filled up to the size
|
||||||
|
// of the slice, starting at the value provided in 'last'. The number of entries will be returned along with io.EOF if there
|
||||||
|
// are no more entries
|
||||||
|
func (r *registry) Repositories(ctx context.Context, entries []string, last string) (int, error) {
|
||||||
|
var numFilled int
|
||||||
|
var returnErr error
|
||||||
|
|
||||||
|
values := buildCatalogValues(len(entries), last)
|
||||||
|
u, err := r.ub.BuildCatalogURL(values)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := r.client.Get(u)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if SuccessStatus(resp.StatusCode) {
|
||||||
|
var ctlg struct {
|
||||||
|
Repositories []string `json:"repositories"`
|
||||||
|
}
|
||||||
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
|
||||||
|
if err := decoder.Decode(&ctlg); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for cnt := range ctlg.Repositories {
|
||||||
|
entries[cnt] = ctlg.Repositories[cnt]
|
||||||
|
}
|
||||||
|
numFilled = len(ctlg.Repositories)
|
||||||
|
|
||||||
|
link := resp.Header.Get("Link")
|
||||||
|
if link == "" {
|
||||||
|
returnErr = io.EOF
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0, HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return numFilled, returnErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRepository creates a new Repository for the given repository name and base URL.
|
||||||
|
func NewRepository(ctx context.Context, name reference.Named, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
|
||||||
|
ub, err := v2.NewURLBuilderFromString(baseURL, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
CheckRedirect: checkHTTPRedirect,
|
||||||
|
// TODO(dmcgowan): create cookie jar
|
||||||
|
}
|
||||||
|
|
||||||
|
return &repository{
|
||||||
|
client: client,
|
||||||
|
ub: ub,
|
||||||
|
name: name,
|
||||||
|
context: ctx,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type repository struct {
|
||||||
|
client *http.Client
|
||||||
|
ub *v2.URLBuilder
|
||||||
|
context context.Context
|
||||||
|
name reference.Named
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repository) Named() reference.Named {
|
||||||
|
return r.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repository) Blobs(ctx context.Context) distribution.BlobStore {
|
||||||
|
statter := &blobStatter{
|
||||||
|
name: r.name,
|
||||||
|
ub: r.ub,
|
||||||
|
client: r.client,
|
||||||
|
}
|
||||||
|
return &blobs{
|
||||||
|
name: r.name,
|
||||||
|
ub: r.ub,
|
||||||
|
client: r.client,
|
||||||
|
statter: cache.NewCachedBlobStatter(memory.NewInMemoryBlobDescriptorCacheProvider(), statter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {
|
||||||
|
// todo(richardscothern): options should be sent over the wire
|
||||||
|
return &manifests{
|
||||||
|
name: r.name,
|
||||||
|
ub: r.ub,
|
||||||
|
client: r.client,
|
||||||
|
etags: make(map[string]string),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repository) Tags(ctx context.Context) distribution.TagService {
|
||||||
|
return &tags{
|
||||||
|
client: r.client,
|
||||||
|
ub: r.ub,
|
||||||
|
context: r.context,
|
||||||
|
name: r.Named(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tags implements remote tagging operations.
|
||||||
|
type tags struct {
|
||||||
|
client *http.Client
|
||||||
|
ub *v2.URLBuilder
|
||||||
|
context context.Context
|
||||||
|
name reference.Named
|
||||||
|
}
|
||||||
|
|
||||||
|
// All returns all tags
|
||||||
|
func (t *tags) All(ctx context.Context) ([]string, error) {
|
||||||
|
var tags []string
|
||||||
|
|
||||||
|
u, err := t.ub.BuildTagsURL(t.name)
|
||||||
|
if err != nil {
|
||||||
|
return tags, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
resp, err := t.client.Get(u)
|
||||||
|
if err != nil {
|
||||||
|
return tags, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if SuccessStatus(resp.StatusCode) {
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return tags, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsResponse := struct {
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(b, &tagsResponse); err != nil {
|
||||||
|
return tags, err
|
||||||
|
}
|
||||||
|
tags = append(tags, tagsResponse.Tags...)
|
||||||
|
if link := resp.Header.Get("Link"); link != "" {
|
||||||
|
u = strings.Trim(strings.Split(link, ";")[0], "<>")
|
||||||
|
} else {
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return tags, HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func descriptorFromResponse(response *http.Response) (distribution.Descriptor, error) {
|
||||||
|
desc := distribution.Descriptor{}
|
||||||
|
headers := response.Header
|
||||||
|
|
||||||
|
ctHeader := headers.Get("Content-Type")
|
||||||
|
if ctHeader == "" {
|
||||||
|
return distribution.Descriptor{}, errors.New("missing or empty Content-Type header")
|
||||||
|
}
|
||||||
|
desc.MediaType = ctHeader
|
||||||
|
|
||||||
|
digestHeader := headers.Get("Docker-Content-Digest")
|
||||||
|
if digestHeader == "" {
|
||||||
|
bytes, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
_, desc, err := distribution.UnmarshalManifest(ctHeader, bytes)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
return desc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst, err := digest.ParseDigest(digestHeader)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
desc.Digest = dgst
|
||||||
|
|
||||||
|
lengthHeader := headers.Get("Content-Length")
|
||||||
|
if lengthHeader == "" {
|
||||||
|
return distribution.Descriptor{}, errors.New("missing or empty Content-Length header")
|
||||||
|
}
|
||||||
|
length, err := strconv.ParseInt(lengthHeader, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
desc.Size = length
|
||||||
|
|
||||||
|
return desc, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get issues a HEAD request for a Manifest against its named endpoint in order
|
||||||
|
// to construct a descriptor for the tag. If the registry doesn't support HEADing
|
||||||
|
// a manifest, fallback to GET.
|
||||||
|
func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
|
||||||
|
ref, err := reference.WithTag(t.name, tag)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
u, err := t.ub.BuildManifestURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newRequest := func(method string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest(method, u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range distribution.ManifestMediaTypes() {
|
||||||
|
req.Header.Add("Accept", t)
|
||||||
|
}
|
||||||
|
resp, err := t.client.Do(req)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := newRequest("HEAD")
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case resp.StatusCode >= 200 && resp.StatusCode < 400:
|
||||||
|
return descriptorFromResponse(resp)
|
||||||
|
default:
|
||||||
|
// if the response is an error - there will be no body to decode.
|
||||||
|
// Issue a GET request:
|
||||||
|
// - for data from a server that does not handle HEAD
|
||||||
|
// - to get error details in case of a failure
|
||||||
|
resp, err = newRequest("GET")
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode < 400 {
|
||||||
|
return descriptorFromResponse(resp)
|
||||||
|
}
|
||||||
|
return distribution.Descriptor{}, HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tags) Lookup(ctx context.Context, digest distribution.Descriptor) ([]string, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tags) Tag(ctx context.Context, tag string, desc distribution.Descriptor) error {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tags) Untag(ctx context.Context, tag string) error {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
type manifests struct {
|
||||||
|
name reference.Named
|
||||||
|
ub *v2.URLBuilder
|
||||||
|
client *http.Client
|
||||||
|
etags map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *manifests) Exists(ctx context.Context, dgst digest.Digest) (bool, error) {
|
||||||
|
ref, err := reference.WithDigest(ms.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
u, err := ms.ub.BuildManifestURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ms.client.Head(u)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if SuccessStatus(resp.StatusCode) {
|
||||||
|
return true, nil
|
||||||
|
} else if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEtagToTag allows a client to supply an eTag to Get which will be
|
||||||
|
// used for a conditional HTTP request. If the eTag matches, a nil manifest
|
||||||
|
// and ErrManifestNotModified error will be returned. etag is automatically
|
||||||
|
// quoted when added to this map.
|
||||||
|
func AddEtagToTag(tag, etag string) distribution.ManifestServiceOption {
|
||||||
|
return etagOption{tag, etag}
|
||||||
|
}
|
||||||
|
|
||||||
|
type etagOption struct{ tag, etag string }
|
||||||
|
|
||||||
|
func (o etagOption) Apply(ms distribution.ManifestService) error {
|
||||||
|
if ms, ok := ms.(*manifests); ok {
|
||||||
|
ms.etags[o.tag] = fmt.Sprintf(`"%s"`, o.etag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("etag options is a client-only option")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnContentDigest allows a client to set a the content digest on
|
||||||
|
// a successful request from the 'Docker-Content-Digest' header. This
|
||||||
|
// returned digest is represents the digest which the registry uses
|
||||||
|
// to refer to the content and can be used to delete the content.
|
||||||
|
func ReturnContentDigest(dgst *digest.Digest) distribution.ManifestServiceOption {
|
||||||
|
return contentDigestOption{dgst}
|
||||||
|
}
|
||||||
|
|
||||||
|
type contentDigestOption struct{ digest *digest.Digest }
|
||||||
|
|
||||||
|
func (o contentDigestOption) Apply(ms distribution.ManifestService) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
|
||||||
|
var (
|
||||||
|
digestOrTag string
|
||||||
|
ref reference.Named
|
||||||
|
err error
|
||||||
|
contentDgst *digest.Digest
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
if opt, ok := option.(distribution.WithTagOption); ok {
|
||||||
|
digestOrTag = opt.Tag
|
||||||
|
ref, err = reference.WithTag(ms.name, opt.Tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if opt, ok := option.(contentDigestOption); ok {
|
||||||
|
contentDgst = opt.digest
|
||||||
|
} else {
|
||||||
|
err := option.Apply(ms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if digestOrTag == "" {
|
||||||
|
digestOrTag = dgst.String()
|
||||||
|
ref, err = reference.WithDigest(ms.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := ms.ub.BuildManifestURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range distribution.ManifestMediaTypes() {
|
||||||
|
req.Header.Add("Accept", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := ms.etags[digestOrTag]; ok {
|
||||||
|
req.Header.Set("If-None-Match", ms.etags[digestOrTag])
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ms.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode == http.StatusNotModified {
|
||||||
|
return nil, distribution.ErrManifestNotModified
|
||||||
|
} else if SuccessStatus(resp.StatusCode) {
|
||||||
|
if contentDgst != nil {
|
||||||
|
dgst, err := digest.ParseDigest(resp.Header.Get("Docker-Content-Digest"))
|
||||||
|
if err == nil {
|
||||||
|
*contentDgst = dgst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mt := resp.Header.Get("Content-Type")
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m, _, err := distribution.UnmarshalManifest(mt, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return nil, HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the
|
||||||
|
// tag name in order to build the correct upload URL.
|
||||||
|
func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
|
||||||
|
ref := ms.name
|
||||||
|
var tagged bool
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
if opt, ok := option.(distribution.WithTagOption); ok {
|
||||||
|
var err error
|
||||||
|
ref, err = reference.WithTag(ref, opt.Tag)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
tagged = true
|
||||||
|
} else {
|
||||||
|
err := option.Apply(ms)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mediaType, p, err := m.Payload()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tagged {
|
||||||
|
// generate a canonical digest and Put by digest
|
||||||
|
_, d, err := distribution.UnmarshalManifest(mediaType, p)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ref, err = reference.WithDigest(ref, d.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestURL, err := ms.ub.BuildManifestURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
putRequest, err := http.NewRequest("PUT", manifestURL, bytes.NewReader(p))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
putRequest.Header.Set("Content-Type", mediaType)
|
||||||
|
|
||||||
|
resp, err := ms.client.Do(putRequest)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if SuccessStatus(resp.StatusCode) {
|
||||||
|
dgstHeader := resp.Header.Get("Docker-Content-Digest")
|
||||||
|
dgst, err := digest.ParseDigest(dgstHeader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return dgst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *manifests) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
ref, err := reference.WithDigest(ms.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u, err := ms.ub.BuildManifestURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("DELETE", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ms.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if SuccessStatus(resp.StatusCode) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo(richardscothern): Restore interface and implementation with merge of #1050
|
||||||
|
/*func (ms *manifests) Enumerate(ctx context.Context, manifests []distribution.Manifest, last distribution.Manifest) (n int, err error) {
|
||||||
|
panic("not supported")
|
||||||
|
}*/
|
||||||
|
|
||||||
|
type blobs struct {
|
||||||
|
name reference.Named
|
||||||
|
ub *v2.URLBuilder
|
||||||
|
client *http.Client
|
||||||
|
|
||||||
|
statter distribution.BlobDescriptorService
|
||||||
|
distribution.BlobDeleter
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeLocation(location, base string) (string, error) {
|
||||||
|
baseURL, err := url.Parse(base)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
locationURL, err := url.Parse(location)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseURL.ResolveReference(locationURL).String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
return bs.statter.Stat(ctx, dgst)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
|
||||||
|
reader, err := bs.Open(ctx, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
return ioutil.ReadAll(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
|
||||||
|
ref, err := reference.WithDigest(bs.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
blobURL, err := bs.ub.BuildBlobURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport.NewHTTPReadSeeker(bs.client, blobURL,
|
||||||
|
func(resp *http.Response) error {
|
||||||
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return distribution.ErrBlobUnknown
|
||||||
|
}
|
||||||
|
return HandleErrorResponse(resp)
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) ServeBlob(ctx context.Context, w http.ResponseWriter, r *http.Request, dgst digest.Digest) error {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
|
||||||
|
writer, err := bs.Create(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
dgstr := digest.Canonical.New()
|
||||||
|
n, err := io.Copy(writer, io.TeeReader(bytes.NewReader(p), dgstr.Hash()))
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
if n < int64(len(p)) {
|
||||||
|
return distribution.Descriptor{}, fmt.Errorf("short copy: wrote %d of %d", n, len(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
desc := distribution.Descriptor{
|
||||||
|
MediaType: mediaType,
|
||||||
|
Size: int64(len(p)),
|
||||||
|
Digest: dgstr.Digest(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.Commit(ctx, desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionFunc func(interface{}) error
|
||||||
|
|
||||||
|
func (f optionFunc) Apply(v interface{}) error {
|
||||||
|
return f(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMountFrom returns a BlobCreateOption which designates that the blob should be
|
||||||
|
// mounted from the given canonical reference.
|
||||||
|
func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption {
|
||||||
|
return optionFunc(func(v interface{}) error {
|
||||||
|
opts, ok := v.(*distribution.CreateOptions)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected options type: %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.Mount.ShouldMount = true
|
||||||
|
opts.Mount.From = ref
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
|
||||||
|
var opts distribution.CreateOptions
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
err := option.Apply(&opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var values []url.Values
|
||||||
|
|
||||||
|
if opts.Mount.ShouldMount {
|
||||||
|
values = append(values, url.Values{"from": {opts.Mount.From.Name()}, "mount": {opts.Mount.From.Digest().String()}})
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := bs.ub.BuildBlobUploadURL(bs.name, values...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := bs.client.Post(u, "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusCreated:
|
||||||
|
desc, err := bs.statter.Stat(ctx, opts.Mount.From.Digest())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, distribution.ErrBlobMounted{From: opts.Mount.From, Descriptor: desc}
|
||||||
|
case http.StatusAccepted:
|
||||||
|
// TODO(dmcgowan): Check for invalid UUID
|
||||||
|
uuid := resp.Header.Get("Docker-Upload-UUID")
|
||||||
|
location, err := sanitizeLocation(resp.Header.Get("Location"), u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpBlobUpload{
|
||||||
|
statter: bs.statter,
|
||||||
|
client: bs.client,
|
||||||
|
uuid: uuid,
|
||||||
|
startedAt: time.Now(),
|
||||||
|
location: location,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return nil, HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobs) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
return bs.statter.Clear(ctx, dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
type blobStatter struct {
|
||||||
|
name reference.Named
|
||||||
|
ub *v2.URLBuilder
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
ref, err := reference.WithDigest(bs.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
u, err := bs.ub.BuildBlobURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := bs.client.Head(u)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if SuccessStatus(resp.StatusCode) {
|
||||||
|
lengthHeader := resp.Header.Get("Content-Length")
|
||||||
|
if lengthHeader == "" {
|
||||||
|
return distribution.Descriptor{}, fmt.Errorf("missing content-length header for request: %s", u)
|
||||||
|
}
|
||||||
|
|
||||||
|
length, err := strconv.ParseInt(lengthHeader, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, fmt.Errorf("error parsing content-length: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return distribution.Descriptor{
|
||||||
|
MediaType: resp.Header.Get("Content-Type"),
|
||||||
|
Size: length,
|
||||||
|
Digest: dgst,
|
||||||
|
}, nil
|
||||||
|
} else if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
||||||
|
}
|
||||||
|
return distribution.Descriptor{}, HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCatalogValues(maxEntries int, last string) url.Values {
|
||||||
|
values := url.Values{}
|
||||||
|
|
||||||
|
if maxEntries > 0 {
|
||||||
|
values.Add("n", strconv.Itoa(maxEntries))
|
||||||
|
}
|
||||||
|
|
||||||
|
if last != "" {
|
||||||
|
values.Add("last", last)
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobStatter) Clear(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
ref, err := reference.WithDigest(bs.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
blobURL, err := bs.ub.BuildBlobURL(ref)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("DELETE", blobURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := bs.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if SuccessStatus(resp.StatusCode) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return HandleErrorResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *blobStatter) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
||||||
|
return nil
|
||||||
|
}
|
251
vendor/github.com/docker/distribution/registry/client/transport/http_reader.go
generated
vendored
Normal file
251
vendor/github.com/docker/distribution/registry/client/transport/http_reader.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
contentRangeRegexp = regexp.MustCompile(`bytes ([0-9]+)-([0-9]+)/([0-9]+|\\*)`)
|
||||||
|
|
||||||
|
// ErrWrongCodeForByteRange is returned if the client sends a request
|
||||||
|
// with a Range header but the server returns a 2xx or 3xx code other
|
||||||
|
// than 206 Partial Content.
|
||||||
|
ErrWrongCodeForByteRange = errors.New("expected HTTP 206 from byte range request")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadSeekCloser combines io.ReadSeeker with io.Closer.
|
||||||
|
type ReadSeekCloser interface {
|
||||||
|
io.ReadSeeker
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPReadSeeker handles reading from an HTTP endpoint using a GET
|
||||||
|
// request. When seeking and starting a read from a non-zero offset
|
||||||
|
// the a "Range" header will be added which sets the offset.
|
||||||
|
// TODO(dmcgowan): Move this into a separate utility package
|
||||||
|
func NewHTTPReadSeeker(client *http.Client, url string, errorHandler func(*http.Response) error) ReadSeekCloser {
|
||||||
|
return &httpReadSeeker{
|
||||||
|
client: client,
|
||||||
|
url: url,
|
||||||
|
errorHandler: errorHandler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpReadSeeker struct {
|
||||||
|
client *http.Client
|
||||||
|
url string
|
||||||
|
|
||||||
|
// errorHandler creates an error from an unsuccessful HTTP response.
|
||||||
|
// This allows the error to be created with the HTTP response body
|
||||||
|
// without leaking the body through a returned error.
|
||||||
|
errorHandler func(*http.Response) error
|
||||||
|
|
||||||
|
size int64
|
||||||
|
|
||||||
|
// rc is the remote read closer.
|
||||||
|
rc io.ReadCloser
|
||||||
|
// readerOffset tracks the offset as of the last read.
|
||||||
|
readerOffset int64
|
||||||
|
// seekOffset allows Seek to override the offset. Seek changes
|
||||||
|
// seekOffset instead of changing readOffset directly so that
|
||||||
|
// connection resets can be delayed and possibly avoided if the
|
||||||
|
// seek is undone (i.e. seeking to the end and then back to the
|
||||||
|
// beginning).
|
||||||
|
seekOffset int64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hrs *httpReadSeeker) Read(p []byte) (n int, err error) {
|
||||||
|
if hrs.err != nil {
|
||||||
|
return 0, hrs.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we sought to a different position, we need to reset the
|
||||||
|
// connection. This logic is here instead of Seek so that if
|
||||||
|
// a seek is undone before the next read, the connection doesn't
|
||||||
|
// need to be closed and reopened. A common example of this is
|
||||||
|
// seeking to the end to determine the length, and then seeking
|
||||||
|
// back to the original position.
|
||||||
|
if hrs.readerOffset != hrs.seekOffset {
|
||||||
|
hrs.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
hrs.readerOffset = hrs.seekOffset
|
||||||
|
|
||||||
|
rd, err := hrs.reader()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = rd.Read(p)
|
||||||
|
hrs.seekOffset += int64(n)
|
||||||
|
hrs.readerOffset += int64(n)
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hrs *httpReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
if hrs.err != nil {
|
||||||
|
return 0, hrs.err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastReaderOffset := hrs.readerOffset
|
||||||
|
|
||||||
|
if whence == os.SEEK_SET && hrs.rc == nil {
|
||||||
|
// If no request has been made yet, and we are seeking to an
|
||||||
|
// absolute position, set the read offset as well to avoid an
|
||||||
|
// unnecessary request.
|
||||||
|
hrs.readerOffset = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := hrs.reader()
|
||||||
|
if err != nil {
|
||||||
|
hrs.readerOffset = lastReaderOffset
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newOffset := hrs.seekOffset
|
||||||
|
|
||||||
|
switch whence {
|
||||||
|
case os.SEEK_CUR:
|
||||||
|
newOffset += offset
|
||||||
|
case os.SEEK_END:
|
||||||
|
if hrs.size < 0 {
|
||||||
|
return 0, errors.New("content length not known")
|
||||||
|
}
|
||||||
|
newOffset = hrs.size + offset
|
||||||
|
case os.SEEK_SET:
|
||||||
|
newOffset = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
if newOffset < 0 {
|
||||||
|
err = errors.New("cannot seek to negative position")
|
||||||
|
} else {
|
||||||
|
hrs.seekOffset = newOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
return hrs.seekOffset, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hrs *httpReadSeeker) Close() error {
|
||||||
|
if hrs.err != nil {
|
||||||
|
return hrs.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// close and release reader chain
|
||||||
|
if hrs.rc != nil {
|
||||||
|
hrs.rc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
hrs.rc = nil
|
||||||
|
|
||||||
|
hrs.err = errors.New("httpLayer: closed")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hrs *httpReadSeeker) reset() {
|
||||||
|
if hrs.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hrs.rc != nil {
|
||||||
|
hrs.rc.Close()
|
||||||
|
hrs.rc = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hrs *httpReadSeeker) reader() (io.Reader, error) {
|
||||||
|
if hrs.err != nil {
|
||||||
|
return nil, hrs.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hrs.rc != nil {
|
||||||
|
return hrs.rc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", hrs.url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hrs.readerOffset > 0 {
|
||||||
|
// If we are at different offset, issue a range request from there.
|
||||||
|
req.Header.Add("Range", fmt.Sprintf("bytes=%d-", hrs.readerOffset))
|
||||||
|
// TODO: get context in here
|
||||||
|
// context.GetLogger(hrs.context).Infof("Range: %s", req.Header.Get("Range"))
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Accept-Encoding", "identity")
|
||||||
|
resp, err := hrs.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normally would use client.SuccessStatus, but that would be a cyclic
|
||||||
|
// import
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode <= 399 {
|
||||||
|
if hrs.readerOffset > 0 {
|
||||||
|
if resp.StatusCode != http.StatusPartialContent {
|
||||||
|
return nil, ErrWrongCodeForByteRange
|
||||||
|
}
|
||||||
|
|
||||||
|
contentRange := resp.Header.Get("Content-Range")
|
||||||
|
if contentRange == "" {
|
||||||
|
return nil, errors.New("no Content-Range header found in HTTP 206 response")
|
||||||
|
}
|
||||||
|
|
||||||
|
submatches := contentRangeRegexp.FindStringSubmatch(contentRange)
|
||||||
|
if len(submatches) < 4 {
|
||||||
|
return nil, fmt.Errorf("could not parse Content-Range header: %s", contentRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
startByte, err := strconv.ParseUint(submatches[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse start of range in Content-Range header: %s", contentRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
if startByte != uint64(hrs.readerOffset) {
|
||||||
|
return nil, fmt.Errorf("received Content-Range starting at offset %d instead of requested %d", startByte, hrs.readerOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
endByte, err := strconv.ParseUint(submatches[2], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse end of range in Content-Range header: %s", contentRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
if submatches[3] == "*" {
|
||||||
|
hrs.size = -1
|
||||||
|
} else {
|
||||||
|
size, err := strconv.ParseUint(submatches[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse total size in Content-Range header: %s", contentRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
if endByte+1 != size {
|
||||||
|
return nil, fmt.Errorf("range in Content-Range stops before the end of the content: %s", contentRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
hrs.size = int64(size)
|
||||||
|
}
|
||||||
|
} else if resp.StatusCode == http.StatusOK {
|
||||||
|
hrs.size = resp.ContentLength
|
||||||
|
} else {
|
||||||
|
hrs.size = -1
|
||||||
|
}
|
||||||
|
hrs.rc = resp.Body
|
||||||
|
} else {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if hrs.errorHandler != nil {
|
||||||
|
return nil, hrs.errorHandler(resp)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected status resolving reader: %v", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hrs.rc, nil
|
||||||
|
}
|
147
vendor/github.com/docker/distribution/registry/client/transport/transport.go
generated
vendored
Normal file
147
vendor/github.com/docker/distribution/registry/client/transport/transport.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestModifier represents an object which will do an inplace
|
||||||
|
// modification of an HTTP request.
|
||||||
|
type RequestModifier interface {
|
||||||
|
ModifyRequest(*http.Request) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerModifier http.Header
|
||||||
|
|
||||||
|
// NewHeaderRequestModifier returns a new RequestModifier which will
|
||||||
|
// add the given headers to a request.
|
||||||
|
func NewHeaderRequestModifier(header http.Header) RequestModifier {
|
||||||
|
return headerModifier(header)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h headerModifier) ModifyRequest(req *http.Request) error {
|
||||||
|
for k, s := range http.Header(h) {
|
||||||
|
req.Header[k] = append(req.Header[k], s...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTransport creates a new transport which will apply modifiers to
|
||||||
|
// the request on a RoundTrip call.
|
||||||
|
func NewTransport(base http.RoundTripper, modifiers ...RequestModifier) http.RoundTripper {
|
||||||
|
return &transport{
|
||||||
|
Modifiers: modifiers,
|
||||||
|
Base: base,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transport is an http.RoundTripper that makes HTTP requests after
|
||||||
|
// copying and modifying the request
|
||||||
|
type transport struct {
|
||||||
|
Modifiers []RequestModifier
|
||||||
|
Base http.RoundTripper
|
||||||
|
|
||||||
|
mu sync.Mutex // guards modReq
|
||||||
|
modReq map[*http.Request]*http.Request // original -> modified
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip authorizes and authenticates the request with an
|
||||||
|
// access token. If no token exists or token is expired,
|
||||||
|
// tries to refresh/fetch a new token.
|
||||||
|
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
req2 := cloneRequest(req)
|
||||||
|
for _, modifier := range t.Modifiers {
|
||||||
|
if err := modifier.ModifyRequest(req2); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.setModReq(req, req2)
|
||||||
|
res, err := t.base().RoundTrip(req2)
|
||||||
|
if err != nil {
|
||||||
|
t.setModReq(req, nil)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Body = &onEOFReader{
|
||||||
|
rc: res.Body,
|
||||||
|
fn: func() { t.setModReq(req, nil) },
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelRequest cancels an in-flight request by closing its connection.
|
||||||
|
func (t *transport) CancelRequest(req *http.Request) {
|
||||||
|
type canceler interface {
|
||||||
|
CancelRequest(*http.Request)
|
||||||
|
}
|
||||||
|
if cr, ok := t.base().(canceler); ok {
|
||||||
|
t.mu.Lock()
|
||||||
|
modReq := t.modReq[req]
|
||||||
|
delete(t.modReq, req)
|
||||||
|
t.mu.Unlock()
|
||||||
|
cr.CancelRequest(modReq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *transport) base() http.RoundTripper {
|
||||||
|
if t.Base != nil {
|
||||||
|
return t.Base
|
||||||
|
}
|
||||||
|
return http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *transport) setModReq(orig, mod *http.Request) {
|
||||||
|
t.mu.Lock()
|
||||||
|
defer t.mu.Unlock()
|
||||||
|
if t.modReq == nil {
|
||||||
|
t.modReq = make(map[*http.Request]*http.Request)
|
||||||
|
}
|
||||||
|
if mod == nil {
|
||||||
|
delete(t.modReq, orig)
|
||||||
|
} else {
|
||||||
|
t.modReq[orig] = mod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneRequest returns a clone of the provided *http.Request.
|
||||||
|
// The clone is a shallow copy of the struct and its Header map.
|
||||||
|
func cloneRequest(r *http.Request) *http.Request {
|
||||||
|
// shallow copy of the struct
|
||||||
|
r2 := new(http.Request)
|
||||||
|
*r2 = *r
|
||||||
|
// deep copy of the Header
|
||||||
|
r2.Header = make(http.Header, len(r.Header))
|
||||||
|
for k, s := range r.Header {
|
||||||
|
r2.Header[k] = append([]string(nil), s...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r2
|
||||||
|
}
|
||||||
|
|
||||||
|
type onEOFReader struct {
|
||||||
|
rc io.ReadCloser
|
||||||
|
fn func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *onEOFReader) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = r.rc.Read(p)
|
||||||
|
if err == io.EOF {
|
||||||
|
r.runFunc()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *onEOFReader) Close() error {
|
||||||
|
err := r.rc.Close()
|
||||||
|
r.runFunc()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *onEOFReader) runFunc() {
|
||||||
|
if fn := r.fn; fn != nil {
|
||||||
|
fn()
|
||||||
|
r.fn = nil
|
||||||
|
}
|
||||||
|
}
|
35
vendor/github.com/docker/distribution/registry/storage/cache/cache.go
generated
vendored
Normal file
35
vendor/github.com/docker/distribution/registry/storage/cache/cache.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Package cache provides facilities to speed up access to the storage
|
||||||
|
// backend.
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlobDescriptorCacheProvider provides repository scoped
|
||||||
|
// BlobDescriptorService cache instances and a global descriptor cache.
|
||||||
|
type BlobDescriptorCacheProvider interface {
|
||||||
|
distribution.BlobDescriptorService
|
||||||
|
|
||||||
|
RepositoryScoped(repo string) (distribution.BlobDescriptorService, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateDescriptor provides a helper function to ensure that caches have
|
||||||
|
// common criteria for admitting descriptors.
|
||||||
|
func ValidateDescriptor(desc distribution.Descriptor) error {
|
||||||
|
if err := desc.Digest.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if desc.Size < 0 {
|
||||||
|
return fmt.Errorf("cache: invalid length in descriptor: %v < 0", desc.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if desc.MediaType == "" {
|
||||||
|
return fmt.Errorf("cache: empty mediatype on descriptor: %v", desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
101
vendor/github.com/docker/distribution/registry/storage/cache/cachedblobdescriptorstore.go
generated
vendored
Normal file
101
vendor/github.com/docker/distribution/registry/storage/cache/cachedblobdescriptorstore.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/distribution/context"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Metrics is used to hold metric counters
|
||||||
|
// related to the number of times a cache was
|
||||||
|
// hit or missed.
|
||||||
|
type Metrics struct {
|
||||||
|
Requests uint64
|
||||||
|
Hits uint64
|
||||||
|
Misses uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricsTracker represents a metric tracker
|
||||||
|
// which simply counts the number of hits and misses.
|
||||||
|
type MetricsTracker interface {
|
||||||
|
Hit()
|
||||||
|
Miss()
|
||||||
|
Metrics() Metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedBlobStatter struct {
|
||||||
|
cache distribution.BlobDescriptorService
|
||||||
|
backend distribution.BlobDescriptorService
|
||||||
|
tracker MetricsTracker
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCachedBlobStatter creates a new statter which prefers a cache and
|
||||||
|
// falls back to a backend.
|
||||||
|
func NewCachedBlobStatter(cache distribution.BlobDescriptorService, backend distribution.BlobDescriptorService) distribution.BlobDescriptorService {
|
||||||
|
return &cachedBlobStatter{
|
||||||
|
cache: cache,
|
||||||
|
backend: backend,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCachedBlobStatterWithMetrics creates a new statter which prefers a cache and
|
||||||
|
// falls back to a backend. Hits and misses will send to the tracker.
|
||||||
|
func NewCachedBlobStatterWithMetrics(cache distribution.BlobDescriptorService, backend distribution.BlobDescriptorService, tracker MetricsTracker) distribution.BlobStatter {
|
||||||
|
return &cachedBlobStatter{
|
||||||
|
cache: cache,
|
||||||
|
backend: backend,
|
||||||
|
tracker: tracker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cbds *cachedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
desc, err := cbds.cache.Stat(ctx, dgst)
|
||||||
|
if err != nil {
|
||||||
|
if err != distribution.ErrBlobUnknown {
|
||||||
|
context.GetLogger(ctx).Errorf("error retrieving descriptor from cache: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
goto fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
if cbds.tracker != nil {
|
||||||
|
cbds.tracker.Hit()
|
||||||
|
}
|
||||||
|
return desc, nil
|
||||||
|
fallback:
|
||||||
|
if cbds.tracker != nil {
|
||||||
|
cbds.tracker.Miss()
|
||||||
|
}
|
||||||
|
desc, err = cbds.backend.Stat(ctx, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return desc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cbds.cache.SetDescriptor(ctx, dgst, desc); err != nil {
|
||||||
|
context.GetLogger(ctx).Errorf("error adding descriptor %v to cache: %v", desc.Digest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cbds *cachedBlobStatter) Clear(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
err := cbds.cache.Clear(ctx, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cbds.backend.Clear(ctx, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cbds *cachedBlobStatter) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
||||||
|
if err := cbds.cache.SetDescriptor(ctx, dgst, desc); err != nil {
|
||||||
|
context.GetLogger(ctx).Errorf("error adding descriptor %v to cache: %v", desc.Digest, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
179
vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go
generated
vendored
Normal file
179
vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go
generated
vendored
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
package memory
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
|
"github.com/docker/distribution/context"
|
||||||
|
"github.com/docker/distribution/digest"
|
||||||
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/docker/distribution/registry/storage/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
type inMemoryBlobDescriptorCacheProvider struct {
|
||||||
|
global *mapBlobDescriptorCache
|
||||||
|
repositories map[string]*mapBlobDescriptorCache
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInMemoryBlobDescriptorCacheProvider returns a new mapped-based cache for
|
||||||
|
// storing blob descriptor data.
|
||||||
|
func NewInMemoryBlobDescriptorCacheProvider() cache.BlobDescriptorCacheProvider {
|
||||||
|
return &inMemoryBlobDescriptorCacheProvider{
|
||||||
|
global: newMapBlobDescriptorCache(),
|
||||||
|
repositories: make(map[string]*mapBlobDescriptorCache),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) RepositoryScoped(repo string) (distribution.BlobDescriptorService, error) {
|
||||||
|
if _, err := reference.ParseNamed(repo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
imbdcp.mu.RLock()
|
||||||
|
defer imbdcp.mu.RUnlock()
|
||||||
|
|
||||||
|
return &repositoryScopedInMemoryBlobDescriptorCache{
|
||||||
|
repo: repo,
|
||||||
|
parent: imbdcp,
|
||||||
|
repository: imbdcp.repositories[repo],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
return imbdcp.global.Stat(ctx, dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) Clear(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
return imbdcp.global.Clear(ctx, dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
||||||
|
_, err := imbdcp.Stat(ctx, dgst)
|
||||||
|
if err == distribution.ErrBlobUnknown {
|
||||||
|
|
||||||
|
if dgst.Algorithm() != desc.Digest.Algorithm() && dgst != desc.Digest {
|
||||||
|
// if the digests differ, set the other canonical mapping
|
||||||
|
if err := imbdcp.global.SetDescriptor(ctx, desc.Digest, desc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown, just set it
|
||||||
|
return imbdcp.global.SetDescriptor(ctx, dgst, desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we already know it, do nothing
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// repositoryScopedInMemoryBlobDescriptorCache provides the request scoped
|
||||||
|
// repository cache. Instances are not thread-safe but the delegated
|
||||||
|
// operations are.
|
||||||
|
type repositoryScopedInMemoryBlobDescriptorCache struct {
|
||||||
|
repo string
|
||||||
|
parent *inMemoryBlobDescriptorCacheProvider // allows lazy allocation of repo's map
|
||||||
|
repository *mapBlobDescriptorCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
rsimbdcp.parent.mu.Lock()
|
||||||
|
repo := rsimbdcp.repository
|
||||||
|
rsimbdcp.parent.mu.Unlock()
|
||||||
|
|
||||||
|
if repo == nil {
|
||||||
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.Stat(ctx, dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Clear(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
rsimbdcp.parent.mu.Lock()
|
||||||
|
repo := rsimbdcp.repository
|
||||||
|
rsimbdcp.parent.mu.Unlock()
|
||||||
|
|
||||||
|
if repo == nil {
|
||||||
|
return distribution.ErrBlobUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.Clear(ctx, dgst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
||||||
|
rsimbdcp.parent.mu.Lock()
|
||||||
|
repo := rsimbdcp.repository
|
||||||
|
if repo == nil {
|
||||||
|
// allocate map since we are setting it now.
|
||||||
|
var ok bool
|
||||||
|
// have to read back value since we may have allocated elsewhere.
|
||||||
|
repo, ok = rsimbdcp.parent.repositories[rsimbdcp.repo]
|
||||||
|
if !ok {
|
||||||
|
repo = newMapBlobDescriptorCache()
|
||||||
|
rsimbdcp.parent.repositories[rsimbdcp.repo] = repo
|
||||||
|
}
|
||||||
|
rsimbdcp.repository = repo
|
||||||
|
}
|
||||||
|
rsimbdcp.parent.mu.Unlock()
|
||||||
|
|
||||||
|
if err := repo.SetDescriptor(ctx, dgst, desc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsimbdcp.parent.SetDescriptor(ctx, dgst, desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapBlobDescriptorCache provides a simple map-based implementation of the
|
||||||
|
// descriptor cache.
|
||||||
|
type mapBlobDescriptorCache struct {
|
||||||
|
descriptors map[digest.Digest]distribution.Descriptor
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ distribution.BlobDescriptorService = &mapBlobDescriptorCache{}
|
||||||
|
|
||||||
|
func newMapBlobDescriptorCache() *mapBlobDescriptorCache {
|
||||||
|
return &mapBlobDescriptorCache{
|
||||||
|
descriptors: make(map[digest.Digest]distribution.Descriptor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mbdc *mapBlobDescriptorCache) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
|
if err := dgst.Validate(); err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mbdc.mu.RLock()
|
||||||
|
defer mbdc.mu.RUnlock()
|
||||||
|
|
||||||
|
desc, ok := mbdc.descriptors[dgst]
|
||||||
|
if !ok {
|
||||||
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mbdc *mapBlobDescriptorCache) Clear(ctx context.Context, dgst digest.Digest) error {
|
||||||
|
mbdc.mu.Lock()
|
||||||
|
defer mbdc.mu.Unlock()
|
||||||
|
|
||||||
|
delete(mbdc.descriptors, dgst)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mbdc *mapBlobDescriptorCache) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
||||||
|
if err := dgst.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cache.ValidateDescriptor(desc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mbdc.mu.Lock()
|
||||||
|
defer mbdc.mu.Unlock()
|
||||||
|
|
||||||
|
mbdc.descriptors[dgst] = desc
|
||||||
|
return nil
|
||||||
|
}
|
126
vendor/github.com/docker/distribution/uuid/uuid.go
generated
vendored
Normal file
126
vendor/github.com/docker/distribution/uuid/uuid.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
// Package uuid provides simple UUID generation. Only version 4 style UUIDs
|
||||||
|
// can be generated.
|
||||||
|
//
|
||||||
|
// Please see http://tools.ietf.org/html/rfc4122 for details on UUIDs.
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Bits is the number of bits in a UUID
|
||||||
|
Bits = 128
|
||||||
|
|
||||||
|
// Size is the number of bytes in a UUID
|
||||||
|
Size = Bits / 8
|
||||||
|
|
||||||
|
format = "%08x-%04x-%04x-%04x-%012x"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrUUIDInvalid indicates a parsed string is not a valid uuid.
|
||||||
|
ErrUUIDInvalid = fmt.Errorf("invalid uuid")
|
||||||
|
|
||||||
|
// Loggerf can be used to override the default logging destination. Such
|
||||||
|
// log messages in this library should be logged at warning or higher.
|
||||||
|
Loggerf = func(format string, args ...interface{}) {}
|
||||||
|
)
|
||||||
|
|
||||||
|
// UUID represents a UUID value. UUIDs can be compared and set to other values
|
||||||
|
// and accessed by byte.
|
||||||
|
type UUID [Size]byte
|
||||||
|
|
||||||
|
// Generate creates a new, version 4 uuid.
|
||||||
|
func Generate() (u UUID) {
|
||||||
|
const (
|
||||||
|
// ensures we backoff for less than 450ms total. Use the following to
|
||||||
|
// select new value, in units of 10ms:
|
||||||
|
// n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2
|
||||||
|
maxretries = 9
|
||||||
|
backoff = time.Millisecond * 10
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
totalBackoff time.Duration
|
||||||
|
count int
|
||||||
|
retries int
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// This should never block but the read may fail. Because of this,
|
||||||
|
// we just try to read the random number generator until we get
|
||||||
|
// something. This is a very rare condition but may happen.
|
||||||
|
b := time.Duration(retries) * backoff
|
||||||
|
time.Sleep(b)
|
||||||
|
totalBackoff += b
|
||||||
|
|
||||||
|
n, err := io.ReadFull(rand.Reader, u[count:])
|
||||||
|
if err != nil {
|
||||||
|
if retryOnError(err) && retries < maxretries {
|
||||||
|
count += n
|
||||||
|
retries++
|
||||||
|
Loggerf("error generating version 4 uuid, retrying: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any other errors represent a system problem. What did someone
|
||||||
|
// do to /dev/urandom?
|
||||||
|
panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err))
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
u[6] = (u[6] & 0x0f) | 0x40 // set version byte
|
||||||
|
u[8] = (u[8] & 0x3f) | 0x80 // set high order byte 0b10{8,9,a,b}
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse attempts to extract a uuid from the string or returns an error.
|
||||||
|
func Parse(s string) (u UUID, err error) {
|
||||||
|
if len(s) != 36 {
|
||||||
|
return UUID{}, ErrUUIDInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
// create stack addresses for each section of the uuid.
|
||||||
|
p := make([][]byte, 5)
|
||||||
|
|
||||||
|
if _, err := fmt.Sscanf(s, format, &p[0], &p[1], &p[2], &p[3], &p[4]); err != nil {
|
||||||
|
return u, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(u[0:4], p[0])
|
||||||
|
copy(u[4:6], p[1])
|
||||||
|
copy(u[6:8], p[2])
|
||||||
|
copy(u[8:10], p[3])
|
||||||
|
copy(u[10:16], p[4])
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UUID) String() string {
|
||||||
|
return fmt.Sprintf(format, u[:4], u[4:6], u[6:8], u[8:10], u[10:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryOnError tries to detect whether or not retrying would be fruitful.
|
||||||
|
func retryOnError(err error) bool {
|
||||||
|
switch err := err.(type) {
|
||||||
|
case *os.PathError:
|
||||||
|
return retryOnError(err.Err) // unpack the target error
|
||||||
|
case syscall.Errno:
|
||||||
|
if err == syscall.EPERM {
|
||||||
|
// EPERM represents an entropy pool exhaustion, a condition under
|
||||||
|
// which we backoff and retry.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
2
vendor/github.com/docker/docker/LICENSE
generated
vendored
2
vendor/github.com/docker/docker/LICENSE
generated
vendored
|
@ -176,7 +176,7 @@
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2013-2016 Docker, Inc.
|
Copyright 2013-2017 Docker, Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
2
vendor/github.com/docker/docker/NOTICE
generated
vendored
2
vendor/github.com/docker/docker/NOTICE
generated
vendored
|
@ -1,5 +1,5 @@
|
||||||
Docker
|
Docker
|
||||||
Copyright 2012-2016 Docker, Inc.
|
Copyright 2012-2017 Docker, Inc.
|
||||||
|
|
||||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||||
|
|
||||||
|
|
166
vendor/github.com/docker/docker/api/common.go
generated
vendored
Normal file
166
vendor/github.com/docker/docker/api/common.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"mime"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/system"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Common constants for daemon and client.
|
||||||
|
const (
|
||||||
|
// DefaultVersion of Current REST API
|
||||||
|
DefaultVersion string = "1.28"
|
||||||
|
|
||||||
|
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||||
|
// command to specify that no base image is to be used.
|
||||||
|
NoBaseImageSpecifier string = "scratch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// byPortInfo is a temporary type used to sort types.Port by its fields
|
||||||
|
type byPortInfo []types.Port
|
||||||
|
|
||||||
|
func (r byPortInfo) Len() int { return len(r) }
|
||||||
|
func (r byPortInfo) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||||
|
func (r byPortInfo) Less(i, j int) bool {
|
||||||
|
if r[i].PrivatePort != r[j].PrivatePort {
|
||||||
|
return r[i].PrivatePort < r[j].PrivatePort
|
||||||
|
}
|
||||||
|
|
||||||
|
if r[i].IP != r[j].IP {
|
||||||
|
return r[i].IP < r[j].IP
|
||||||
|
}
|
||||||
|
|
||||||
|
if r[i].PublicPort != r[j].PublicPort {
|
||||||
|
return r[i].PublicPort < r[j].PublicPort
|
||||||
|
}
|
||||||
|
|
||||||
|
return r[i].Type < r[j].Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayablePorts returns formatted string representing open ports of container
|
||||||
|
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
|
||||||
|
// it's used by command 'docker ps'
|
||||||
|
func DisplayablePorts(ports []types.Port) string {
|
||||||
|
type portGroup struct {
|
||||||
|
first uint16
|
||||||
|
last uint16
|
||||||
|
}
|
||||||
|
groupMap := make(map[string]*portGroup)
|
||||||
|
var result []string
|
||||||
|
var hostMappings []string
|
||||||
|
var groupMapKeys []string
|
||||||
|
sort.Sort(byPortInfo(ports))
|
||||||
|
for _, port := range ports {
|
||||||
|
current := port.PrivatePort
|
||||||
|
portKey := port.Type
|
||||||
|
if port.IP != "" {
|
||||||
|
if port.PublicPort != current {
|
||||||
|
hostMappings = append(hostMappings, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
portKey = fmt.Sprintf("%s/%s", port.IP, port.Type)
|
||||||
|
}
|
||||||
|
group := groupMap[portKey]
|
||||||
|
|
||||||
|
if group == nil {
|
||||||
|
groupMap[portKey] = &portGroup{first: current, last: current}
|
||||||
|
// record order that groupMap keys are created
|
||||||
|
groupMapKeys = append(groupMapKeys, portKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if current == (group.last + 1) {
|
||||||
|
group.last = current
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, formGroup(portKey, group.first, group.last))
|
||||||
|
groupMap[portKey] = &portGroup{first: current, last: current}
|
||||||
|
}
|
||||||
|
for _, portKey := range groupMapKeys {
|
||||||
|
g := groupMap[portKey]
|
||||||
|
result = append(result, formGroup(portKey, g.first, g.last))
|
||||||
|
}
|
||||||
|
result = append(result, hostMappings...)
|
||||||
|
return strings.Join(result, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func formGroup(key string, start, last uint16) string {
|
||||||
|
parts := strings.Split(key, "/")
|
||||||
|
groupType := parts[0]
|
||||||
|
var ip string
|
||||||
|
if len(parts) > 1 {
|
||||||
|
ip = parts[0]
|
||||||
|
groupType = parts[1]
|
||||||
|
}
|
||||||
|
group := strconv.Itoa(int(start))
|
||||||
|
if start != last {
|
||||||
|
group = fmt.Sprintf("%s-%d", group, last)
|
||||||
|
}
|
||||||
|
if ip != "" {
|
||||||
|
group = fmt.Sprintf("%s:%s->%s", ip, group, group)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s", group, groupType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesContentType validates the content type against the expected one
|
||||||
|
func MatchesContentType(contentType, expectedType string) bool {
|
||||||
|
mimetype, _, err := mime.ParseMediaType(contentType)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Error parsing media type: %s error: %v", contentType, err)
|
||||||
|
}
|
||||||
|
return err == nil && mimetype == expectedType
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
|
||||||
|
// otherwise generates a new one
|
||||||
|
func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
|
||||||
|
err := system.MkdirAll(filepath.Dir(trustKeyPath), 0700)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trustKey, err := libtrust.LoadKeyFile(trustKeyPath)
|
||||||
|
if err == libtrust.ErrKeyFileDoesNotExist {
|
||||||
|
trustKey, err = libtrust.GenerateECP256PrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error generating key: %s", err)
|
||||||
|
}
|
||||||
|
encodedKey, err := serializePrivateKey(trustKey, filepath.Ext(trustKeyPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error serializing key: %s", err)
|
||||||
|
}
|
||||||
|
if err := ioutils.AtomicWriteFile(trustKeyPath, encodedKey, os.FileMode(0600)); err != nil {
|
||||||
|
return nil, fmt.Errorf("Error saving key file: %s", err)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err)
|
||||||
|
}
|
||||||
|
return trustKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func serializePrivateKey(key libtrust.PrivateKey, ext string) (encoded []byte, err error) {
|
||||||
|
if ext == ".json" || ext == ".jwk" {
|
||||||
|
encoded, err = json.Marshal(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encode private key JWK: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pemBlock, err := key.PEMBlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encode private key PEM: %s", err)
|
||||||
|
}
|
||||||
|
encoded = pem.EncodeToMemory(pemBlock)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
6
vendor/github.com/docker/docker/api/common_unix.go
generated
vendored
Normal file
6
vendor/github.com/docker/docker/api/common_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
// MinVersion represents Minimum REST API version supported
|
||||||
|
const MinVersion string = "1.12"
|
8
vendor/github.com/docker/docker/api/common_windows.go
generated
vendored
Normal file
8
vendor/github.com/docker/docker/api/common_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
// MinVersion represents Minimum REST API version supported
|
||||||
|
// Technically the first daemon API version released on Windows is v1.25 in
|
||||||
|
// engine version 1.13. However, some clients are explicitly using downlevel
|
||||||
|
// APIs (e.g. docker-compose v2.1 file format) and that is just too restrictive.
|
||||||
|
// Hence also allowing 1.24 on Windows.
|
||||||
|
const MinVersion string = "1.24"
|
9
vendor/github.com/docker/docker/api/names.go
generated
vendored
Normal file
9
vendor/github.com/docker/docker/api/names.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
// RestrictedNameChars collects the characters allowed to represent a name, normally used to validate container and volume names.
|
||||||
|
const RestrictedNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]`
|
||||||
|
|
||||||
|
// RestrictedNamePattern is a regular expression to validate names against the collection of restricted characters.
|
||||||
|
var RestrictedNamePattern = regexp.MustCompile(`^` + RestrictedNameChars + `+$`)
|
22
vendor/github.com/docker/docker/api/types/auth.go
generated
vendored
Normal file
22
vendor/github.com/docker/docker/api/types/auth.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// AuthConfig contains authorization information for connecting to a Registry
|
||||||
|
type AuthConfig struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Auth string `json:"auth,omitempty"`
|
||||||
|
|
||||||
|
// Email is an optional value associated with the username.
|
||||||
|
// This field is deprecated and will be removed in a later
|
||||||
|
// version of docker.
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
|
||||||
|
ServerAddress string `json:"serveraddress,omitempty"`
|
||||||
|
|
||||||
|
// IdentityToken is used to authenticate the user and get
|
||||||
|
// an access token for the registry.
|
||||||
|
IdentityToken string `json:"identitytoken,omitempty"`
|
||||||
|
|
||||||
|
// RegistryToken is a bearer token to be sent to a registry
|
||||||
|
RegistryToken string `json:"registrytoken,omitempty"`
|
||||||
|
}
|
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
23
vendor/github.com/docker/docker/api/types/blkiodev/blkio.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package blkiodev
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// WeightDevice is a structure that holds device:weight pair
|
||||||
|
type WeightDevice struct {
|
||||||
|
Path string
|
||||||
|
Weight uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WeightDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThrottleDevice is a structure that holds device:rate_per_second pair
|
||||||
|
type ThrottleDevice struct {
|
||||||
|
Path string
|
||||||
|
Rate uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ThrottleDevice) String() string {
|
||||||
|
return fmt.Sprintf("%s:%d", t.Path, t.Rate)
|
||||||
|
}
|
386
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
Normal file
386
vendor/github.com/docker/docker/api/types/client.go
generated
vendored
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
|
||||||
|
type CheckpointCreateOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
Exit bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointListOptions holds parameters to list checkpoints for a container
|
||||||
|
type CheckpointListOptions struct {
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container
|
||||||
|
type CheckpointDeleteOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAttachOptions holds parameters to attach to a container.
|
||||||
|
type ContainerAttachOptions struct {
|
||||||
|
Stream bool
|
||||||
|
Stdin bool
|
||||||
|
Stdout bool
|
||||||
|
Stderr bool
|
||||||
|
DetachKeys string
|
||||||
|
Logs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerCommitOptions holds parameters to commit changes into a container.
|
||||||
|
type ContainerCommitOptions struct {
|
||||||
|
Reference string
|
||||||
|
Comment string
|
||||||
|
Author string
|
||||||
|
Changes []string
|
||||||
|
Pause bool
|
||||||
|
Config *container.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerExecInspect holds information returned by exec inspect.
|
||||||
|
type ContainerExecInspect struct {
|
||||||
|
ExecID string
|
||||||
|
ContainerID string
|
||||||
|
Running bool
|
||||||
|
ExitCode int
|
||||||
|
Pid int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerListOptions holds parameters to list containers with.
|
||||||
|
type ContainerListOptions struct {
|
||||||
|
Quiet bool
|
||||||
|
Size bool
|
||||||
|
All bool
|
||||||
|
Latest bool
|
||||||
|
Since string
|
||||||
|
Before string
|
||||||
|
Limit int
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerLogsOptions holds parameters to filter logs with.
|
||||||
|
type ContainerLogsOptions struct {
|
||||||
|
ShowStdout bool
|
||||||
|
ShowStderr bool
|
||||||
|
Since string
|
||||||
|
Timestamps bool
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Details bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerRemoveOptions holds parameters to remove containers.
|
||||||
|
type ContainerRemoveOptions struct {
|
||||||
|
RemoveVolumes bool
|
||||||
|
RemoveLinks bool
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStartOptions holds parameters to start containers.
|
||||||
|
type ContainerStartOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
CheckpointDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyToContainerOptions holds information
|
||||||
|
// about files to copy into a container
|
||||||
|
type CopyToContainerOptions struct {
|
||||||
|
AllowOverwriteDirWithFile bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventsOptions holds parameters to filter events with.
|
||||||
|
type EventsOptions struct {
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkListOptions holds parameters to filter the list of networks with.
|
||||||
|
type NetworkListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// HijackedResponse holds connection information for a hijacked request.
|
||||||
|
type HijackedResponse struct {
|
||||||
|
Conn net.Conn
|
||||||
|
Reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the hijacked connection and reader.
|
||||||
|
func (h *HijackedResponse) Close() {
|
||||||
|
h.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWriter is an interface that implements structs
|
||||||
|
// that close input streams to prevent from writing.
|
||||||
|
type CloseWriter interface {
|
||||||
|
CloseWrite() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWrite closes a readWriter for writing.
|
||||||
|
func (h *HijackedResponse) CloseWrite() error {
|
||||||
|
if conn, ok := h.Conn.(CloseWriter); ok {
|
||||||
|
return conn.CloseWrite()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageBuildOptions holds the information
|
||||||
|
// necessary to build images.
|
||||||
|
type ImageBuildOptions struct {
|
||||||
|
Tags []string
|
||||||
|
SuppressOutput bool
|
||||||
|
RemoteContext string
|
||||||
|
NoCache bool
|
||||||
|
Remove bool
|
||||||
|
ForceRemove bool
|
||||||
|
PullParent bool
|
||||||
|
Isolation container.Isolation
|
||||||
|
CPUSetCPUs string
|
||||||
|
CPUSetMems string
|
||||||
|
CPUShares int64
|
||||||
|
CPUQuota int64
|
||||||
|
CPUPeriod int64
|
||||||
|
Memory int64
|
||||||
|
MemorySwap int64
|
||||||
|
CgroupParent string
|
||||||
|
NetworkMode string
|
||||||
|
ShmSize int64
|
||||||
|
Dockerfile string
|
||||||
|
Ulimits []*units.Ulimit
|
||||||
|
// BuildArgs needs to be a *string instead of just a string so that
|
||||||
|
// we can tell the difference between "" (empty string) and no value
|
||||||
|
// at all (nil). See the parsing of buildArgs in
|
||||||
|
// api/server/router/build/build_routes.go for even more info.
|
||||||
|
BuildArgs map[string]*string
|
||||||
|
AuthConfigs map[string]AuthConfig
|
||||||
|
Context io.Reader
|
||||||
|
Labels map[string]string
|
||||||
|
// squash the resulting image's layers to the parent
|
||||||
|
// preserves the original image and creates a new one from the parent with all
|
||||||
|
// the changes applied to a single layer
|
||||||
|
Squash bool
|
||||||
|
// CacheFrom specifies images that are used for matching cache. Images
|
||||||
|
// specified here do not need to have a valid parent chain to match cache.
|
||||||
|
CacheFrom []string
|
||||||
|
SecurityOpt []string
|
||||||
|
ExtraHosts []string // List of extra hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageBuildResponse holds information
|
||||||
|
// returned by a server after building
|
||||||
|
// an image.
|
||||||
|
type ImageBuildResponse struct {
|
||||||
|
Body io.ReadCloser
|
||||||
|
OSType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageCreateOptions holds information to create images.
|
||||||
|
type ImageCreateOptions struct {
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageImportSource holds source information for ImageImport
|
||||||
|
type ImageImportSource struct {
|
||||||
|
Source io.Reader // Source is the data to send to the server to create this image from. You must set SourceName to "-" to leverage this.
|
||||||
|
SourceName string // SourceName is the name of the image to pull. Set to "-" to leverage the Source attribute.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageImportOptions holds information to import images from the client host.
|
||||||
|
type ImageImportOptions struct {
|
||||||
|
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
|
||||||
|
Message string // Message is the message to tag the image with
|
||||||
|
Changes []string // Changes are the raw changes to apply to this image
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageListOptions holds parameters to filter the list of images with.
|
||||||
|
type ImageListOptions struct {
|
||||||
|
All bool
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageLoadResponse returns information to the client about a load process.
|
||||||
|
type ImageLoadResponse struct {
|
||||||
|
// Body must be closed to avoid a resource leak
|
||||||
|
Body io.ReadCloser
|
||||||
|
JSON bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagePullOptions holds information to pull images.
|
||||||
|
type ImagePullOptions struct {
|
||||||
|
All bool
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestPrivilegeFunc is a function interface that
|
||||||
|
// clients can supply to retry operations after
|
||||||
|
// getting an authorization error.
|
||||||
|
// This function returns the registry authentication
|
||||||
|
// header value in base 64 format, or an error
|
||||||
|
// if the privilege request fails.
|
||||||
|
type RequestPrivilegeFunc func() (string, error)
|
||||||
|
|
||||||
|
//ImagePushOptions holds information to push images.
|
||||||
|
type ImagePushOptions ImagePullOptions
|
||||||
|
|
||||||
|
// ImageRemoveOptions holds parameters to remove images.
|
||||||
|
type ImageRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
PruneChildren bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageSearchOptions holds parameters to search images with.
|
||||||
|
type ImageSearchOptions struct {
|
||||||
|
RegistryAuth string
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
Filters filters.Args
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeOptions holds parameters to resize a tty.
|
||||||
|
// It can be used to resize container ttys and
|
||||||
|
// exec process ttys too.
|
||||||
|
type ResizeOptions struct {
|
||||||
|
Height uint
|
||||||
|
Width uint
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionResponse holds version information for the client and the server
|
||||||
|
type VersionResponse struct {
|
||||||
|
Client *Version
|
||||||
|
Server *Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerOK returns true when the client could connect to the docker server
|
||||||
|
// and parse the information received. It returns false otherwise.
|
||||||
|
func (v VersionResponse) ServerOK() bool {
|
||||||
|
return v.Server != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeListOptions holds parameters to list nodes with.
|
||||||
|
type NodeListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRemoveOptions holds parameters to remove nodes with.
|
||||||
|
type NodeRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateOptions contains the options to use when creating a service.
|
||||||
|
type ServiceCreateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateResponse contains the information returned to a client
|
||||||
|
// on the creation of a new service.
|
||||||
|
type ServiceCreateResponse struct {
|
||||||
|
// ID is the ID of the created service.
|
||||||
|
ID string
|
||||||
|
// Warnings is a set of non-fatal warning messages to pass on to the user.
|
||||||
|
Warnings []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values for RegistryAuthFrom in ServiceUpdateOptions
|
||||||
|
const (
|
||||||
|
RegistryAuthFromSpec = "spec"
|
||||||
|
RegistryAuthFromPreviousSpec = "previous-spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceUpdateOptions contains the options to be used for updating services.
|
||||||
|
type ServiceUpdateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// TODO(stevvooe): Consider moving the version parameter of ServiceUpdate
|
||||||
|
// into this field. While it does open API users up to racy writes, most
|
||||||
|
// users may not need that level of consistency in practice.
|
||||||
|
|
||||||
|
// RegistryAuthFrom specifies where to find the registry authorization
|
||||||
|
// credentials if they are not given in EncodedRegistryAuth. Valid
|
||||||
|
// values are "spec" and "previous-spec".
|
||||||
|
RegistryAuthFrom string
|
||||||
|
|
||||||
|
// Rollback indicates whether a server-side rollback should be
|
||||||
|
// performed. When this is set, the provided spec will be ignored.
|
||||||
|
// The valid values are "previous" and "none". An empty value is the
|
||||||
|
// same as "none".
|
||||||
|
Rollback string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceListOptions holds parameters to list services with.
|
||||||
|
type ServiceListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskListOptions holds parameters to list tasks with.
|
||||||
|
type TaskListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginRemoveOptions holds parameters to remove plugins.
|
||||||
|
type PluginRemoveOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginEnableOptions holds parameters to enable plugins.
|
||||||
|
type PluginEnableOptions struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDisableOptions holds parameters to disable plugins.
|
||||||
|
type PluginDisableOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginInstallOptions holds parameters to install a plugin.
|
||||||
|
type PluginInstallOptions struct {
|
||||||
|
Disabled bool
|
||||||
|
AcceptAllPermissions bool
|
||||||
|
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||||
|
RemoteRef string // RemoteRef is the plugin name on the registry
|
||||||
|
PrivilegeFunc RequestPrivilegeFunc
|
||||||
|
AcceptPermissionsFunc func(PluginPrivileges) (bool, error)
|
||||||
|
Args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretRequestOption is a type for requesting secrets
|
||||||
|
type SecretRequestOption struct {
|
||||||
|
Source string
|
||||||
|
Target string
|
||||||
|
UID string
|
||||||
|
GID string
|
||||||
|
Mode os.FileMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwarmUnlockKeyResponse contains the response for Engine API:
|
||||||
|
// GET /swarm/unlockkey
|
||||||
|
type SwarmUnlockKeyResponse struct {
|
||||||
|
// UnlockKey is the unlock key in ASCII-armored format.
|
||||||
|
UnlockKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginCreateOptions hold all options to plugin create.
|
||||||
|
type PluginCreateOptions struct {
|
||||||
|
RepoName string
|
||||||
|
}
|
69
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
Normal file
69
vendor/github.com/docker/docker/api/types/configs.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// configs holds structs used for internal communication between the
|
||||||
|
// frontend (such as an http server) and the backend (such as the
|
||||||
|
// docker daemon).
|
||||||
|
|
||||||
|
// ContainerCreateConfig is the parameter set to ContainerCreate()
|
||||||
|
type ContainerCreateConfig struct {
|
||||||
|
Name string
|
||||||
|
Config *container.Config
|
||||||
|
HostConfig *container.HostConfig
|
||||||
|
NetworkingConfig *network.NetworkingConfig
|
||||||
|
AdjustCPUShares bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerRmConfig holds arguments for the container remove
|
||||||
|
// operation. This struct is used to tell the backend what operations
|
||||||
|
// to perform.
|
||||||
|
type ContainerRmConfig struct {
|
||||||
|
ForceRemove, RemoveVolume, RemoveLink bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerCommitConfig contains build configs for commit operation,
|
||||||
|
// and is used when making a commit with the current state of the container.
|
||||||
|
type ContainerCommitConfig struct {
|
||||||
|
Pause bool
|
||||||
|
Repo string
|
||||||
|
Tag string
|
||||||
|
Author string
|
||||||
|
Comment string
|
||||||
|
// merge container config into commit config before commit
|
||||||
|
MergeConfigs bool
|
||||||
|
Config *container.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecConfig is a small subset of the Config struct that holds the configuration
|
||||||
|
// for the exec feature of docker.
|
||||||
|
type ExecConfig struct {
|
||||||
|
User string // User that will run the command
|
||||||
|
Privileged bool // Is the container in privileged mode
|
||||||
|
Tty bool // Attach standard streams to a tty.
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
Detach bool // Execute in detach mode
|
||||||
|
DetachKeys string // Escape keys for detach
|
||||||
|
Env []string // Environment variables
|
||||||
|
Cmd []string // Execution commands and args
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginRmConfig holds arguments for plugin remove.
|
||||||
|
type PluginRmConfig struct {
|
||||||
|
ForceRemove bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginEnableConfig holds arguments for plugin enable
|
||||||
|
type PluginEnableConfig struct {
|
||||||
|
Timeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDisableConfig holds arguments for plugin disable.
|
||||||
|
type PluginDisableConfig struct {
|
||||||
|
ForceDisable bool
|
||||||
|
}
|
62
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
62
vendor/github.com/docker/docker/api/types/container/config.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
|
||||||
|
type HealthConfig struct {
|
||||||
|
// Test is the test to perform to check that the container is healthy.
|
||||||
|
// An empty slice means to inherit the default.
|
||||||
|
// The options are:
|
||||||
|
// {} : inherit healthcheck
|
||||||
|
// {"NONE"} : disable healthcheck
|
||||||
|
// {"CMD", args...} : exec arguments directly
|
||||||
|
// {"CMD-SHELL", command} : run command with system's default shell
|
||||||
|
Test []string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Zero means to inherit. Durations are expressed as integer nanoseconds.
|
||||||
|
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
|
||||||
|
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
|
||||||
|
|
||||||
|
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
|
||||||
|
// Zero means inherit.
|
||||||
|
Retries int `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config contains the configuration data about a container.
|
||||||
|
// It should hold only portable information about the container.
|
||||||
|
// Here, "portable" means "independent from the host we are running on".
|
||||||
|
// Non-portable information *should* appear in HostConfig.
|
||||||
|
// All fields added to this struct must be marked `omitempty` to keep getting
|
||||||
|
// predictable hashes from the old `v1Compatibility` configuration.
|
||||||
|
type Config struct {
|
||||||
|
Hostname string // Hostname
|
||||||
|
Domainname string // Domainname
|
||||||
|
User string // User that will run the command(s) inside the container, also support user:group
|
||||||
|
AttachStdin bool // Attach the standard input, makes possible user interaction
|
||||||
|
AttachStdout bool // Attach the standard output
|
||||||
|
AttachStderr bool // Attach the standard error
|
||||||
|
ExposedPorts nat.PortSet `json:",omitempty"` // List of exposed ports
|
||||||
|
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||||
|
OpenStdin bool // Open stdin
|
||||||
|
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
||||||
|
Env []string // List of environment variable to set in the container
|
||||||
|
Cmd strslice.StrSlice // Command to run when starting the container
|
||||||
|
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||||
|
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
||||||
|
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
|
||||||
|
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
||||||
|
WorkingDir string // Current directory (PWD) in the command will be launched
|
||||||
|
Entrypoint strslice.StrSlice // Entrypoint to run when starting the container
|
||||||
|
NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
||||||
|
MacAddress string `json:",omitempty"` // Mac Address of the container
|
||||||
|
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
||||||
|
Labels map[string]string // List of labels set to this container
|
||||||
|
StopSignal string `json:",omitempty"` // Signal to stop a container
|
||||||
|
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
|
||||||
|
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_changes.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerChangeResponseItem container change response item
|
||||||
|
// swagger:model ContainerChangeResponseItem
|
||||||
|
type ContainerChangeResponseItem struct {
|
||||||
|
|
||||||
|
// Kind of change
|
||||||
|
// Required: true
|
||||||
|
Kind uint8 `json:"Kind"`
|
||||||
|
|
||||||
|
// Path to file that has changed
|
||||||
|
// Required: true
|
||||||
|
Path string `json:"Path"`
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_create.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerCreateCreatedBody container create created body
|
||||||
|
// swagger:model ContainerCreateCreatedBody
|
||||||
|
type ContainerCreateCreatedBody struct {
|
||||||
|
|
||||||
|
// The ID of the created container
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
|
||||||
|
// Warnings encountered when creating the container
|
||||||
|
// Required: true
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/container/container_top.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerTopOKBody container top o k body
|
||||||
|
// swagger:model ContainerTopOKBody
|
||||||
|
type ContainerTopOKBody struct {
|
||||||
|
|
||||||
|
// Each process running in the container, where each is process is an array of values corresponding to the titles
|
||||||
|
// Required: true
|
||||||
|
Processes [][]string `json:"Processes"`
|
||||||
|
|
||||||
|
// The ps column titles
|
||||||
|
// Required: true
|
||||||
|
Titles []string `json:"Titles"`
|
||||||
|
}
|
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/container/container_update.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerUpdateOKBody container update o k body
|
||||||
|
// swagger:model ContainerUpdateOKBody
|
||||||
|
type ContainerUpdateOKBody struct {
|
||||||
|
|
||||||
|
// warnings
|
||||||
|
// Required: true
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
17
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/container/container_wait.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ContainerWaitOKBody container wait o k body
|
||||||
|
// swagger:model ContainerWaitOKBody
|
||||||
|
type ContainerWaitOKBody struct {
|
||||||
|
|
||||||
|
// Exit code of the container
|
||||||
|
// Required: true
|
||||||
|
StatusCode int64 `json:"StatusCode"`
|
||||||
|
}
|
383
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
383
vendor/github.com/docker/docker/api/types/container/host_config.go
generated
vendored
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/blkiodev"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/api/types/strslice"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Isolation represents the isolation technology of a container. The supported
|
||||||
|
// values are platform specific
|
||||||
|
type Isolation string
|
||||||
|
|
||||||
|
// IsDefault indicates the default isolation technology of a container. On Linux this
|
||||||
|
// is the native driver. On Windows, this is a Windows Server Container.
|
||||||
|
func (i Isolation) IsDefault() bool {
|
||||||
|
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IpcMode represents the container ipc stack.
|
||||||
|
type IpcMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private ipc stack.
|
||||||
|
func (n IpcMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's ipc stack.
|
||||||
|
func (n IpcMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's ipc stack.
|
||||||
|
func (n IpcMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the ipc stack is valid.
|
||||||
|
func (n IpcMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container ipc stack is going to be used.
|
||||||
|
func (n IpcMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkMode represents the container network stack.
|
||||||
|
type NetworkMode string
|
||||||
|
|
||||||
|
// IsNone indicates whether container isn't using a network stack.
|
||||||
|
func (n NetworkMode) IsNone() bool {
|
||||||
|
return n == "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDefault indicates whether container uses the default network stack.
|
||||||
|
func (n NetworkMode) IsDefault() bool {
|
||||||
|
return n == "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether container uses its private network stack.
|
||||||
|
func (n NetworkMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether container uses a container network stack.
|
||||||
|
func (n NetworkMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectedContainer is the id of the container which network this container is connected to.
|
||||||
|
func (n NetworkMode) ConnectedContainer() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
//UserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) UserDefined() string {
|
||||||
|
if n.IsUserDefined() {
|
||||||
|
return string(n)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsernsMode represents userns mode in the container.
|
||||||
|
type UsernsMode string
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's userns.
|
||||||
|
func (n UsernsMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses the a private userns.
|
||||||
|
func (n UsernsMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the userns is valid.
|
||||||
|
func (n UsernsMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CgroupSpec represents the cgroup to use for the container.
|
||||||
|
type CgroupSpec string
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container is using another container cgroup
|
||||||
|
func (c CgroupSpec) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the cgroup spec is valid.
|
||||||
|
func (c CgroupSpec) Valid() bool {
|
||||||
|
return c.IsContainer() || c == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose cgroup will be used.
|
||||||
|
func (c CgroupSpec) Container() string {
|
||||||
|
parts := strings.SplitN(string(c), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTSMode represents the UTS namespace of the container.
|
||||||
|
type UTSMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its private UTS namespace.
|
||||||
|
func (n UTSMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's UTS namespace.
|
||||||
|
func (n UTSMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the UTS namespace is valid.
|
||||||
|
func (n UTSMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidMode represents the pid namespace of the container.
|
||||||
|
type PidMode string
|
||||||
|
|
||||||
|
// IsPrivate indicates whether the container uses its own new pid namespace.
|
||||||
|
func (n PidMode) IsPrivate() bool {
|
||||||
|
return !(n.IsHost() || n.IsContainer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether the container uses the host's pid namespace.
|
||||||
|
func (n PidMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsContainer indicates whether the container uses a container's pid namespace.
|
||||||
|
func (n PidMode) IsContainer() bool {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
return len(parts) > 1 && parts[0] == "container"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid indicates whether the pid namespace is valid.
|
||||||
|
func (n PidMode) Valid() bool {
|
||||||
|
parts := strings.Split(string(n), ":")
|
||||||
|
switch mode := parts[0]; mode {
|
||||||
|
case "", "host":
|
||||||
|
case "container":
|
||||||
|
if len(parts) != 2 || parts[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container returns the name of the container whose pid namespace is going to be used.
|
||||||
|
func (n PidMode) Container() string {
|
||||||
|
parts := strings.SplitN(string(n), ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeviceMapping represents the device mapping between the host and the container.
|
||||||
|
type DeviceMapping struct {
|
||||||
|
PathOnHost string
|
||||||
|
PathInContainer string
|
||||||
|
CgroupPermissions string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartPolicy represents the restart policies of the container.
|
||||||
|
type RestartPolicy struct {
|
||||||
|
Name string
|
||||||
|
MaximumRetryCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNone indicates whether the container has the "no" restart policy.
|
||||||
|
// This means the container will not automatically restart when exiting.
|
||||||
|
func (rp *RestartPolicy) IsNone() bool {
|
||||||
|
return rp.Name == "no" || rp.Name == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlways indicates whether the container has the "always" restart policy.
|
||||||
|
// This means the container will automatically restart regardless of the exit status.
|
||||||
|
func (rp *RestartPolicy) IsAlways() bool {
|
||||||
|
return rp.Name == "always"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnFailure indicates whether the container has the "on-failure" restart policy.
|
||||||
|
// This means the container will automatically restart of exiting with a non-zero exit status.
|
||||||
|
func (rp *RestartPolicy) IsOnFailure() bool {
|
||||||
|
return rp.Name == "on-failure"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnlessStopped indicates whether the container has the
|
||||||
|
// "unless-stopped" restart policy. This means the container will
|
||||||
|
// automatically restart unless user has put it to stopped state.
|
||||||
|
func (rp *RestartPolicy) IsUnlessStopped() bool {
|
||||||
|
return rp.Name == "unless-stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSame compares two RestartPolicy to see if they are the same
|
||||||
|
func (rp *RestartPolicy) IsSame(tp *RestartPolicy) bool {
|
||||||
|
return rp.Name == tp.Name && rp.MaximumRetryCount == tp.MaximumRetryCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogMode is a type to define the available modes for logging
|
||||||
|
// These modes affect how logs are handled when log messages start piling up.
|
||||||
|
type LogMode string
|
||||||
|
|
||||||
|
// Available logging modes
|
||||||
|
const (
|
||||||
|
LogModeUnset = ""
|
||||||
|
LogModeBlocking LogMode = "blocking"
|
||||||
|
LogModeNonBlock LogMode = "non-blocking"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogConfig represents the logging configuration of the container.
|
||||||
|
type LogConfig struct {
|
||||||
|
Type string
|
||||||
|
Config map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources contains container's resources (cgroups config, ulimits...)
|
||||||
|
type Resources struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers)
|
||||||
|
Memory int64 // Memory limit (in bytes)
|
||||||
|
NanoCPUs int64 `json:"NanoCpus"` // CPU quota in units of 10<sup>-9</sup> CPUs.
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CgroupParent string // Parent cgroup.
|
||||||
|
BlkioWeight uint16 // Block IO weight (relative weight vs. other containers)
|
||||||
|
BlkioWeightDevice []*blkiodev.WeightDevice
|
||||||
|
BlkioDeviceReadBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteBps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceReadIOps []*blkiodev.ThrottleDevice
|
||||||
|
BlkioDeviceWriteIOps []*blkiodev.ThrottleDevice
|
||||||
|
CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
|
||||||
|
CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota
|
||||||
|
CPURealtimePeriod int64 `json:"CpuRealtimePeriod"` // CPU real-time period
|
||||||
|
CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime"` // CPU real-time runtime
|
||||||
|
CpusetCpus string // CpusetCpus 0-2, 0,1
|
||||||
|
CpusetMems string // CpusetMems 0-2, 0,1
|
||||||
|
Devices []DeviceMapping // List of devices to map inside the container
|
||||||
|
DeviceCgroupRules []string // List of rule to be added to the device cgroup
|
||||||
|
DiskQuota int64 // Disk limit (in bytes)
|
||||||
|
KernelMemory int64 // Kernel memory limit (in bytes)
|
||||||
|
MemoryReservation int64 // Memory soft limit (in bytes)
|
||||||
|
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||||
|
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||||
|
OomKillDisable *bool // Whether to disable OOM Killer or not
|
||||||
|
PidsLimit int64 // Setting pids limit for a container
|
||||||
|
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||||
|
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||||
|
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
||||||
|
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig holds the mutable attributes of a Container.
|
||||||
|
// Those attributes can be updated at runtime.
|
||||||
|
type UpdateConfig struct {
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
RestartPolicy RestartPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostConfig the non-portable Config structure of a container.
|
||||||
|
// Here, "non-portable" means "dependent of the host we are running on".
|
||||||
|
// Portable information *should* appear in Config.
|
||||||
|
type HostConfig struct {
|
||||||
|
// Applicable to all platforms
|
||||||
|
Binds []string // List of volume bindings for this container
|
||||||
|
ContainerIDFile string // File (path) where the containerId is written
|
||||||
|
LogConfig LogConfig // Configuration of the logs for this container
|
||||||
|
NetworkMode NetworkMode // Network mode to use for the container
|
||||||
|
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
||||||
|
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
||||||
|
AutoRemove bool // Automatically remove container when it exits
|
||||||
|
VolumeDriver string // Name of the volume driver used to mount volumes
|
||||||
|
VolumesFrom []string // List of volumes to take from other container
|
||||||
|
|
||||||
|
// Applicable to UNIX platforms
|
||||||
|
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
|
||||||
|
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
|
||||||
|
DNS []string `json:"Dns"` // List of DNS server to lookup
|
||||||
|
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
||||||
|
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
||||||
|
ExtraHosts []string // List of extra hosts
|
||||||
|
GroupAdd []string // List of additional groups that the container process will run as
|
||||||
|
IpcMode IpcMode // IPC namespace to use for the container
|
||||||
|
Cgroup CgroupSpec // Cgroup to use for the container
|
||||||
|
Links []string // List of links (in the name:alias form)
|
||||||
|
OomScoreAdj int // Container preference for OOM-killing
|
||||||
|
PidMode PidMode // PID namespace to use for the container
|
||||||
|
Privileged bool // Is the container in privileged mode
|
||||||
|
PublishAllPorts bool // Should docker publish all exposed port for the container
|
||||||
|
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
||||||
|
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
||||||
|
StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container.
|
||||||
|
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
|
||||||
|
UTSMode UTSMode // UTS namespace to use for the container
|
||||||
|
UsernsMode UsernsMode // The user namespace to use for the container
|
||||||
|
ShmSize int64 // Total shm memory usage
|
||||||
|
Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
|
||||||
|
Runtime string `json:",omitempty"` // Runtime to use with this container
|
||||||
|
|
||||||
|
// Applicable to Windows
|
||||||
|
ConsoleSize [2]uint // Initial console size (height,width)
|
||||||
|
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
|
||||||
|
|
||||||
|
// Contains container's resources (cgroups, ulimits)
|
||||||
|
Resources
|
||||||
|
|
||||||
|
// Mounts specs used by the container
|
||||||
|
Mounts []mount.Mount `json:",omitempty"`
|
||||||
|
|
||||||
|
// Run a custom init inside the container, if null, use the daemon's configured settings
|
||||||
|
Init *bool `json:",omitempty"`
|
||||||
|
|
||||||
|
// Custom init path
|
||||||
|
InitPath string `json:",omitempty"`
|
||||||
|
}
|
41
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
41
vendor/github.com/docker/docker/api/types/container/hostconfig_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package container
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsBridge() {
|
||||||
|
return "bridge"
|
||||||
|
} else if n.IsHost() {
|
||||||
|
return "host"
|
||||||
|
} else if n.IsContainer() {
|
||||||
|
return "container"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "bridge"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return n == "host"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
|
||||||
|
}
|
54
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
54
vendor/github.com/docker/docker/api/types/container/hostconfig_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsBridge indicates whether container uses the bridge network stack
|
||||||
|
// in windows it is given the name NAT
|
||||||
|
func (n NetworkMode) IsBridge() bool {
|
||||||
|
return n == "nat"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHost indicates whether container uses the host network stack.
|
||||||
|
// returns false as this is not supported by windows
|
||||||
|
func (n NetworkMode) IsHost() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUserDefined indicates user-created network
|
||||||
|
func (n NetworkMode) IsUserDefined() bool {
|
||||||
|
return !n.IsDefault() && !n.IsNone() && !n.IsBridge() && !n.IsContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
||||||
|
func (i Isolation) IsHyperV() bool {
|
||||||
|
return strings.ToLower(string(i)) == "hyperv"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProcess indicates the use of process isolation
|
||||||
|
func (i Isolation) IsProcess() bool {
|
||||||
|
return strings.ToLower(string(i)) == "process"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid indicates if an isolation technology is valid
|
||||||
|
func (i Isolation) IsValid() bool {
|
||||||
|
return i.IsDefault() || i.IsHyperV() || i.IsProcess()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkName returns the name of the network stack.
|
||||||
|
func (n NetworkMode) NetworkName() string {
|
||||||
|
if n.IsDefault() {
|
||||||
|
return "default"
|
||||||
|
} else if n.IsBridge() {
|
||||||
|
return "nat"
|
||||||
|
} else if n.IsNone() {
|
||||||
|
return "none"
|
||||||
|
} else if n.IsContainer() {
|
||||||
|
return "container"
|
||||||
|
} else if n.IsUserDefined() {
|
||||||
|
return n.UserDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
13
vendor/github.com/docker/docker/api/types/error_response.go
generated
vendored
Normal file
13
vendor/github.com/docker/docker/api/types/error_response.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// ErrorResponse Represents an error.
|
||||||
|
// swagger:model ErrorResponse
|
||||||
|
type ErrorResponse struct {
|
||||||
|
|
||||||
|
// The error message.
|
||||||
|
// Required: true
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
42
vendor/github.com/docker/docker/api/types/events/events.go
generated
vendored
Normal file
42
vendor/github.com/docker/docker/api/types/events/events.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ContainerEventType is the event type that containers generate
|
||||||
|
ContainerEventType = "container"
|
||||||
|
// DaemonEventType is the event type that daemon generate
|
||||||
|
DaemonEventType = "daemon"
|
||||||
|
// ImageEventType is the event type that images generate
|
||||||
|
ImageEventType = "image"
|
||||||
|
// NetworkEventType is the event type that networks generate
|
||||||
|
NetworkEventType = "network"
|
||||||
|
// PluginEventType is the event type that plugins generate
|
||||||
|
PluginEventType = "plugin"
|
||||||
|
// VolumeEventType is the event type that volumes generate
|
||||||
|
VolumeEventType = "volume"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Actor describes something that generates events,
|
||||||
|
// like a container, or a network, or a volume.
|
||||||
|
// It has a defined name and a set or attributes.
|
||||||
|
// The container attributes are its labels, other actors
|
||||||
|
// can generate these attributes from other properties.
|
||||||
|
type Actor struct {
|
||||||
|
ID string
|
||||||
|
Attributes map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message represents the information an event contains
|
||||||
|
type Message struct {
|
||||||
|
// Deprecated information from JSONMessage.
|
||||||
|
// With data only in container events.
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
From string `json:"from,omitempty"`
|
||||||
|
|
||||||
|
Type string
|
||||||
|
Action string
|
||||||
|
Actor Actor
|
||||||
|
|
||||||
|
Time int64 `json:"time,omitempty"`
|
||||||
|
TimeNano int64 `json:"timeNano,omitempty"`
|
||||||
|
}
|
310
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
Normal file
310
vendor/github.com/docker/docker/api/types/filters/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
// Package filters provides helper function to parse and handle command line
|
||||||
|
// filter, used for example in docker ps or docker images commands.
|
||||||
|
package filters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/versions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Args stores filter arguments as map key:{map key: bool}.
|
||||||
|
// It contains an aggregation of the map of arguments (which are in the form
|
||||||
|
// of -f 'key=value') based on the key, and stores values for the same key
|
||||||
|
// in a map with string keys and boolean values.
|
||||||
|
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
|
||||||
|
// the args will be {"image.name":{"ubuntu":true},"label":{"label1=1":true,"label2=2":true}}
|
||||||
|
type Args struct {
|
||||||
|
fields map[string]map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewArgs initializes a new Args struct.
|
||||||
|
func NewArgs() Args {
|
||||||
|
return Args{fields: map[string]map[string]bool{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFlag parses the argument to the filter flag. Like
|
||||||
|
//
|
||||||
|
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
||||||
|
//
|
||||||
|
// If prev map is provided, then it is appended to, and returned. By default a new
|
||||||
|
// map is created.
|
||||||
|
func ParseFlag(arg string, prev Args) (Args, error) {
|
||||||
|
filters := prev
|
||||||
|
if len(arg) == 0 {
|
||||||
|
return filters, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(arg, "=") {
|
||||||
|
return filters, ErrBadFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
f := strings.SplitN(arg, "=", 2)
|
||||||
|
|
||||||
|
name := strings.ToLower(strings.TrimSpace(f[0]))
|
||||||
|
value := strings.TrimSpace(f[1])
|
||||||
|
|
||||||
|
filters.Add(name, value)
|
||||||
|
|
||||||
|
return filters, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBadFormat is an error returned in case of bad format for a filter.
|
||||||
|
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
|
||||||
|
|
||||||
|
// ToParam packs the Args into a string for easy transport from client to server.
|
||||||
|
func ToParam(a Args) (string, error) {
|
||||||
|
// this way we don't URL encode {}, just empty space
|
||||||
|
if a.Len() == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := json.Marshal(a.fields)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToParamWithVersion packs the Args into a string for easy transport from client to server.
|
||||||
|
// The generated string will depend on the specified version (corresponding to the API version).
|
||||||
|
func ToParamWithVersion(version string, a Args) (string, error) {
|
||||||
|
// this way we don't URL encode {}, just empty space
|
||||||
|
if a.Len() == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// for daemons older than v1.10, filter must be of the form map[string][]string
|
||||||
|
var buf []byte
|
||||||
|
var err error
|
||||||
|
if version != "" && versions.LessThan(version, "1.22") {
|
||||||
|
buf, err = json.Marshal(convertArgsToSlice(a.fields))
|
||||||
|
} else {
|
||||||
|
buf, err = json.Marshal(a.fields)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromParam unpacks the filter Args.
|
||||||
|
func FromParam(p string) (Args, error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return NewArgs(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r := strings.NewReader(p)
|
||||||
|
d := json.NewDecoder(r)
|
||||||
|
|
||||||
|
m := map[string]map[string]bool{}
|
||||||
|
if err := d.Decode(&m); err != nil {
|
||||||
|
r.Seek(0, 0)
|
||||||
|
|
||||||
|
// Allow parsing old arguments in slice format.
|
||||||
|
// Because other libraries might be sending them in this format.
|
||||||
|
deprecated := map[string][]string{}
|
||||||
|
if deprecatedErr := d.Decode(&deprecated); deprecatedErr == nil {
|
||||||
|
m = deprecatedArgs(deprecated)
|
||||||
|
} else {
|
||||||
|
return NewArgs(), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Args{m}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the list of values associates with a field.
|
||||||
|
// It returns a slice of strings to keep backwards compatibility with old code.
|
||||||
|
func (filters Args) Get(field string) []string {
|
||||||
|
values := filters.fields[field]
|
||||||
|
if values == nil {
|
||||||
|
return make([]string, 0)
|
||||||
|
}
|
||||||
|
slice := make([]string, 0, len(values))
|
||||||
|
for key := range values {
|
||||||
|
slice = append(slice, key)
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a new value to a filter field.
|
||||||
|
func (filters Args) Add(name, value string) {
|
||||||
|
if _, ok := filters.fields[name]; ok {
|
||||||
|
filters.fields[name][value] = true
|
||||||
|
} else {
|
||||||
|
filters.fields[name] = map[string]bool{value: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del removes a value from a filter field.
|
||||||
|
func (filters Args) Del(name, value string) {
|
||||||
|
if _, ok := filters.fields[name]; ok {
|
||||||
|
delete(filters.fields[name], value)
|
||||||
|
if len(filters.fields[name]) == 0 {
|
||||||
|
delete(filters.fields, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of fields in the arguments.
|
||||||
|
func (filters Args) Len() int {
|
||||||
|
return len(filters.fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchKVList returns true if the values for the specified field matches the ones
|
||||||
|
// from the sources.
|
||||||
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||||
|
// field is 'label' and sources are {'label1': '1', 'label2': '2'}
|
||||||
|
// it returns true.
|
||||||
|
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sources) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for name2match := range fieldValues {
|
||||||
|
testKV := strings.SplitN(name2match, "=", 2)
|
||||||
|
|
||||||
|
v, ok := sources[testKV[0]]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(testKV) == 2 && testKV[1] != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the values for the specified field matches the source string
|
||||||
|
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
||||||
|
// field is 'image.name' and source is 'ubuntu'
|
||||||
|
// it returns true.
|
||||||
|
func (filters Args) Match(field, source string) bool {
|
||||||
|
if filters.ExactMatch(field, source) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
for name2match := range fieldValues {
|
||||||
|
match, err := regexp.MatchString(name2match, source)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExactMatch returns true if the source matches exactly one of the filters.
|
||||||
|
func (filters Args) ExactMatch(field, source string) bool {
|
||||||
|
fieldValues, ok := filters.fields[field]
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if !ok || len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueExactMatch returns true if there is only one filter and the source matches exactly this one.
|
||||||
|
func (filters Args) UniqueExactMatch(field, source string) bool {
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if len(fieldValues) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(filters.fields[field]) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuzzyMatch returns true if the source matches exactly one of the filters,
|
||||||
|
// or the source has one of the filters as a prefix.
|
||||||
|
func (filters Args) FuzzyMatch(field, source string) bool {
|
||||||
|
if filters.ExactMatch(field, source) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
for prefix := range fieldValues {
|
||||||
|
if strings.HasPrefix(source, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include returns true if the name of the field to filter is in the filters.
|
||||||
|
func (filters Args) Include(field string) bool {
|
||||||
|
_, ok := filters.fields[field]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate ensures that all the fields in the filter are valid.
|
||||||
|
// It returns an error as soon as it finds an invalid field.
|
||||||
|
func (filters Args) Validate(accepted map[string]bool) error {
|
||||||
|
for name := range filters.fields {
|
||||||
|
if !accepted[name] {
|
||||||
|
return fmt.Errorf("Invalid filter '%s'", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkValues iterates over the list of filtered values for a field.
|
||||||
|
// It stops the iteration if it finds an error and it returns that error.
|
||||||
|
func (filters Args) WalkValues(field string, op func(value string) error) error {
|
||||||
|
if _, ok := filters.fields[field]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for v := range filters.fields[field] {
|
||||||
|
if err := op(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
|
||||||
|
m := map[string]map[string]bool{}
|
||||||
|
for k, v := range d {
|
||||||
|
values := map[string]bool{}
|
||||||
|
for _, vv := range v {
|
||||||
|
values[vv] = true
|
||||||
|
}
|
||||||
|
m[k] = values
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertArgsToSlice(f map[string]map[string]bool) map[string][]string {
|
||||||
|
m := map[string][]string{}
|
||||||
|
for k, v := range f {
|
||||||
|
values := []string{}
|
||||||
|
for kk := range v {
|
||||||
|
if v[kk] {
|
||||||
|
values = append(values, kk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[k] = values
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
17
vendor/github.com/docker/docker/api/types/graph_driver_data.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/api/types/graph_driver_data.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// GraphDriverData Information about a container's graph driver.
|
||||||
|
// swagger:model GraphDriverData
|
||||||
|
type GraphDriverData struct {
|
||||||
|
|
||||||
|
// data
|
||||||
|
// Required: true
|
||||||
|
Data map[string]string `json:"Data"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
}
|
13
vendor/github.com/docker/docker/api/types/id_response.go
generated
vendored
Normal file
13
vendor/github.com/docker/docker/api/types/id_response.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// IDResponse Response to an API call that returns just an Id
|
||||||
|
// swagger:model IdResponse
|
||||||
|
type IDResponse struct {
|
||||||
|
|
||||||
|
// The id of the newly created object.
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
}
|
37
vendor/github.com/docker/docker/api/types/image/image_history.go
generated
vendored
Normal file
37
vendor/github.com/docker/docker/api/types/image/image_history.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package image
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// HistoryResponseItem history response item
|
||||||
|
// swagger:model HistoryResponseItem
|
||||||
|
type HistoryResponseItem struct {
|
||||||
|
|
||||||
|
// comment
|
||||||
|
// Required: true
|
||||||
|
Comment string `json:"Comment"`
|
||||||
|
|
||||||
|
// created
|
||||||
|
// Required: true
|
||||||
|
Created int64 `json:"Created"`
|
||||||
|
|
||||||
|
// created by
|
||||||
|
// Required: true
|
||||||
|
CreatedBy string `json:"CreatedBy"`
|
||||||
|
|
||||||
|
// Id
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
|
||||||
|
// size
|
||||||
|
// Required: true
|
||||||
|
Size int64 `json:"Size"`
|
||||||
|
|
||||||
|
// tags
|
||||||
|
// Required: true
|
||||||
|
Tags []string `json:"Tags"`
|
||||||
|
}
|
15
vendor/github.com/docker/docker/api/types/image_delete_response_item.go
generated
vendored
Normal file
15
vendor/github.com/docker/docker/api/types/image_delete_response_item.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// ImageDeleteResponseItem image delete response item
|
||||||
|
// swagger:model ImageDeleteResponseItem
|
||||||
|
type ImageDeleteResponseItem struct {
|
||||||
|
|
||||||
|
// The image ID of an image that was deleted
|
||||||
|
Deleted string `json:"Deleted,omitempty"`
|
||||||
|
|
||||||
|
// The image ID of an image that was untagged
|
||||||
|
Untagged string `json:"Untagged,omitempty"`
|
||||||
|
}
|
49
vendor/github.com/docker/docker/api/types/image_summary.go
generated
vendored
Normal file
49
vendor/github.com/docker/docker/api/types/image_summary.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// ImageSummary image summary
|
||||||
|
// swagger:model ImageSummary
|
||||||
|
type ImageSummary struct {
|
||||||
|
|
||||||
|
// containers
|
||||||
|
// Required: true
|
||||||
|
Containers int64 `json:"Containers"`
|
||||||
|
|
||||||
|
// created
|
||||||
|
// Required: true
|
||||||
|
Created int64 `json:"Created"`
|
||||||
|
|
||||||
|
// Id
|
||||||
|
// Required: true
|
||||||
|
ID string `json:"Id"`
|
||||||
|
|
||||||
|
// labels
|
||||||
|
// Required: true
|
||||||
|
Labels map[string]string `json:"Labels"`
|
||||||
|
|
||||||
|
// parent Id
|
||||||
|
// Required: true
|
||||||
|
ParentID string `json:"ParentId"`
|
||||||
|
|
||||||
|
// repo digests
|
||||||
|
// Required: true
|
||||||
|
RepoDigests []string `json:"RepoDigests"`
|
||||||
|
|
||||||
|
// repo tags
|
||||||
|
// Required: true
|
||||||
|
RepoTags []string `json:"RepoTags"`
|
||||||
|
|
||||||
|
// shared size
|
||||||
|
// Required: true
|
||||||
|
SharedSize int64 `json:"SharedSize"`
|
||||||
|
|
||||||
|
// size
|
||||||
|
// Required: true
|
||||||
|
Size int64 `json:"Size"`
|
||||||
|
|
||||||
|
// virtual size
|
||||||
|
// Required: true
|
||||||
|
VirtualSize int64 `json:"VirtualSize"`
|
||||||
|
}
|
128
vendor/github.com/docker/docker/api/types/mount/mount.go
generated
vendored
Normal file
128
vendor/github.com/docker/docker/api/types/mount/mount.go
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type represents the type of a mount.
|
||||||
|
type Type string
|
||||||
|
|
||||||
|
// Type constants
|
||||||
|
const (
|
||||||
|
// TypeBind is the type for mounting host dir
|
||||||
|
TypeBind Type = "bind"
|
||||||
|
// TypeVolume is the type for remote storage volumes
|
||||||
|
TypeVolume Type = "volume"
|
||||||
|
// TypeTmpfs is the type for mounting tmpfs
|
||||||
|
TypeTmpfs Type = "tmpfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mount represents a mount (volume).
|
||||||
|
type Mount struct {
|
||||||
|
Type Type `json:",omitempty"`
|
||||||
|
// Source specifies the name of the mount. Depending on mount type, this
|
||||||
|
// may be a volume name or a host path, or even ignored.
|
||||||
|
// Source is not supported for tmpfs (must be an empty value)
|
||||||
|
Source string `json:",omitempty"`
|
||||||
|
Target string `json:",omitempty"`
|
||||||
|
ReadOnly bool `json:",omitempty"`
|
||||||
|
Consistency Consistency `json:",omitempty"`
|
||||||
|
|
||||||
|
BindOptions *BindOptions `json:",omitempty"`
|
||||||
|
VolumeOptions *VolumeOptions `json:",omitempty"`
|
||||||
|
TmpfsOptions *TmpfsOptions `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagation represents the propagation of a mount.
|
||||||
|
type Propagation string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PropagationRPrivate RPRIVATE
|
||||||
|
PropagationRPrivate Propagation = "rprivate"
|
||||||
|
// PropagationPrivate PRIVATE
|
||||||
|
PropagationPrivate Propagation = "private"
|
||||||
|
// PropagationRShared RSHARED
|
||||||
|
PropagationRShared Propagation = "rshared"
|
||||||
|
// PropagationShared SHARED
|
||||||
|
PropagationShared Propagation = "shared"
|
||||||
|
// PropagationRSlave RSLAVE
|
||||||
|
PropagationRSlave Propagation = "rslave"
|
||||||
|
// PropagationSlave SLAVE
|
||||||
|
PropagationSlave Propagation = "slave"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Propagations is the list of all valid mount propagations
|
||||||
|
var Propagations = []Propagation{
|
||||||
|
PropagationRPrivate,
|
||||||
|
PropagationPrivate,
|
||||||
|
PropagationRShared,
|
||||||
|
PropagationShared,
|
||||||
|
PropagationRSlave,
|
||||||
|
PropagationSlave,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consistency represents the consistency requirements of a mount.
|
||||||
|
type Consistency string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ConsistencyFull guarantees bind-mount-like consistency
|
||||||
|
ConsistencyFull Consistency = "consistent"
|
||||||
|
// ConsistencyCached mounts can cache read data and FS structure
|
||||||
|
ConsistencyCached Consistency = "cached"
|
||||||
|
// ConsistencyDelegated mounts can cache read and written data and structure
|
||||||
|
ConsistencyDelegated Consistency = "delegated"
|
||||||
|
// ConsistencyDefault provides "consistent" behavior unless overridden
|
||||||
|
ConsistencyDefault Consistency = "default"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BindOptions defines options specific to mounts of type "bind".
|
||||||
|
type BindOptions struct {
|
||||||
|
Propagation Propagation `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeOptions represents the options for a mount of type volume.
|
||||||
|
type VolumeOptions struct {
|
||||||
|
NoCopy bool `json:",omitempty"`
|
||||||
|
Labels map[string]string `json:",omitempty"`
|
||||||
|
DriverConfig *Driver `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Driver represents a volume driver.
|
||||||
|
type Driver struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Options map[string]string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TmpfsOptions defines options specific to mounts of type "tmpfs".
|
||||||
|
type TmpfsOptions struct {
|
||||||
|
// Size sets the size of the tmpfs, in bytes.
|
||||||
|
//
|
||||||
|
// This will be converted to an operating system specific value
|
||||||
|
// depending on the host. For example, on linux, it will be converted to
|
||||||
|
// use a 'k', 'm' or 'g' syntax. BSD, though not widely supported with
|
||||||
|
// docker, uses a straight byte value.
|
||||||
|
//
|
||||||
|
// Percentages are not supported.
|
||||||
|
SizeBytes int64 `json:",omitempty"`
|
||||||
|
// Mode of the tmpfs upon creation
|
||||||
|
Mode os.FileMode `json:",omitempty"`
|
||||||
|
|
||||||
|
// TODO(stevvooe): There are several more tmpfs flags, specified in the
|
||||||
|
// daemon, that are accepted. Only the most basic are added for now.
|
||||||
|
//
|
||||||
|
// From docker/docker/pkg/mount/flags.go:
|
||||||
|
//
|
||||||
|
// var validFlags = map[string]bool{
|
||||||
|
// "": true,
|
||||||
|
// "size": true, X
|
||||||
|
// "mode": true, X
|
||||||
|
// "uid": true,
|
||||||
|
// "gid": true,
|
||||||
|
// "nr_inodes": true,
|
||||||
|
// "nr_blocks": true,
|
||||||
|
// "mpol": true,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Some of these may be straightforward to add, but others, such as
|
||||||
|
// uid/gid have implications in a clustered system.
|
||||||
|
}
|
102
vendor/github.com/docker/docker/api/types/network/network.go
generated
vendored
Normal file
102
vendor/github.com/docker/docker/api/types/network/network.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
// Address represents an IP address
|
||||||
|
type Address struct {
|
||||||
|
Addr string
|
||||||
|
PrefixLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAM represents IP Address Management
|
||||||
|
type IPAM struct {
|
||||||
|
Driver string
|
||||||
|
Options map[string]string //Per network IPAM driver options
|
||||||
|
Config []IPAMConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAMConfig represents IPAM configurations
|
||||||
|
type IPAMConfig struct {
|
||||||
|
Subnet string `json:",omitempty"`
|
||||||
|
IPRange string `json:",omitempty"`
|
||||||
|
Gateway string `json:",omitempty"`
|
||||||
|
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointIPAMConfig represents IPAM configurations for the endpoint
|
||||||
|
type EndpointIPAMConfig struct {
|
||||||
|
IPv4Address string `json:",omitempty"`
|
||||||
|
IPv6Address string `json:",omitempty"`
|
||||||
|
LinkLocalIPs []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a copy of the endpoint ipam config
|
||||||
|
func (cfg *EndpointIPAMConfig) Copy() *EndpointIPAMConfig {
|
||||||
|
cfgCopy := *cfg
|
||||||
|
cfgCopy.LinkLocalIPs = make([]string, 0, len(cfg.LinkLocalIPs))
|
||||||
|
cfgCopy.LinkLocalIPs = append(cfgCopy.LinkLocalIPs, cfg.LinkLocalIPs...)
|
||||||
|
return &cfgCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerInfo represents one peer of an overlay network
|
||||||
|
type PeerInfo struct {
|
||||||
|
Name string
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointSettings stores the network endpoint details
|
||||||
|
type EndpointSettings struct {
|
||||||
|
// Configurations
|
||||||
|
IPAMConfig *EndpointIPAMConfig
|
||||||
|
Links []string
|
||||||
|
Aliases []string
|
||||||
|
// Operational data
|
||||||
|
NetworkID string
|
||||||
|
EndpointID string
|
||||||
|
Gateway string
|
||||||
|
IPAddress string
|
||||||
|
IPPrefixLen int
|
||||||
|
IPv6Gateway string
|
||||||
|
GlobalIPv6Address string
|
||||||
|
GlobalIPv6PrefixLen int
|
||||||
|
MacAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task carries the information about one backend task
|
||||||
|
type Task struct {
|
||||||
|
Name string
|
||||||
|
EndpointID string
|
||||||
|
EndpointIP string
|
||||||
|
Info map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceInfo represents service parameters with the list of service's tasks
|
||||||
|
type ServiceInfo struct {
|
||||||
|
VIP string
|
||||||
|
Ports []string
|
||||||
|
LocalLBIndex int
|
||||||
|
Tasks []Task
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy makes a deep copy of `EndpointSettings`
|
||||||
|
func (es *EndpointSettings) Copy() *EndpointSettings {
|
||||||
|
epCopy := *es
|
||||||
|
if es.IPAMConfig != nil {
|
||||||
|
epCopy.IPAMConfig = es.IPAMConfig.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
if es.Links != nil {
|
||||||
|
links := make([]string, 0, len(es.Links))
|
||||||
|
epCopy.Links = append(links, es.Links...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if es.Aliases != nil {
|
||||||
|
aliases := make([]string, 0, len(es.Aliases))
|
||||||
|
epCopy.Aliases = append(aliases, es.Aliases...)
|
||||||
|
}
|
||||||
|
return &epCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkingConfig represents the container's networking configuration for each of its interfaces
|
||||||
|
// Carries the networking configs specified in the `docker run` and `docker network connect` commands
|
||||||
|
type NetworkingConfig struct {
|
||||||
|
EndpointsConfig map[string]*EndpointSettings // Endpoint configs for each connecting network
|
||||||
|
}
|
189
vendor/github.com/docker/docker/api/types/plugin.go
generated
vendored
Normal file
189
vendor/github.com/docker/docker/api/types/plugin.go
generated
vendored
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// Plugin A plugin for the Engine API
|
||||||
|
// swagger:model Plugin
|
||||||
|
type Plugin struct {
|
||||||
|
|
||||||
|
// config
|
||||||
|
// Required: true
|
||||||
|
Config PluginConfig `json:"Config"`
|
||||||
|
|
||||||
|
// True when the plugin is running. False when the plugin is not running, only installed.
|
||||||
|
// Required: true
|
||||||
|
Enabled bool `json:"Enabled"`
|
||||||
|
|
||||||
|
// Id
|
||||||
|
ID string `json:"Id,omitempty"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
|
||||||
|
// plugin remote reference used to push/pull the plugin
|
||||||
|
PluginReference string `json:"PluginReference,omitempty"`
|
||||||
|
|
||||||
|
// settings
|
||||||
|
// Required: true
|
||||||
|
Settings PluginSettings `json:"Settings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfig The config of a plugin.
|
||||||
|
// swagger:model PluginConfig
|
||||||
|
type PluginConfig struct {
|
||||||
|
|
||||||
|
// args
|
||||||
|
// Required: true
|
||||||
|
Args PluginConfigArgs `json:"Args"`
|
||||||
|
|
||||||
|
// description
|
||||||
|
// Required: true
|
||||||
|
Description string `json:"Description"`
|
||||||
|
|
||||||
|
// documentation
|
||||||
|
// Required: true
|
||||||
|
Documentation string `json:"Documentation"`
|
||||||
|
|
||||||
|
// entrypoint
|
||||||
|
// Required: true
|
||||||
|
Entrypoint []string `json:"Entrypoint"`
|
||||||
|
|
||||||
|
// env
|
||||||
|
// Required: true
|
||||||
|
Env []PluginEnv `json:"Env"`
|
||||||
|
|
||||||
|
// interface
|
||||||
|
// Required: true
|
||||||
|
Interface PluginConfigInterface `json:"Interface"`
|
||||||
|
|
||||||
|
// linux
|
||||||
|
// Required: true
|
||||||
|
Linux PluginConfigLinux `json:"Linux"`
|
||||||
|
|
||||||
|
// mounts
|
||||||
|
// Required: true
|
||||||
|
Mounts []PluginMount `json:"Mounts"`
|
||||||
|
|
||||||
|
// network
|
||||||
|
// Required: true
|
||||||
|
Network PluginConfigNetwork `json:"Network"`
|
||||||
|
|
||||||
|
// propagated mount
|
||||||
|
// Required: true
|
||||||
|
PropagatedMount string `json:"PropagatedMount"`
|
||||||
|
|
||||||
|
// user
|
||||||
|
User PluginConfigUser `json:"User,omitempty"`
|
||||||
|
|
||||||
|
// work dir
|
||||||
|
// Required: true
|
||||||
|
WorkDir string `json:"WorkDir"`
|
||||||
|
|
||||||
|
// rootfs
|
||||||
|
Rootfs *PluginConfigRootfs `json:"rootfs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfigArgs plugin config args
|
||||||
|
// swagger:model PluginConfigArgs
|
||||||
|
type PluginConfigArgs struct {
|
||||||
|
|
||||||
|
// description
|
||||||
|
// Required: true
|
||||||
|
Description string `json:"Description"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
|
||||||
|
// settable
|
||||||
|
// Required: true
|
||||||
|
Settable []string `json:"Settable"`
|
||||||
|
|
||||||
|
// value
|
||||||
|
// Required: true
|
||||||
|
Value []string `json:"Value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfigInterface The interface between Docker and the plugin
|
||||||
|
// swagger:model PluginConfigInterface
|
||||||
|
type PluginConfigInterface struct {
|
||||||
|
|
||||||
|
// socket
|
||||||
|
// Required: true
|
||||||
|
Socket string `json:"Socket"`
|
||||||
|
|
||||||
|
// types
|
||||||
|
// Required: true
|
||||||
|
Types []PluginInterfaceType `json:"Types"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfigLinux plugin config linux
|
||||||
|
// swagger:model PluginConfigLinux
|
||||||
|
type PluginConfigLinux struct {
|
||||||
|
|
||||||
|
// allow all devices
|
||||||
|
// Required: true
|
||||||
|
AllowAllDevices bool `json:"AllowAllDevices"`
|
||||||
|
|
||||||
|
// capabilities
|
||||||
|
// Required: true
|
||||||
|
Capabilities []string `json:"Capabilities"`
|
||||||
|
|
||||||
|
// devices
|
||||||
|
// Required: true
|
||||||
|
Devices []PluginDevice `json:"Devices"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfigNetwork plugin config network
|
||||||
|
// swagger:model PluginConfigNetwork
|
||||||
|
type PluginConfigNetwork struct {
|
||||||
|
|
||||||
|
// type
|
||||||
|
// Required: true
|
||||||
|
Type string `json:"Type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfigRootfs plugin config rootfs
|
||||||
|
// swagger:model PluginConfigRootfs
|
||||||
|
type PluginConfigRootfs struct {
|
||||||
|
|
||||||
|
// diff ids
|
||||||
|
DiffIds []string `json:"diff_ids"`
|
||||||
|
|
||||||
|
// type
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginConfigUser plugin config user
|
||||||
|
// swagger:model PluginConfigUser
|
||||||
|
type PluginConfigUser struct {
|
||||||
|
|
||||||
|
// g ID
|
||||||
|
GID uint32 `json:"GID,omitempty"`
|
||||||
|
|
||||||
|
// UID
|
||||||
|
UID uint32 `json:"UID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginSettings Settings that can be modified by users.
|
||||||
|
// swagger:model PluginSettings
|
||||||
|
type PluginSettings struct {
|
||||||
|
|
||||||
|
// args
|
||||||
|
// Required: true
|
||||||
|
Args []string `json:"Args"`
|
||||||
|
|
||||||
|
// devices
|
||||||
|
// Required: true
|
||||||
|
Devices []PluginDevice `json:"Devices"`
|
||||||
|
|
||||||
|
// env
|
||||||
|
// Required: true
|
||||||
|
Env []string `json:"Env"`
|
||||||
|
|
||||||
|
// mounts
|
||||||
|
// Required: true
|
||||||
|
Mounts []PluginMount `json:"Mounts"`
|
||||||
|
}
|
25
vendor/github.com/docker/docker/api/types/plugin_device.go
generated
vendored
Normal file
25
vendor/github.com/docker/docker/api/types/plugin_device.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// PluginDevice plugin device
|
||||||
|
// swagger:model PluginDevice
|
||||||
|
type PluginDevice struct {
|
||||||
|
|
||||||
|
// description
|
||||||
|
// Required: true
|
||||||
|
Description string `json:"Description"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
|
||||||
|
// path
|
||||||
|
// Required: true
|
||||||
|
Path *string `json:"Path"`
|
||||||
|
|
||||||
|
// settable
|
||||||
|
// Required: true
|
||||||
|
Settable []string `json:"Settable"`
|
||||||
|
}
|
25
vendor/github.com/docker/docker/api/types/plugin_env.go
generated
vendored
Normal file
25
vendor/github.com/docker/docker/api/types/plugin_env.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// PluginEnv plugin env
|
||||||
|
// swagger:model PluginEnv
|
||||||
|
type PluginEnv struct {
|
||||||
|
|
||||||
|
// description
|
||||||
|
// Required: true
|
||||||
|
Description string `json:"Description"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
|
||||||
|
// settable
|
||||||
|
// Required: true
|
||||||
|
Settable []string `json:"Settable"`
|
||||||
|
|
||||||
|
// value
|
||||||
|
// Required: true
|
||||||
|
Value *string `json:"Value"`
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/plugin_interface_type.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/plugin_interface_type.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// PluginInterfaceType plugin interface type
|
||||||
|
// swagger:model PluginInterfaceType
|
||||||
|
type PluginInterfaceType struct {
|
||||||
|
|
||||||
|
// capability
|
||||||
|
// Required: true
|
||||||
|
Capability string `json:"Capability"`
|
||||||
|
|
||||||
|
// prefix
|
||||||
|
// Required: true
|
||||||
|
Prefix string `json:"Prefix"`
|
||||||
|
|
||||||
|
// version
|
||||||
|
// Required: true
|
||||||
|
Version string `json:"Version"`
|
||||||
|
}
|
37
vendor/github.com/docker/docker/api/types/plugin_mount.go
generated
vendored
Normal file
37
vendor/github.com/docker/docker/api/types/plugin_mount.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// PluginMount plugin mount
|
||||||
|
// swagger:model PluginMount
|
||||||
|
type PluginMount struct {
|
||||||
|
|
||||||
|
// description
|
||||||
|
// Required: true
|
||||||
|
Description string `json:"Description"`
|
||||||
|
|
||||||
|
// destination
|
||||||
|
// Required: true
|
||||||
|
Destination string `json:"Destination"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
|
||||||
|
// options
|
||||||
|
// Required: true
|
||||||
|
Options []string `json:"Options"`
|
||||||
|
|
||||||
|
// settable
|
||||||
|
// Required: true
|
||||||
|
Settable []string `json:"Settable"`
|
||||||
|
|
||||||
|
// source
|
||||||
|
// Required: true
|
||||||
|
Source *string `json:"Source"`
|
||||||
|
|
||||||
|
// type
|
||||||
|
// Required: true
|
||||||
|
Type string `json:"Type"`
|
||||||
|
}
|
79
vendor/github.com/docker/docker/api/types/plugin_responses.go
generated
vendored
Normal file
79
vendor/github.com/docker/docker/api/types/plugin_responses.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PluginsListResponse contains the response for the Engine API
|
||||||
|
type PluginsListResponse []*Plugin
|
||||||
|
|
||||||
|
const (
|
||||||
|
authzDriver = "AuthzDriver"
|
||||||
|
graphDriver = "GraphDriver"
|
||||||
|
ipamDriver = "IpamDriver"
|
||||||
|
networkDriver = "NetworkDriver"
|
||||||
|
volumeDriver = "VolumeDriver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler for PluginInterfaceType
|
||||||
|
func (t *PluginInterfaceType) UnmarshalJSON(p []byte) error {
|
||||||
|
versionIndex := len(p)
|
||||||
|
prefixIndex := 0
|
||||||
|
if len(p) < 2 || p[0] != '"' || p[len(p)-1] != '"' {
|
||||||
|
return fmt.Errorf("%q is not a plugin interface type", p)
|
||||||
|
}
|
||||||
|
p = p[1 : len(p)-1]
|
||||||
|
loop:
|
||||||
|
for i, b := range p {
|
||||||
|
switch b {
|
||||||
|
case '.':
|
||||||
|
prefixIndex = i
|
||||||
|
case '/':
|
||||||
|
versionIndex = i
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Prefix = string(p[:prefixIndex])
|
||||||
|
t.Capability = string(p[prefixIndex+1 : versionIndex])
|
||||||
|
if versionIndex < len(p) {
|
||||||
|
t.Version = string(p[versionIndex+1:])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler for PluginInterfaceType
|
||||||
|
func (t *PluginInterfaceType) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(t.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements fmt.Stringer for PluginInterfaceType
|
||||||
|
func (t PluginInterfaceType) String() string {
|
||||||
|
return fmt.Sprintf("%s.%s/%s", t.Prefix, t.Capability, t.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginPrivilege describes a permission the user has to accept
|
||||||
|
// upon installing a plugin.
|
||||||
|
type PluginPrivilege struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Value []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginPrivileges is a list of PluginPrivilege
|
||||||
|
type PluginPrivileges []PluginPrivilege
|
||||||
|
|
||||||
|
func (s PluginPrivileges) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s PluginPrivileges) Less(i, j int) bool {
|
||||||
|
return s[i].Name < s[j].Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s PluginPrivileges) Swap(i, j int) {
|
||||||
|
sort.Strings(s[i].Value)
|
||||||
|
sort.Strings(s[j].Value)
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
23
vendor/github.com/docker/docker/api/types/port.go
generated
vendored
Normal file
23
vendor/github.com/docker/docker/api/types/port.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// Port An open port on a container
|
||||||
|
// swagger:model Port
|
||||||
|
type Port struct {
|
||||||
|
|
||||||
|
// IP
|
||||||
|
IP string `json:"IP,omitempty"`
|
||||||
|
|
||||||
|
// Port on the container
|
||||||
|
// Required: true
|
||||||
|
PrivatePort uint16 `json:"PrivatePort"`
|
||||||
|
|
||||||
|
// Port exposed on the host
|
||||||
|
PublicPort uint16 `json:"PublicPort,omitempty"`
|
||||||
|
|
||||||
|
// type
|
||||||
|
// Required: true
|
||||||
|
Type string `json:"Type"`
|
||||||
|
}
|
21
vendor/github.com/docker/docker/api/types/registry/authenticate.go
generated
vendored
Normal file
21
vendor/github.com/docker/docker/api/types/registry/authenticate.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package registry
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// AuthenticateOKBody authenticate o k body
|
||||||
|
// swagger:model AuthenticateOKBody
|
||||||
|
type AuthenticateOKBody struct {
|
||||||
|
|
||||||
|
// An opaque token used to authenticate a user after a successful login
|
||||||
|
// Required: true
|
||||||
|
IdentityToken string `json:"IdentityToken"`
|
||||||
|
|
||||||
|
// The status of the authentication
|
||||||
|
// Required: true
|
||||||
|
Status string `json:"Status"`
|
||||||
|
}
|
104
vendor/github.com/docker/docker/api/types/registry/registry.go
generated
vendored
Normal file
104
vendor/github.com/docker/docker/api/types/registry/registry.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceConfig stores daemon registry services configuration.
|
||||||
|
type ServiceConfig struct {
|
||||||
|
InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"`
|
||||||
|
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
||||||
|
Mirrors []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetIPNet is the net.IPNet type, which can be marshalled and
|
||||||
|
// unmarshalled to JSON
|
||||||
|
type NetIPNet net.IPNet
|
||||||
|
|
||||||
|
// String returns the CIDR notation of ipnet
|
||||||
|
func (ipnet *NetIPNet) String() string {
|
||||||
|
return (*net.IPNet)(ipnet).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the JSON representation of the IPNet
|
||||||
|
func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal((*net.IPNet)(ipnet).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON sets the IPNet from a byte array of JSON
|
||||||
|
func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
var ipnetStr string
|
||||||
|
if err = json.Unmarshal(b, &ipnetStr); err == nil {
|
||||||
|
var cidr *net.IPNet
|
||||||
|
if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil {
|
||||||
|
*ipnet = NetIPNet(*cidr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexInfo contains information about a registry
|
||||||
|
//
|
||||||
|
// RepositoryInfo Examples:
|
||||||
|
// {
|
||||||
|
// "Index" : {
|
||||||
|
// "Name" : "docker.io",
|
||||||
|
// "Mirrors" : ["https://registry-2.docker.io/v1/", "https://registry-3.docker.io/v1/"],
|
||||||
|
// "Secure" : true,
|
||||||
|
// "Official" : true,
|
||||||
|
// },
|
||||||
|
// "RemoteName" : "library/debian",
|
||||||
|
// "LocalName" : "debian",
|
||||||
|
// "CanonicalName" : "docker.io/debian"
|
||||||
|
// "Official" : true,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "Index" : {
|
||||||
|
// "Name" : "127.0.0.1:5000",
|
||||||
|
// "Mirrors" : [],
|
||||||
|
// "Secure" : false,
|
||||||
|
// "Official" : false,
|
||||||
|
// },
|
||||||
|
// "RemoteName" : "user/repo",
|
||||||
|
// "LocalName" : "127.0.0.1:5000/user/repo",
|
||||||
|
// "CanonicalName" : "127.0.0.1:5000/user/repo",
|
||||||
|
// "Official" : false,
|
||||||
|
// }
|
||||||
|
type IndexInfo struct {
|
||||||
|
// Name is the name of the registry, such as "docker.io"
|
||||||
|
Name string
|
||||||
|
// Mirrors is a list of mirrors, expressed as URIs
|
||||||
|
Mirrors []string
|
||||||
|
// Secure is set to false if the registry is part of the list of
|
||||||
|
// insecure registries. Insecure registries accept HTTP and/or accept
|
||||||
|
// HTTPS with certificates from unknown CAs.
|
||||||
|
Secure bool
|
||||||
|
// Official indicates whether this is an official registry
|
||||||
|
Official bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchResult describes a search result returned from a registry
|
||||||
|
type SearchResult struct {
|
||||||
|
// StarCount indicates the number of stars this repository has
|
||||||
|
StarCount int `json:"star_count"`
|
||||||
|
// IsOfficial is true if the result is from an official repository.
|
||||||
|
IsOfficial bool `json:"is_official"`
|
||||||
|
// Name is the name of the repository
|
||||||
|
Name string `json:"name"`
|
||||||
|
// IsAutomated indicates whether the result is automated
|
||||||
|
IsAutomated bool `json:"is_automated"`
|
||||||
|
// Description is a textual description of the repository
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchResults lists a collection search results returned from a registry
|
||||||
|
type SearchResults struct {
|
||||||
|
// Query contains the query string that generated the search results
|
||||||
|
Query string `json:"query"`
|
||||||
|
// NumResults indicates the number of results the query returned
|
||||||
|
NumResults int `json:"num_results"`
|
||||||
|
// Results is a slice containing the actual results for the search
|
||||||
|
Results []SearchResult `json:"results"`
|
||||||
|
}
|
93
vendor/github.com/docker/docker/api/types/seccomp.go
generated
vendored
Normal file
93
vendor/github.com/docker/docker/api/types/seccomp.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// Seccomp represents the config for a seccomp profile for syscall restriction.
|
||||||
|
type Seccomp struct {
|
||||||
|
DefaultAction Action `json:"defaultAction"`
|
||||||
|
// Architectures is kept to maintain backward compatibility with the old
|
||||||
|
// seccomp profile.
|
||||||
|
Architectures []Arch `json:"architectures,omitempty"`
|
||||||
|
ArchMap []Architecture `json:"archMap,omitempty"`
|
||||||
|
Syscalls []*Syscall `json:"syscalls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Architecture is used to represent a specific architecture
|
||||||
|
// and its sub-architectures
|
||||||
|
type Architecture struct {
|
||||||
|
Arch Arch `json:"architecture"`
|
||||||
|
SubArches []Arch `json:"subArchitectures"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arch used for architectures
|
||||||
|
type Arch string
|
||||||
|
|
||||||
|
// Additional architectures permitted to be used for system calls
|
||||||
|
// By default only the native architecture of the kernel is permitted
|
||||||
|
const (
|
||||||
|
ArchX86 Arch = "SCMP_ARCH_X86"
|
||||||
|
ArchX86_64 Arch = "SCMP_ARCH_X86_64"
|
||||||
|
ArchX32 Arch = "SCMP_ARCH_X32"
|
||||||
|
ArchARM Arch = "SCMP_ARCH_ARM"
|
||||||
|
ArchAARCH64 Arch = "SCMP_ARCH_AARCH64"
|
||||||
|
ArchMIPS Arch = "SCMP_ARCH_MIPS"
|
||||||
|
ArchMIPS64 Arch = "SCMP_ARCH_MIPS64"
|
||||||
|
ArchMIPS64N32 Arch = "SCMP_ARCH_MIPS64N32"
|
||||||
|
ArchMIPSEL Arch = "SCMP_ARCH_MIPSEL"
|
||||||
|
ArchMIPSEL64 Arch = "SCMP_ARCH_MIPSEL64"
|
||||||
|
ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32"
|
||||||
|
ArchPPC Arch = "SCMP_ARCH_PPC"
|
||||||
|
ArchPPC64 Arch = "SCMP_ARCH_PPC64"
|
||||||
|
ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE"
|
||||||
|
ArchS390 Arch = "SCMP_ARCH_S390"
|
||||||
|
ArchS390X Arch = "SCMP_ARCH_S390X"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Action taken upon Seccomp rule match
|
||||||
|
type Action string
|
||||||
|
|
||||||
|
// Define actions for Seccomp rules
|
||||||
|
const (
|
||||||
|
ActKill Action = "SCMP_ACT_KILL"
|
||||||
|
ActTrap Action = "SCMP_ACT_TRAP"
|
||||||
|
ActErrno Action = "SCMP_ACT_ERRNO"
|
||||||
|
ActTrace Action = "SCMP_ACT_TRACE"
|
||||||
|
ActAllow Action = "SCMP_ACT_ALLOW"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Operator used to match syscall arguments in Seccomp
|
||||||
|
type Operator string
|
||||||
|
|
||||||
|
// Define operators for syscall arguments in Seccomp
|
||||||
|
const (
|
||||||
|
OpNotEqual Operator = "SCMP_CMP_NE"
|
||||||
|
OpLessThan Operator = "SCMP_CMP_LT"
|
||||||
|
OpLessEqual Operator = "SCMP_CMP_LE"
|
||||||
|
OpEqualTo Operator = "SCMP_CMP_EQ"
|
||||||
|
OpGreaterEqual Operator = "SCMP_CMP_GE"
|
||||||
|
OpGreaterThan Operator = "SCMP_CMP_GT"
|
||||||
|
OpMaskedEqual Operator = "SCMP_CMP_MASKED_EQ"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Arg used for matching specific syscall arguments in Seccomp
|
||||||
|
type Arg struct {
|
||||||
|
Index uint `json:"index"`
|
||||||
|
Value uint64 `json:"value"`
|
||||||
|
ValueTwo uint64 `json:"valueTwo"`
|
||||||
|
Op Operator `json:"op"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter is used to conditionally apply Seccomp rules
|
||||||
|
type Filter struct {
|
||||||
|
Caps []string `json:"caps,omitempty"`
|
||||||
|
Arches []string `json:"arches,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syscall is used to match a group of syscalls in Seccomp
|
||||||
|
type Syscall struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Names []string `json:"names,omitempty"`
|
||||||
|
Action Action `json:"action"`
|
||||||
|
Args []*Arg `json:"args"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
Includes Filter `json:"includes"`
|
||||||
|
Excludes Filter `json:"excludes"`
|
||||||
|
}
|
12
vendor/github.com/docker/docker/api/types/service_update_response.go
generated
vendored
Normal file
12
vendor/github.com/docker/docker/api/types/service_update_response.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// ServiceUpdateResponse service update response
|
||||||
|
// swagger:model ServiceUpdateResponse
|
||||||
|
type ServiceUpdateResponse struct {
|
||||||
|
|
||||||
|
// Optional warning messages
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
181
vendor/github.com/docker/docker/api/types/stats.go
generated
vendored
Normal file
181
vendor/github.com/docker/docker/api/types/stats.go
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
// Package types is used for API stability in the types and response to the
|
||||||
|
// consumers of the API stats endpoint.
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// ThrottlingData stores CPU throttling stats of one running container.
|
||||||
|
// Not used on Windows.
|
||||||
|
type ThrottlingData struct {
|
||||||
|
// Number of periods with throttling active
|
||||||
|
Periods uint64 `json:"periods"`
|
||||||
|
// Number of periods when the container hits its throttling limit.
|
||||||
|
ThrottledPeriods uint64 `json:"throttled_periods"`
|
||||||
|
// Aggregate time the container was throttled for in nanoseconds.
|
||||||
|
ThrottledTime uint64 `json:"throttled_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPUUsage stores All CPU stats aggregated since container inception.
|
||||||
|
type CPUUsage struct {
|
||||||
|
// Total CPU time consumed.
|
||||||
|
// Units: nanoseconds (Linux)
|
||||||
|
// Units: 100's of nanoseconds (Windows)
|
||||||
|
TotalUsage uint64 `json:"total_usage"`
|
||||||
|
|
||||||
|
// Total CPU time consumed per core (Linux). Not used on Windows.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
||||||
|
|
||||||
|
// Time spent by tasks of the cgroup in kernel mode (Linux).
|
||||||
|
// Time spent by all container processes in kernel mode (Windows).
|
||||||
|
// Units: nanoseconds (Linux).
|
||||||
|
// Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers.
|
||||||
|
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||||
|
|
||||||
|
// Time spent by tasks of the cgroup in user mode (Linux).
|
||||||
|
// Time spent by all container processes in user mode (Windows).
|
||||||
|
// Units: nanoseconds (Linux).
|
||||||
|
// Units: 100's of nanoseconds (Windows). Not populated for Hyper-V Containers
|
||||||
|
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPUStats aggregates and wraps all CPU related info of container
|
||||||
|
type CPUStats struct {
|
||||||
|
// CPU Usage. Linux and Windows.
|
||||||
|
CPUUsage CPUUsage `json:"cpu_usage"`
|
||||||
|
|
||||||
|
// System Usage. Linux only.
|
||||||
|
SystemUsage uint64 `json:"system_cpu_usage,omitempty"`
|
||||||
|
|
||||||
|
// Online CPUs. Linux only.
|
||||||
|
OnlineCPUs uint32 `json:"online_cpus,omitempty"`
|
||||||
|
|
||||||
|
// Throttling Data. Linux only.
|
||||||
|
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryStats aggregates all memory stats since container inception on Linux.
|
||||||
|
// Windows returns stats for commit and private working set only.
|
||||||
|
type MemoryStats struct {
|
||||||
|
// Linux Memory Stats
|
||||||
|
|
||||||
|
// current res_counter usage for memory
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
// maximum usage ever recorded.
|
||||||
|
MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||||
|
// TODO(vishh): Export these as stronger types.
|
||||||
|
// all the stats exported via memory.stat.
|
||||||
|
Stats map[string]uint64 `json:"stats,omitempty"`
|
||||||
|
// number of times memory usage hits limits.
|
||||||
|
Failcnt uint64 `json:"failcnt,omitempty"`
|
||||||
|
Limit uint64 `json:"limit,omitempty"`
|
||||||
|
|
||||||
|
// Windows Memory Stats
|
||||||
|
// See https://technet.microsoft.com/en-us/magazine/ff382715.aspx
|
||||||
|
|
||||||
|
// committed bytes
|
||||||
|
Commit uint64 `json:"commitbytes,omitempty"`
|
||||||
|
// peak committed bytes
|
||||||
|
CommitPeak uint64 `json:"commitpeakbytes,omitempty"`
|
||||||
|
// private working set
|
||||||
|
PrivateWorkingSet uint64 `json:"privateworkingset,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlkioStatEntry is one small entity to store a piece of Blkio stats
|
||||||
|
// Not used on Windows.
|
||||||
|
type BlkioStatEntry struct {
|
||||||
|
Major uint64 `json:"major"`
|
||||||
|
Minor uint64 `json:"minor"`
|
||||||
|
Op string `json:"op"`
|
||||||
|
Value uint64 `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlkioStats stores All IO service stats for data read and write.
|
||||||
|
// This is a Linux specific structure as the differences between expressing
|
||||||
|
// block I/O on Windows and Linux are sufficiently significant to make
|
||||||
|
// little sense attempting to morph into a combined structure.
|
||||||
|
type BlkioStats struct {
|
||||||
|
// number of bytes transferred to and from the block device
|
||||||
|
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"`
|
||||||
|
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"`
|
||||||
|
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"`
|
||||||
|
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"`
|
||||||
|
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"`
|
||||||
|
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"`
|
||||||
|
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"`
|
||||||
|
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageStats is the disk I/O stats for read/write on Windows.
|
||||||
|
type StorageStats struct {
|
||||||
|
ReadCountNormalized uint64 `json:"read_count_normalized,omitempty"`
|
||||||
|
ReadSizeBytes uint64 `json:"read_size_bytes,omitempty"`
|
||||||
|
WriteCountNormalized uint64 `json:"write_count_normalized,omitempty"`
|
||||||
|
WriteSizeBytes uint64 `json:"write_size_bytes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkStats aggregates the network stats of one container
|
||||||
|
type NetworkStats struct {
|
||||||
|
// Bytes received. Windows and Linux.
|
||||||
|
RxBytes uint64 `json:"rx_bytes"`
|
||||||
|
// Packets received. Windows and Linux.
|
||||||
|
RxPackets uint64 `json:"rx_packets"`
|
||||||
|
// Received errors. Not used on Windows. Note that we dont `omitempty` this
|
||||||
|
// field as it is expected in the >=v1.21 API stats structure.
|
||||||
|
RxErrors uint64 `json:"rx_errors"`
|
||||||
|
// Incoming packets dropped. Windows and Linux.
|
||||||
|
RxDropped uint64 `json:"rx_dropped"`
|
||||||
|
// Bytes sent. Windows and Linux.
|
||||||
|
TxBytes uint64 `json:"tx_bytes"`
|
||||||
|
// Packets sent. Windows and Linux.
|
||||||
|
TxPackets uint64 `json:"tx_packets"`
|
||||||
|
// Sent errors. Not used on Windows. Note that we dont `omitempty` this
|
||||||
|
// field as it is expected in the >=v1.21 API stats structure.
|
||||||
|
TxErrors uint64 `json:"tx_errors"`
|
||||||
|
// Outgoing packets dropped. Windows and Linux.
|
||||||
|
TxDropped uint64 `json:"tx_dropped"`
|
||||||
|
// Endpoint ID. Not used on Linux.
|
||||||
|
EndpointID string `json:"endpoint_id,omitempty"`
|
||||||
|
// Instance ID. Not used on Linux.
|
||||||
|
InstanceID string `json:"instance_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidsStats contains the stats of a container's pids
|
||||||
|
type PidsStats struct {
|
||||||
|
// Current is the number of pids in the cgroup
|
||||||
|
Current uint64 `json:"current,omitempty"`
|
||||||
|
// Limit is the hard limit on the number of pids in the cgroup.
|
||||||
|
// A "Limit" of 0 means that there is no limit.
|
||||||
|
Limit uint64 `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats is Ultimate struct aggregating all types of stats of one container
|
||||||
|
type Stats struct {
|
||||||
|
// Common stats
|
||||||
|
Read time.Time `json:"read"`
|
||||||
|
PreRead time.Time `json:"preread"`
|
||||||
|
|
||||||
|
// Linux specific stats, not populated on Windows.
|
||||||
|
PidsStats PidsStats `json:"pids_stats,omitempty"`
|
||||||
|
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||||
|
|
||||||
|
// Windows specific stats, not populated on Linux.
|
||||||
|
NumProcs uint32 `json:"num_procs"`
|
||||||
|
StorageStats StorageStats `json:"storage_stats,omitempty"`
|
||||||
|
|
||||||
|
// Shared stats
|
||||||
|
CPUStats CPUStats `json:"cpu_stats,omitempty"`
|
||||||
|
PreCPUStats CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous"
|
||||||
|
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsJSON is newly used Networks
|
||||||
|
type StatsJSON struct {
|
||||||
|
Stats
|
||||||
|
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
// Networks request version >=1.21
|
||||||
|
Networks map[string]NetworkStats `json:"networks,omitempty"`
|
||||||
|
}
|
30
vendor/github.com/docker/docker/api/types/strslice/strslice.go
generated
vendored
Normal file
30
vendor/github.com/docker/docker/api/types/strslice/strslice.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package strslice
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// StrSlice represents a string or an array of strings.
|
||||||
|
// We need to override the json decoder to accept both options.
|
||||||
|
type StrSlice []string
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes the byte slice whether it's a string or an array of
|
||||||
|
// strings. This method is needed to implement json.Unmarshaler.
|
||||||
|
func (e *StrSlice) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) == 0 {
|
||||||
|
// With no input, we preserve the existing value by returning nil and
|
||||||
|
// leaving the target alone. This allows defining default values for
|
||||||
|
// the type.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p := make([]string, 0, 1)
|
||||||
|
if err := json.Unmarshal(b, &p); err != nil {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p = append(p, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = p
|
||||||
|
return nil
|
||||||
|
}
|
27
vendor/github.com/docker/docker/api/types/swarm/common.go
generated
vendored
Normal file
27
vendor/github.com/docker/docker/api/types/swarm/common.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Version represents the internal object version.
|
||||||
|
type Version struct {
|
||||||
|
Index uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta is a base object inherited by most of the other once.
|
||||||
|
type Meta struct {
|
||||||
|
Version Version `json:",omitempty"`
|
||||||
|
CreatedAt time.Time `json:",omitempty"`
|
||||||
|
UpdatedAt time.Time `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotations represents how to describe an object.
|
||||||
|
type Annotations struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Labels map[string]string `json:"Labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Driver represents a driver (network, logging).
|
||||||
|
type Driver struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Options map[string]string `json:",omitempty"`
|
||||||
|
}
|
48
vendor/github.com/docker/docker/api/types/swarm/container.go
generated
vendored
Normal file
48
vendor/github.com/docker/docker/api/types/swarm/container.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSConfig specifies DNS related configurations in resolver configuration file (resolv.conf)
|
||||||
|
// Detailed documentation is available in:
|
||||||
|
// http://man7.org/linux/man-pages/man5/resolv.conf.5.html
|
||||||
|
// `nameserver`, `search`, `options` have been supported.
|
||||||
|
// TODO: `domain` is not supported yet.
|
||||||
|
type DNSConfig struct {
|
||||||
|
// Nameservers specifies the IP addresses of the name servers
|
||||||
|
Nameservers []string `json:",omitempty"`
|
||||||
|
// Search specifies the search list for host-name lookup
|
||||||
|
Search []string `json:",omitempty"`
|
||||||
|
// Options allows certain internal resolver variables to be modified
|
||||||
|
Options []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerSpec represents the spec of a container.
|
||||||
|
type ContainerSpec struct {
|
||||||
|
Image string `json:",omitempty"`
|
||||||
|
Labels map[string]string `json:",omitempty"`
|
||||||
|
Command []string `json:",omitempty"`
|
||||||
|
Args []string `json:",omitempty"`
|
||||||
|
Hostname string `json:",omitempty"`
|
||||||
|
Env []string `json:",omitempty"`
|
||||||
|
Dir string `json:",omitempty"`
|
||||||
|
User string `json:",omitempty"`
|
||||||
|
Groups []string `json:",omitempty"`
|
||||||
|
StopSignal string `json:",omitempty"`
|
||||||
|
TTY bool `json:",omitempty"`
|
||||||
|
OpenStdin bool `json:",omitempty"`
|
||||||
|
ReadOnly bool `json:",omitempty"`
|
||||||
|
Mounts []mount.Mount `json:",omitempty"`
|
||||||
|
StopGracePeriod *time.Duration `json:",omitempty"`
|
||||||
|
Healthcheck *container.HealthConfig `json:",omitempty"`
|
||||||
|
// The format of extra hosts on swarmkit is specified in:
|
||||||
|
// http://man7.org/linux/man-pages/man5/hosts.5.html
|
||||||
|
// IP_address canonical_hostname [aliases...]
|
||||||
|
Hosts []string `json:",omitempty"`
|
||||||
|
DNSConfig *DNSConfig `json:",omitempty"`
|
||||||
|
Secrets []*SecretReference `json:",omitempty"`
|
||||||
|
}
|
111
vendor/github.com/docker/docker/api/types/swarm/network.go
generated
vendored
Normal file
111
vendor/github.com/docker/docker/api/types/swarm/network.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
// Endpoint represents an endpoint.
|
||||||
|
type Endpoint struct {
|
||||||
|
Spec EndpointSpec `json:",omitempty"`
|
||||||
|
Ports []PortConfig `json:",omitempty"`
|
||||||
|
VirtualIPs []EndpointVirtualIP `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointSpec represents the spec of an endpoint.
|
||||||
|
type EndpointSpec struct {
|
||||||
|
Mode ResolutionMode `json:",omitempty"`
|
||||||
|
Ports []PortConfig `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolutionMode represents a resolution mode.
|
||||||
|
type ResolutionMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ResolutionModeVIP VIP
|
||||||
|
ResolutionModeVIP ResolutionMode = "vip"
|
||||||
|
// ResolutionModeDNSRR DNSRR
|
||||||
|
ResolutionModeDNSRR ResolutionMode = "dnsrr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PortConfig represents the config of a port.
|
||||||
|
type PortConfig struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Protocol PortConfigProtocol `json:",omitempty"`
|
||||||
|
// TargetPort is the port inside the container
|
||||||
|
TargetPort uint32 `json:",omitempty"`
|
||||||
|
// PublishedPort is the port on the swarm hosts
|
||||||
|
PublishedPort uint32 `json:",omitempty"`
|
||||||
|
// PublishMode is the mode in which port is published
|
||||||
|
PublishMode PortConfigPublishMode `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortConfigPublishMode represents the mode in which the port is to
|
||||||
|
// be published.
|
||||||
|
type PortConfigPublishMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PortConfigPublishModeIngress is used for ports published
|
||||||
|
// for ingress load balancing using routing mesh.
|
||||||
|
PortConfigPublishModeIngress PortConfigPublishMode = "ingress"
|
||||||
|
// PortConfigPublishModeHost is used for ports published
|
||||||
|
// for direct host level access on the host where the task is running.
|
||||||
|
PortConfigPublishModeHost PortConfigPublishMode = "host"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PortConfigProtocol represents the protocol of a port.
|
||||||
|
type PortConfigProtocol string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TODO(stevvooe): These should be used generally, not just for PortConfig.
|
||||||
|
|
||||||
|
// PortConfigProtocolTCP TCP
|
||||||
|
PortConfigProtocolTCP PortConfigProtocol = "tcp"
|
||||||
|
// PortConfigProtocolUDP UDP
|
||||||
|
PortConfigProtocolUDP PortConfigProtocol = "udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EndpointVirtualIP represents the virtual ip of a port.
|
||||||
|
type EndpointVirtualIP struct {
|
||||||
|
NetworkID string `json:",omitempty"`
|
||||||
|
Addr string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network represents a network.
|
||||||
|
type Network struct {
|
||||||
|
ID string
|
||||||
|
Meta
|
||||||
|
Spec NetworkSpec `json:",omitempty"`
|
||||||
|
DriverState Driver `json:",omitempty"`
|
||||||
|
IPAMOptions *IPAMOptions `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkSpec represents the spec of a network.
|
||||||
|
type NetworkSpec struct {
|
||||||
|
Annotations
|
||||||
|
DriverConfiguration *Driver `json:",omitempty"`
|
||||||
|
IPv6Enabled bool `json:",omitempty"`
|
||||||
|
Internal bool `json:",omitempty"`
|
||||||
|
Attachable bool `json:",omitempty"`
|
||||||
|
IPAMOptions *IPAMOptions `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkAttachmentConfig represents the configuration of a network attachment.
|
||||||
|
type NetworkAttachmentConfig struct {
|
||||||
|
Target string `json:",omitempty"`
|
||||||
|
Aliases []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkAttachment represents a network attachment.
|
||||||
|
type NetworkAttachment struct {
|
||||||
|
Network Network `json:",omitempty"`
|
||||||
|
Addresses []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAMOptions represents ipam options.
|
||||||
|
type IPAMOptions struct {
|
||||||
|
Driver Driver `json:",omitempty"`
|
||||||
|
Configs []IPAMConfig `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAMConfig represents ipam configuration.
|
||||||
|
type IPAMConfig struct {
|
||||||
|
Subnet string `json:",omitempty"`
|
||||||
|
Range string `json:",omitempty"`
|
||||||
|
Gateway string `json:",omitempty"`
|
||||||
|
}
|
114
vendor/github.com/docker/docker/api/types/swarm/node.go
generated
vendored
Normal file
114
vendor/github.com/docker/docker/api/types/swarm/node.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
// Node represents a node.
|
||||||
|
type Node struct {
|
||||||
|
ID string
|
||||||
|
Meta
|
||||||
|
// Spec defines the desired state of the node as specified by the user.
|
||||||
|
// The system will honor this and will *never* modify it.
|
||||||
|
Spec NodeSpec `json:",omitempty"`
|
||||||
|
// Description encapsulates the properties of the Node as reported by the
|
||||||
|
// agent.
|
||||||
|
Description NodeDescription `json:",omitempty"`
|
||||||
|
// Status provides the current status of the node, as seen by the manager.
|
||||||
|
Status NodeStatus `json:",omitempty"`
|
||||||
|
// ManagerStatus provides the current status of the node's manager
|
||||||
|
// component, if the node is a manager.
|
||||||
|
ManagerStatus *ManagerStatus `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeSpec represents the spec of a node.
|
||||||
|
type NodeSpec struct {
|
||||||
|
Annotations
|
||||||
|
Role NodeRole `json:",omitempty"`
|
||||||
|
Availability NodeAvailability `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRole represents the role of a node.
|
||||||
|
type NodeRole string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NodeRoleWorker WORKER
|
||||||
|
NodeRoleWorker NodeRole = "worker"
|
||||||
|
// NodeRoleManager MANAGER
|
||||||
|
NodeRoleManager NodeRole = "manager"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeAvailability represents the availability of a node.
|
||||||
|
type NodeAvailability string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NodeAvailabilityActive ACTIVE
|
||||||
|
NodeAvailabilityActive NodeAvailability = "active"
|
||||||
|
// NodeAvailabilityPause PAUSE
|
||||||
|
NodeAvailabilityPause NodeAvailability = "pause"
|
||||||
|
// NodeAvailabilityDrain DRAIN
|
||||||
|
NodeAvailabilityDrain NodeAvailability = "drain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeDescription represents the description of a node.
|
||||||
|
type NodeDescription struct {
|
||||||
|
Hostname string `json:",omitempty"`
|
||||||
|
Platform Platform `json:",omitempty"`
|
||||||
|
Resources Resources `json:",omitempty"`
|
||||||
|
Engine EngineDescription `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Platform represents the platform (Arch/OS).
|
||||||
|
type Platform struct {
|
||||||
|
Architecture string `json:",omitempty"`
|
||||||
|
OS string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EngineDescription represents the description of an engine.
|
||||||
|
type EngineDescription struct {
|
||||||
|
EngineVersion string `json:",omitempty"`
|
||||||
|
Labels map[string]string `json:",omitempty"`
|
||||||
|
Plugins []PluginDescription `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginDescription represents the description of an engine plugin.
|
||||||
|
type PluginDescription struct {
|
||||||
|
Type string `json:",omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeStatus represents the status of a node.
|
||||||
|
type NodeStatus struct {
|
||||||
|
State NodeState `json:",omitempty"`
|
||||||
|
Message string `json:",omitempty"`
|
||||||
|
Addr string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reachability represents the reachability of a node.
|
||||||
|
type Reachability string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ReachabilityUnknown UNKNOWN
|
||||||
|
ReachabilityUnknown Reachability = "unknown"
|
||||||
|
// ReachabilityUnreachable UNREACHABLE
|
||||||
|
ReachabilityUnreachable Reachability = "unreachable"
|
||||||
|
// ReachabilityReachable REACHABLE
|
||||||
|
ReachabilityReachable Reachability = "reachable"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ManagerStatus represents the status of a manager.
|
||||||
|
type ManagerStatus struct {
|
||||||
|
Leader bool `json:",omitempty"`
|
||||||
|
Reachability Reachability `json:",omitempty"`
|
||||||
|
Addr string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeState represents the state of a node.
|
||||||
|
type NodeState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NodeStateUnknown UNKNOWN
|
||||||
|
NodeStateUnknown NodeState = "unknown"
|
||||||
|
// NodeStateDown DOWN
|
||||||
|
NodeStateDown NodeState = "down"
|
||||||
|
// NodeStateReady READY
|
||||||
|
NodeStateReady NodeState = "ready"
|
||||||
|
// NodeStateDisconnected DISCONNECTED
|
||||||
|
NodeStateDisconnected NodeState = "disconnected"
|
||||||
|
)
|
31
vendor/github.com/docker/docker/api/types/swarm/secret.go
generated
vendored
Normal file
31
vendor/github.com/docker/docker/api/types/swarm/secret.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// Secret represents a secret.
|
||||||
|
type Secret struct {
|
||||||
|
ID string
|
||||||
|
Meta
|
||||||
|
Spec SecretSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretSpec represents a secret specification from a secret in swarm
|
||||||
|
type SecretSpec struct {
|
||||||
|
Annotations
|
||||||
|
Data []byte `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretReferenceFileTarget is a file target in a secret reference
|
||||||
|
type SecretReferenceFileTarget struct {
|
||||||
|
Name string
|
||||||
|
UID string
|
||||||
|
GID string
|
||||||
|
Mode os.FileMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretReference is a reference to a secret in swarm
|
||||||
|
type SecretReference struct {
|
||||||
|
File *SecretReferenceFileTarget
|
||||||
|
SecretID string
|
||||||
|
SecretName string
|
||||||
|
}
|
114
vendor/github.com/docker/docker/api/types/swarm/service.go
generated
vendored
Normal file
114
vendor/github.com/docker/docker/api/types/swarm/service.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Service represents a service.
|
||||||
|
type Service struct {
|
||||||
|
ID string
|
||||||
|
Meta
|
||||||
|
Spec ServiceSpec `json:",omitempty"`
|
||||||
|
PreviousSpec *ServiceSpec `json:",omitempty"`
|
||||||
|
Endpoint Endpoint `json:",omitempty"`
|
||||||
|
UpdateStatus *UpdateStatus `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceSpec represents the spec of a service.
|
||||||
|
type ServiceSpec struct {
|
||||||
|
Annotations
|
||||||
|
|
||||||
|
// TaskTemplate defines how the service should construct new tasks when
|
||||||
|
// orchestrating this service.
|
||||||
|
TaskTemplate TaskSpec `json:",omitempty"`
|
||||||
|
Mode ServiceMode `json:",omitempty"`
|
||||||
|
UpdateConfig *UpdateConfig `json:",omitempty"`
|
||||||
|
RollbackConfig *UpdateConfig `json:",omitempty"`
|
||||||
|
|
||||||
|
// Networks field in ServiceSpec is deprecated. The
|
||||||
|
// same field in TaskSpec should be used instead.
|
||||||
|
// This field will be removed in a future release.
|
||||||
|
Networks []NetworkAttachmentConfig `json:",omitempty"`
|
||||||
|
EndpointSpec *EndpointSpec `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceMode represents the mode of a service.
|
||||||
|
type ServiceMode struct {
|
||||||
|
Replicated *ReplicatedService `json:",omitempty"`
|
||||||
|
Global *GlobalService `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateState is the state of a service update.
|
||||||
|
type UpdateState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UpdateStateUpdating is the updating state.
|
||||||
|
UpdateStateUpdating UpdateState = "updating"
|
||||||
|
// UpdateStatePaused is the paused state.
|
||||||
|
UpdateStatePaused UpdateState = "paused"
|
||||||
|
// UpdateStateCompleted is the completed state.
|
||||||
|
UpdateStateCompleted UpdateState = "completed"
|
||||||
|
// UpdateStateRollbackStarted is the state with a rollback in progress.
|
||||||
|
UpdateStateRollbackStarted UpdateState = "rollback_started"
|
||||||
|
// UpdateStateRollbackPaused is the state with a rollback in progress.
|
||||||
|
UpdateStateRollbackPaused UpdateState = "rollback_paused"
|
||||||
|
// UpdateStateRollbackCompleted is the state with a rollback in progress.
|
||||||
|
UpdateStateRollbackCompleted UpdateState = "rollback_completed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateStatus reports the status of a service update.
|
||||||
|
type UpdateStatus struct {
|
||||||
|
State UpdateState `json:",omitempty"`
|
||||||
|
StartedAt *time.Time `json:",omitempty"`
|
||||||
|
CompletedAt *time.Time `json:",omitempty"`
|
||||||
|
Message string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplicatedService is a kind of ServiceMode.
|
||||||
|
type ReplicatedService struct {
|
||||||
|
Replicas *uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalService is a kind of ServiceMode.
|
||||||
|
type GlobalService struct{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UpdateFailureActionPause PAUSE
|
||||||
|
UpdateFailureActionPause = "pause"
|
||||||
|
// UpdateFailureActionContinue CONTINUE
|
||||||
|
UpdateFailureActionContinue = "continue"
|
||||||
|
// UpdateFailureActionRollback ROLLBACK
|
||||||
|
UpdateFailureActionRollback = "rollback"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateConfig represents the update configuration.
|
||||||
|
type UpdateConfig struct {
|
||||||
|
// Maximum number of tasks to be updated in one iteration.
|
||||||
|
// 0 means unlimited parallelism.
|
||||||
|
Parallelism uint64
|
||||||
|
|
||||||
|
// Amount of time between updates.
|
||||||
|
Delay time.Duration `json:",omitempty"`
|
||||||
|
|
||||||
|
// FailureAction is the action to take when an update failures.
|
||||||
|
FailureAction string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Monitor indicates how long to monitor a task for failure after it is
|
||||||
|
// created. If the task fails by ending up in one of the states
|
||||||
|
// REJECTED, COMPLETED, or FAILED, within Monitor from its creation,
|
||||||
|
// this counts as a failure. If it fails after Monitor, it does not
|
||||||
|
// count as a failure. If Monitor is unspecified, a default value will
|
||||||
|
// be used.
|
||||||
|
Monitor time.Duration `json:",omitempty"`
|
||||||
|
|
||||||
|
// MaxFailureRatio is the fraction of tasks that may fail during
|
||||||
|
// an update before the failure action is invoked. Any task created by
|
||||||
|
// the current update which ends up in one of the states REJECTED,
|
||||||
|
// COMPLETED or FAILED within Monitor from its creation counts as a
|
||||||
|
// failure. The number of failures is divided by the number of tasks
|
||||||
|
// being updated, and if this fraction is greater than
|
||||||
|
// MaxFailureRatio, the failure action is invoked.
|
||||||
|
//
|
||||||
|
// If the failure action is CONTINUE, there is no effect.
|
||||||
|
// If the failure action is PAUSE, no more tasks will be updated until
|
||||||
|
// another update is started.
|
||||||
|
MaxFailureRatio float32
|
||||||
|
}
|
199
vendor/github.com/docker/docker/api/types/swarm/swarm.go
generated
vendored
Normal file
199
vendor/github.com/docker/docker/api/types/swarm/swarm.go
generated
vendored
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// ClusterInfo represents info about the cluster for outputing in "info"
|
||||||
|
// it contains the same information as "Swarm", but without the JoinTokens
|
||||||
|
type ClusterInfo struct {
|
||||||
|
ID string
|
||||||
|
Meta
|
||||||
|
Spec Spec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swarm represents a swarm.
|
||||||
|
type Swarm struct {
|
||||||
|
ClusterInfo
|
||||||
|
JoinTokens JoinTokens
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinTokens contains the tokens workers and managers need to join the swarm.
|
||||||
|
type JoinTokens struct {
|
||||||
|
// Worker is the join token workers may use to join the swarm.
|
||||||
|
Worker string
|
||||||
|
// Manager is the join token managers may use to join the swarm.
|
||||||
|
Manager string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spec represents the spec of a swarm.
|
||||||
|
type Spec struct {
|
||||||
|
Annotations
|
||||||
|
|
||||||
|
Orchestration OrchestrationConfig `json:",omitempty"`
|
||||||
|
Raft RaftConfig `json:",omitempty"`
|
||||||
|
Dispatcher DispatcherConfig `json:",omitempty"`
|
||||||
|
CAConfig CAConfig `json:",omitempty"`
|
||||||
|
TaskDefaults TaskDefaults `json:",omitempty"`
|
||||||
|
EncryptionConfig EncryptionConfig `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrchestrationConfig represents orchestration configuration.
|
||||||
|
type OrchestrationConfig struct {
|
||||||
|
// TaskHistoryRetentionLimit is the number of historic tasks to keep per instance or
|
||||||
|
// node. If negative, never remove completed or failed tasks.
|
||||||
|
TaskHistoryRetentionLimit *int64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskDefaults parameterizes cluster-level task creation with default values.
|
||||||
|
type TaskDefaults struct {
|
||||||
|
// LogDriver selects the log driver to use for tasks created in the
|
||||||
|
// orchestrator if unspecified by a service.
|
||||||
|
//
|
||||||
|
// Updating this value will only have an affect on new tasks. Old tasks
|
||||||
|
// will continue use their previously configured log driver until
|
||||||
|
// recreated.
|
||||||
|
LogDriver *Driver `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptionConfig controls at-rest encryption of data and keys.
|
||||||
|
type EncryptionConfig struct {
|
||||||
|
// AutoLockManagers specifies whether or not managers TLS keys and raft data
|
||||||
|
// should be encrypted at rest in such a way that they must be unlocked
|
||||||
|
// before the manager node starts up again.
|
||||||
|
AutoLockManagers bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaftConfig represents raft configuration.
|
||||||
|
type RaftConfig struct {
|
||||||
|
// SnapshotInterval is the number of log entries between snapshots.
|
||||||
|
SnapshotInterval uint64 `json:",omitempty"`
|
||||||
|
|
||||||
|
// KeepOldSnapshots is the number of snapshots to keep beyond the
|
||||||
|
// current snapshot.
|
||||||
|
KeepOldSnapshots *uint64 `json:",omitempty"`
|
||||||
|
|
||||||
|
// LogEntriesForSlowFollowers is the number of log entries to keep
|
||||||
|
// around to sync up slow followers after a snapshot is created.
|
||||||
|
LogEntriesForSlowFollowers uint64 `json:",omitempty"`
|
||||||
|
|
||||||
|
// ElectionTick is the number of ticks that a follower will wait for a message
|
||||||
|
// from the leader before becoming a candidate and starting an election.
|
||||||
|
// ElectionTick must be greater than HeartbeatTick.
|
||||||
|
//
|
||||||
|
// A tick currently defaults to one second, so these translate directly to
|
||||||
|
// seconds currently, but this is NOT guaranteed.
|
||||||
|
ElectionTick int
|
||||||
|
|
||||||
|
// HeartbeatTick is the number of ticks between heartbeats. Every
|
||||||
|
// HeartbeatTick ticks, the leader will send a heartbeat to the
|
||||||
|
// followers.
|
||||||
|
//
|
||||||
|
// A tick currently defaults to one second, so these translate directly to
|
||||||
|
// seconds currently, but this is NOT guaranteed.
|
||||||
|
HeartbeatTick int
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatcherConfig represents dispatcher configuration.
|
||||||
|
type DispatcherConfig struct {
|
||||||
|
// HeartbeatPeriod defines how often agent should send heartbeats to
|
||||||
|
// dispatcher.
|
||||||
|
HeartbeatPeriod time.Duration `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAConfig represents CA configuration.
|
||||||
|
type CAConfig struct {
|
||||||
|
// NodeCertExpiry is the duration certificates should be issued for
|
||||||
|
NodeCertExpiry time.Duration `json:",omitempty"`
|
||||||
|
|
||||||
|
// ExternalCAs is a list of CAs to which a manager node will make
|
||||||
|
// certificate signing requests for node certificates.
|
||||||
|
ExternalCAs []*ExternalCA `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalCAProtocol represents type of external CA.
|
||||||
|
type ExternalCAProtocol string
|
||||||
|
|
||||||
|
// ExternalCAProtocolCFSSL CFSSL
|
||||||
|
const ExternalCAProtocolCFSSL ExternalCAProtocol = "cfssl"
|
||||||
|
|
||||||
|
// ExternalCA defines external CA to be used by the cluster.
|
||||||
|
type ExternalCA struct {
|
||||||
|
// Protocol is the protocol used by this external CA.
|
||||||
|
Protocol ExternalCAProtocol
|
||||||
|
|
||||||
|
// URL is the URL where the external CA can be reached.
|
||||||
|
URL string
|
||||||
|
|
||||||
|
// Options is a set of additional key/value pairs whose interpretation
|
||||||
|
// depends on the specified CA type.
|
||||||
|
Options map[string]string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitRequest is the request used to init a swarm.
|
||||||
|
type InitRequest struct {
|
||||||
|
ListenAddr string
|
||||||
|
AdvertiseAddr string
|
||||||
|
ForceNewCluster bool
|
||||||
|
Spec Spec
|
||||||
|
AutoLockManagers bool
|
||||||
|
Availability NodeAvailability
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinRequest is the request used to join a swarm.
|
||||||
|
type JoinRequest struct {
|
||||||
|
ListenAddr string
|
||||||
|
AdvertiseAddr string
|
||||||
|
RemoteAddrs []string
|
||||||
|
JoinToken string // accept by secret
|
||||||
|
Availability NodeAvailability
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnlockRequest is the request used to unlock a swarm.
|
||||||
|
type UnlockRequest struct {
|
||||||
|
// UnlockKey is the unlock key in ASCII-armored format.
|
||||||
|
UnlockKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalNodeState represents the state of the local node.
|
||||||
|
type LocalNodeState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LocalNodeStateInactive INACTIVE
|
||||||
|
LocalNodeStateInactive LocalNodeState = "inactive"
|
||||||
|
// LocalNodeStatePending PENDING
|
||||||
|
LocalNodeStatePending LocalNodeState = "pending"
|
||||||
|
// LocalNodeStateActive ACTIVE
|
||||||
|
LocalNodeStateActive LocalNodeState = "active"
|
||||||
|
// LocalNodeStateError ERROR
|
||||||
|
LocalNodeStateError LocalNodeState = "error"
|
||||||
|
// LocalNodeStateLocked LOCKED
|
||||||
|
LocalNodeStateLocked LocalNodeState = "locked"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Info represents generic information about swarm.
|
||||||
|
type Info struct {
|
||||||
|
NodeID string
|
||||||
|
NodeAddr string
|
||||||
|
|
||||||
|
LocalNodeState LocalNodeState
|
||||||
|
ControlAvailable bool
|
||||||
|
Error string
|
||||||
|
|
||||||
|
RemoteManagers []Peer
|
||||||
|
Nodes int
|
||||||
|
Managers int
|
||||||
|
|
||||||
|
Cluster ClusterInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peer represents a peer.
|
||||||
|
type Peer struct {
|
||||||
|
NodeID string
|
||||||
|
Addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFlags contains flags for SwarmUpdate.
|
||||||
|
type UpdateFlags struct {
|
||||||
|
RotateWorkerToken bool
|
||||||
|
RotateManagerToken bool
|
||||||
|
RotateManagerUnlockKey bool
|
||||||
|
}
|
142
vendor/github.com/docker/docker/api/types/swarm/task.go
generated
vendored
Normal file
142
vendor/github.com/docker/docker/api/types/swarm/task.go
generated
vendored
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package swarm
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// TaskState represents the state of a task.
|
||||||
|
type TaskState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TaskStateNew NEW
|
||||||
|
TaskStateNew TaskState = "new"
|
||||||
|
// TaskStateAllocated ALLOCATED
|
||||||
|
TaskStateAllocated TaskState = "allocated"
|
||||||
|
// TaskStatePending PENDING
|
||||||
|
TaskStatePending TaskState = "pending"
|
||||||
|
// TaskStateAssigned ASSIGNED
|
||||||
|
TaskStateAssigned TaskState = "assigned"
|
||||||
|
// TaskStateAccepted ACCEPTED
|
||||||
|
TaskStateAccepted TaskState = "accepted"
|
||||||
|
// TaskStatePreparing PREPARING
|
||||||
|
TaskStatePreparing TaskState = "preparing"
|
||||||
|
// TaskStateReady READY
|
||||||
|
TaskStateReady TaskState = "ready"
|
||||||
|
// TaskStateStarting STARTING
|
||||||
|
TaskStateStarting TaskState = "starting"
|
||||||
|
// TaskStateRunning RUNNING
|
||||||
|
TaskStateRunning TaskState = "running"
|
||||||
|
// TaskStateComplete COMPLETE
|
||||||
|
TaskStateComplete TaskState = "complete"
|
||||||
|
// TaskStateShutdown SHUTDOWN
|
||||||
|
TaskStateShutdown TaskState = "shutdown"
|
||||||
|
// TaskStateFailed FAILED
|
||||||
|
TaskStateFailed TaskState = "failed"
|
||||||
|
// TaskStateRejected REJECTED
|
||||||
|
TaskStateRejected TaskState = "rejected"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Task represents a task.
|
||||||
|
type Task struct {
|
||||||
|
ID string
|
||||||
|
Meta
|
||||||
|
Annotations
|
||||||
|
|
||||||
|
Spec TaskSpec `json:",omitempty"`
|
||||||
|
ServiceID string `json:",omitempty"`
|
||||||
|
Slot int `json:",omitempty"`
|
||||||
|
NodeID string `json:",omitempty"`
|
||||||
|
Status TaskStatus `json:",omitempty"`
|
||||||
|
DesiredState TaskState `json:",omitempty"`
|
||||||
|
NetworksAttachments []NetworkAttachment `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskSpec represents the spec of a task.
|
||||||
|
type TaskSpec struct {
|
||||||
|
ContainerSpec ContainerSpec `json:",omitempty"`
|
||||||
|
Resources *ResourceRequirements `json:",omitempty"`
|
||||||
|
RestartPolicy *RestartPolicy `json:",omitempty"`
|
||||||
|
Placement *Placement `json:",omitempty"`
|
||||||
|
Networks []NetworkAttachmentConfig `json:",omitempty"`
|
||||||
|
|
||||||
|
// LogDriver specifies the LogDriver to use for tasks created from this
|
||||||
|
// spec. If not present, the one on cluster default on swarm.Spec will be
|
||||||
|
// used, finally falling back to the engine default if not specified.
|
||||||
|
LogDriver *Driver `json:",omitempty"`
|
||||||
|
|
||||||
|
// ForceUpdate is a counter that triggers an update even if no relevant
|
||||||
|
// parameters have been changed.
|
||||||
|
ForceUpdate uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources represents resources (CPU/Memory).
|
||||||
|
type Resources struct {
|
||||||
|
NanoCPUs int64 `json:",omitempty"`
|
||||||
|
MemoryBytes int64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceRequirements represents resources requirements.
|
||||||
|
type ResourceRequirements struct {
|
||||||
|
Limits *Resources `json:",omitempty"`
|
||||||
|
Reservations *Resources `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placement represents orchestration parameters.
|
||||||
|
type Placement struct {
|
||||||
|
Constraints []string `json:",omitempty"`
|
||||||
|
Preferences []PlacementPreference `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlacementPreference provides a way to make the scheduler aware of factors
|
||||||
|
// such as topology.
|
||||||
|
type PlacementPreference struct {
|
||||||
|
Spread *SpreadOver
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpreadOver is a scheduling preference that instructs the scheduler to spread
|
||||||
|
// tasks evenly over groups of nodes identified by labels.
|
||||||
|
type SpreadOver struct {
|
||||||
|
// label descriptor, such as engine.labels.az
|
||||||
|
SpreadDescriptor string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartPolicy represents the restart policy.
|
||||||
|
type RestartPolicy struct {
|
||||||
|
Condition RestartPolicyCondition `json:",omitempty"`
|
||||||
|
Delay *time.Duration `json:",omitempty"`
|
||||||
|
MaxAttempts *uint64 `json:",omitempty"`
|
||||||
|
Window *time.Duration `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestartPolicyCondition represents when to restart.
|
||||||
|
type RestartPolicyCondition string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RestartPolicyConditionNone NONE
|
||||||
|
RestartPolicyConditionNone RestartPolicyCondition = "none"
|
||||||
|
// RestartPolicyConditionOnFailure ON_FAILURE
|
||||||
|
RestartPolicyConditionOnFailure RestartPolicyCondition = "on-failure"
|
||||||
|
// RestartPolicyConditionAny ANY
|
||||||
|
RestartPolicyConditionAny RestartPolicyCondition = "any"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TaskStatus represents the status of a task.
|
||||||
|
type TaskStatus struct {
|
||||||
|
Timestamp time.Time `json:",omitempty"`
|
||||||
|
State TaskState `json:",omitempty"`
|
||||||
|
Message string `json:",omitempty"`
|
||||||
|
Err string `json:",omitempty"`
|
||||||
|
ContainerStatus ContainerStatus `json:",omitempty"`
|
||||||
|
PortStatus PortStatus `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStatus represents the status of a container.
|
||||||
|
type ContainerStatus struct {
|
||||||
|
ContainerID string `json:",omitempty"`
|
||||||
|
PID int `json:",omitempty"`
|
||||||
|
ExitCode int `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortStatus represents the port status of a task's host ports whose
|
||||||
|
// service has published host ports
|
||||||
|
type PortStatus struct {
|
||||||
|
Ports []PortConfig `json:",omitempty"`
|
||||||
|
}
|
12
vendor/github.com/docker/docker/api/types/time/duration_convert.go
generated
vendored
Normal file
12
vendor/github.com/docker/docker/api/types/time/duration_convert.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package time
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DurationToSecondsString converts the specified duration to the number
|
||||||
|
// seconds it represents, formatted as a string.
|
||||||
|
func DurationToSecondsString(duration time.Duration) string {
|
||||||
|
return strconv.FormatFloat(duration.Seconds(), 'f', 0, 64)
|
||||||
|
}
|
124
vendor/github.com/docker/docker/api/types/time/timestamp.go
generated
vendored
Normal file
124
vendor/github.com/docker/docker/api/types/time/timestamp.go
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
package time
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These are additional predefined layouts for use in Time.Format and Time.Parse
|
||||||
|
// with --since and --until parameters for `docker logs` and `docker events`
|
||||||
|
const (
|
||||||
|
rFC3339Local = "2006-01-02T15:04:05" // RFC3339 with local timezone
|
||||||
|
rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone
|
||||||
|
dateWithZone = "2006-01-02Z07:00" // RFC3339 with time at 00:00:00
|
||||||
|
dateLocal = "2006-01-02" // RFC3339 with local timezone and time at 00:00:00
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetTimestamp tries to parse given string as golang duration,
|
||||||
|
// then RFC3339 time and finally as a Unix timestamp. If
|
||||||
|
// any of these were successful, it returns a Unix timestamp
|
||||||
|
// as string otherwise returns the given value back.
|
||||||
|
// In case of duration input, the returned timestamp is computed
|
||||||
|
// as the given reference time minus the amount of the duration.
|
||||||
|
func GetTimestamp(value string, reference time.Time) (string, error) {
|
||||||
|
if d, err := time.ParseDuration(value); value != "0" && err == nil {
|
||||||
|
return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var format string
|
||||||
|
var parseInLocation bool
|
||||||
|
|
||||||
|
// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
|
||||||
|
parseInLocation = !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
|
||||||
|
|
||||||
|
if strings.Contains(value, ".") {
|
||||||
|
if parseInLocation {
|
||||||
|
format = rFC3339NanoLocal
|
||||||
|
} else {
|
||||||
|
format = time.RFC3339Nano
|
||||||
|
}
|
||||||
|
} else if strings.Contains(value, "T") {
|
||||||
|
// we want the number of colons in the T portion of the timestamp
|
||||||
|
tcolons := strings.Count(value, ":")
|
||||||
|
// if parseInLocation is off and we have a +/- zone offset (not Z) then
|
||||||
|
// there will be an extra colon in the input for the tz offset subtract that
|
||||||
|
// colon from the tcolons count
|
||||||
|
if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 {
|
||||||
|
tcolons--
|
||||||
|
}
|
||||||
|
if parseInLocation {
|
||||||
|
switch tcolons {
|
||||||
|
case 0:
|
||||||
|
format = "2006-01-02T15"
|
||||||
|
case 1:
|
||||||
|
format = "2006-01-02T15:04"
|
||||||
|
default:
|
||||||
|
format = rFC3339Local
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch tcolons {
|
||||||
|
case 0:
|
||||||
|
format = "2006-01-02T15Z07:00"
|
||||||
|
case 1:
|
||||||
|
format = "2006-01-02T15:04Z07:00"
|
||||||
|
default:
|
||||||
|
format = time.RFC3339
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if parseInLocation {
|
||||||
|
format = dateLocal
|
||||||
|
} else {
|
||||||
|
format = dateWithZone
|
||||||
|
}
|
||||||
|
|
||||||
|
var t time.Time
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if parseInLocation {
|
||||||
|
t, err = time.ParseInLocation(format, value, time.FixedZone(reference.Zone()))
|
||||||
|
} else {
|
||||||
|
t, err = time.Parse(format, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// if there is a `-` then it's an RFC3339 like timestamp otherwise assume unixtimestamp
|
||||||
|
if strings.Contains(value, "-") {
|
||||||
|
return "", err // was probably an RFC3339 like timestamp but the parser failed with an error
|
||||||
|
}
|
||||||
|
return value, nil // unixtimestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
|
||||||
|
// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
|
||||||
|
// if the incoming nanosecond portion is longer or shorter than 9 digits it is
|
||||||
|
// converted to nanoseconds. The expectation is that the seconds and
|
||||||
|
// seconds will be used to create a time variable. For example:
|
||||||
|
// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
|
||||||
|
// if err == nil since := time.Unix(seconds, nanoseconds)
|
||||||
|
// returns seconds as def(aultSeconds) if value == ""
|
||||||
|
func ParseTimestamps(value string, def int64) (int64, int64, error) {
|
||||||
|
if value == "" {
|
||||||
|
return def, 0, nil
|
||||||
|
}
|
||||||
|
sa := strings.SplitN(value, ".", 2)
|
||||||
|
s, err := strconv.ParseInt(sa[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return s, 0, err
|
||||||
|
}
|
||||||
|
if len(sa) != 2 {
|
||||||
|
return s, 0, nil
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseInt(sa[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return s, n, err
|
||||||
|
}
|
||||||
|
// should already be in nanoseconds but just in case convert n to nanoseconds
|
||||||
|
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
|
||||||
|
return s, n, nil
|
||||||
|
}
|
528
vendor/github.com/docker/docker/api/types/types.go
generated
vendored
Normal file
528
vendor/github.com/docker/docker/api/types/types.go
generated
vendored
Normal file
|
@ -0,0 +1,528 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
"github.com/docker/docker/api/types/registry"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RootFS returns Image's RootFS description including the layer IDs.
|
||||||
|
type RootFS struct {
|
||||||
|
Type string
|
||||||
|
Layers []string `json:",omitempty"`
|
||||||
|
BaseLayer string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageInspect contains response of Engine API:
|
||||||
|
// GET "/images/{name:.*}/json"
|
||||||
|
type ImageInspect struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
RepoTags []string
|
||||||
|
RepoDigests []string
|
||||||
|
Parent string
|
||||||
|
Comment string
|
||||||
|
Created string
|
||||||
|
Container string
|
||||||
|
ContainerConfig *container.Config
|
||||||
|
DockerVersion string
|
||||||
|
Author string
|
||||||
|
Config *container.Config
|
||||||
|
Architecture string
|
||||||
|
Os string
|
||||||
|
OsVersion string `json:",omitempty"`
|
||||||
|
Size int64
|
||||||
|
VirtualSize int64
|
||||||
|
GraphDriver GraphDriverData
|
||||||
|
RootFS RootFS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container contains response of Engine API:
|
||||||
|
// GET "/containers/json"
|
||||||
|
type Container struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
Names []string
|
||||||
|
Image string
|
||||||
|
ImageID string
|
||||||
|
Command string
|
||||||
|
Created int64
|
||||||
|
Ports []Port
|
||||||
|
SizeRw int64 `json:",omitempty"`
|
||||||
|
SizeRootFs int64 `json:",omitempty"`
|
||||||
|
Labels map[string]string
|
||||||
|
State string
|
||||||
|
Status string
|
||||||
|
HostConfig struct {
|
||||||
|
NetworkMode string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
NetworkSettings *SummaryNetworkSettings
|
||||||
|
Mounts []MountPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyConfig contains request body of Engine API:
|
||||||
|
// POST "/containers/"+containerID+"/copy"
|
||||||
|
type CopyConfig struct {
|
||||||
|
Resource string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerPathStat is used to encode the header from
|
||||||
|
// GET "/containers/{name:.*}/archive"
|
||||||
|
// "Name" is the file or directory name.
|
||||||
|
type ContainerPathStat struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Mode os.FileMode `json:"mode"`
|
||||||
|
Mtime time.Time `json:"mtime"`
|
||||||
|
LinkTarget string `json:"linkTarget"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStats contains response of Engine API:
|
||||||
|
// GET "/stats"
|
||||||
|
type ContainerStats struct {
|
||||||
|
Body io.ReadCloser `json:"body"`
|
||||||
|
OSType string `json:"ostype"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping contains response of Engine API:
|
||||||
|
// GET "/_ping"
|
||||||
|
type Ping struct {
|
||||||
|
APIVersion string
|
||||||
|
OSType string
|
||||||
|
Experimental bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version contains response of Engine API:
|
||||||
|
// GET "/version"
|
||||||
|
type Version struct {
|
||||||
|
Version string
|
||||||
|
APIVersion string `json:"ApiVersion"`
|
||||||
|
MinAPIVersion string `json:"MinAPIVersion,omitempty"`
|
||||||
|
GitCommit string
|
||||||
|
GoVersion string
|
||||||
|
Os string
|
||||||
|
Arch string
|
||||||
|
KernelVersion string `json:",omitempty"`
|
||||||
|
Experimental bool `json:",omitempty"`
|
||||||
|
BuildTime string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit holds the Git-commit (SHA1) that a binary was built from, as reported
|
||||||
|
// in the version-string of external tools, such as containerd, or runC.
|
||||||
|
type Commit struct {
|
||||||
|
ID string // ID is the actual commit ID of external tool.
|
||||||
|
Expected string // Expected is the commit ID of external tool expected by dockerd as set at build time.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info contains response of Engine API:
|
||||||
|
// GET "/info"
|
||||||
|
type Info struct {
|
||||||
|
ID string
|
||||||
|
Containers int
|
||||||
|
ContainersRunning int
|
||||||
|
ContainersPaused int
|
||||||
|
ContainersStopped int
|
||||||
|
Images int
|
||||||
|
Driver string
|
||||||
|
DriverStatus [][2]string
|
||||||
|
SystemStatus [][2]string
|
||||||
|
Plugins PluginsInfo
|
||||||
|
MemoryLimit bool
|
||||||
|
SwapLimit bool
|
||||||
|
KernelMemory bool
|
||||||
|
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
|
||||||
|
CPUCfsQuota bool `json:"CpuCfsQuota"`
|
||||||
|
CPUShares bool
|
||||||
|
CPUSet bool
|
||||||
|
IPv4Forwarding bool
|
||||||
|
BridgeNfIptables bool
|
||||||
|
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
|
||||||
|
Debug bool
|
||||||
|
NFd int
|
||||||
|
OomKillDisable bool
|
||||||
|
NGoroutines int
|
||||||
|
SystemTime string
|
||||||
|
LoggingDriver string
|
||||||
|
CgroupDriver string
|
||||||
|
NEventsListener int
|
||||||
|
KernelVersion string
|
||||||
|
OperatingSystem string
|
||||||
|
OSType string
|
||||||
|
Architecture string
|
||||||
|
IndexServerAddress string
|
||||||
|
RegistryConfig *registry.ServiceConfig
|
||||||
|
NCPU int
|
||||||
|
MemTotal int64
|
||||||
|
DockerRootDir string
|
||||||
|
HTTPProxy string `json:"HttpProxy"`
|
||||||
|
HTTPSProxy string `json:"HttpsProxy"`
|
||||||
|
NoProxy string
|
||||||
|
Name string
|
||||||
|
Labels []string
|
||||||
|
ExperimentalBuild bool
|
||||||
|
ServerVersion string
|
||||||
|
ClusterStore string
|
||||||
|
ClusterAdvertise string
|
||||||
|
Runtimes map[string]Runtime
|
||||||
|
DefaultRuntime string
|
||||||
|
Swarm swarm.Info
|
||||||
|
// LiveRestoreEnabled determines whether containers should be kept
|
||||||
|
// running when the daemon is shutdown or upon daemon start if
|
||||||
|
// running containers are detected
|
||||||
|
LiveRestoreEnabled bool
|
||||||
|
Isolation container.Isolation
|
||||||
|
InitBinary string
|
||||||
|
ContainerdCommit Commit
|
||||||
|
RuncCommit Commit
|
||||||
|
InitCommit Commit
|
||||||
|
SecurityOptions []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyValue holds a key/value pair
|
||||||
|
type KeyValue struct {
|
||||||
|
Key, Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecurityOpt contains the name and options of a security option
|
||||||
|
type SecurityOpt struct {
|
||||||
|
Name string
|
||||||
|
Options []KeyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeSecurityOptions decodes a security options string slice to a type safe
|
||||||
|
// SecurityOpt
|
||||||
|
func DecodeSecurityOptions(opts []string) ([]SecurityOpt, error) {
|
||||||
|
so := []SecurityOpt{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
// support output from a < 1.13 docker daemon
|
||||||
|
if !strings.Contains(opt, "=") {
|
||||||
|
so = append(so, SecurityOpt{Name: opt})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
secopt := SecurityOpt{}
|
||||||
|
split := strings.Split(opt, ",")
|
||||||
|
for _, s := range split {
|
||||||
|
kv := strings.SplitN(s, "=", 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid security option %q", s)
|
||||||
|
}
|
||||||
|
if kv[0] == "" || kv[1] == "" {
|
||||||
|
return nil, errors.New("invalid empty security option")
|
||||||
|
}
|
||||||
|
if kv[0] == "name" {
|
||||||
|
secopt.Name = kv[1]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
secopt.Options = append(secopt.Options, KeyValue{Key: kv[0], Value: kv[1]})
|
||||||
|
}
|
||||||
|
so = append(so, secopt)
|
||||||
|
}
|
||||||
|
return so, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginsInfo is a temp struct holding Plugins name
|
||||||
|
// registered with docker daemon. It is used by Info struct
|
||||||
|
type PluginsInfo struct {
|
||||||
|
// List of Volume plugins registered
|
||||||
|
Volume []string
|
||||||
|
// List of Network plugins registered
|
||||||
|
Network []string
|
||||||
|
// List of Authorization plugins registered
|
||||||
|
Authorization []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecStartCheck is a temp struct used by execStart
|
||||||
|
// Config fields is part of ExecConfig in runconfig package
|
||||||
|
type ExecStartCheck struct {
|
||||||
|
// ExecStart will first check if it's detached
|
||||||
|
Detach bool
|
||||||
|
// Check if there's a tty
|
||||||
|
Tty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthcheckResult stores information about a single run of a healthcheck probe
|
||||||
|
type HealthcheckResult struct {
|
||||||
|
Start time.Time // Start is the time this check started
|
||||||
|
End time.Time // End is the time this check ended
|
||||||
|
ExitCode int // ExitCode meanings: 0=healthy, 1=unhealthy, 2=reserved (considered unhealthy), else=error running probe
|
||||||
|
Output string // Output from last check
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health states
|
||||||
|
const (
|
||||||
|
NoHealthcheck = "none" // Indicates there is no healthcheck
|
||||||
|
Starting = "starting" // Starting indicates that the container is not yet ready
|
||||||
|
Healthy = "healthy" // Healthy indicates that the container is running correctly
|
||||||
|
Unhealthy = "unhealthy" // Unhealthy indicates that the container has a problem
|
||||||
|
)
|
||||||
|
|
||||||
|
// Health stores information about the container's healthcheck results
|
||||||
|
type Health struct {
|
||||||
|
Status string // Status is one of Starting, Healthy or Unhealthy
|
||||||
|
FailingStreak int // FailingStreak is the number of consecutive failures
|
||||||
|
Log []*HealthcheckResult // Log contains the last few results (oldest first)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerState stores container's running state
|
||||||
|
// it's part of ContainerJSONBase and will return by "inspect" command
|
||||||
|
type ContainerState struct {
|
||||||
|
Status string
|
||||||
|
Running bool
|
||||||
|
Paused bool
|
||||||
|
Restarting bool
|
||||||
|
OOMKilled bool
|
||||||
|
Dead bool
|
||||||
|
Pid int
|
||||||
|
ExitCode int
|
||||||
|
Error string
|
||||||
|
StartedAt string
|
||||||
|
FinishedAt string
|
||||||
|
Health *Health `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerNode stores information about the node that a container
|
||||||
|
// is running on. It's only available in Docker Swarm
|
||||||
|
type ContainerNode struct {
|
||||||
|
ID string
|
||||||
|
IPAddress string `json:"IP"`
|
||||||
|
Addr string
|
||||||
|
Name string
|
||||||
|
Cpus int
|
||||||
|
Memory int64
|
||||||
|
Labels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerJSONBase contains response of Engine API:
|
||||||
|
// GET "/containers/{name:.*}/json"
|
||||||
|
type ContainerJSONBase struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
Created string
|
||||||
|
Path string
|
||||||
|
Args []string
|
||||||
|
State *ContainerState
|
||||||
|
Image string
|
||||||
|
ResolvConfPath string
|
||||||
|
HostnamePath string
|
||||||
|
HostsPath string
|
||||||
|
LogPath string
|
||||||
|
Node *ContainerNode `json:",omitempty"`
|
||||||
|
Name string
|
||||||
|
RestartCount int
|
||||||
|
Driver string
|
||||||
|
MountLabel string
|
||||||
|
ProcessLabel string
|
||||||
|
AppArmorProfile string
|
||||||
|
ExecIDs []string
|
||||||
|
HostConfig *container.HostConfig
|
||||||
|
GraphDriver GraphDriverData
|
||||||
|
SizeRw *int64 `json:",omitempty"`
|
||||||
|
SizeRootFs *int64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerJSON is newly used struct along with MountPoint
|
||||||
|
type ContainerJSON struct {
|
||||||
|
*ContainerJSONBase
|
||||||
|
Mounts []MountPoint
|
||||||
|
Config *container.Config
|
||||||
|
NetworkSettings *NetworkSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkSettings exposes the network settings in the api
|
||||||
|
type NetworkSettings struct {
|
||||||
|
NetworkSettingsBase
|
||||||
|
DefaultNetworkSettings
|
||||||
|
Networks map[string]*network.EndpointSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
// SummaryNetworkSettings provides a summary of container's networks
|
||||||
|
// in /containers/json
|
||||||
|
type SummaryNetworkSettings struct {
|
||||||
|
Networks map[string]*network.EndpointSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkSettingsBase holds basic information about networks
|
||||||
|
type NetworkSettingsBase struct {
|
||||||
|
Bridge string // Bridge is the Bridge name the network uses(e.g. `docker0`)
|
||||||
|
SandboxID string // SandboxID uniquely represents a container's network stack
|
||||||
|
HairpinMode bool // HairpinMode specifies if hairpin NAT should be enabled on the virtual interface
|
||||||
|
LinkLocalIPv6Address string // LinkLocalIPv6Address is an IPv6 unicast address using the link-local prefix
|
||||||
|
LinkLocalIPv6PrefixLen int // LinkLocalIPv6PrefixLen is the prefix length of an IPv6 unicast address
|
||||||
|
Ports nat.PortMap // Ports is a collection of PortBinding indexed by Port
|
||||||
|
SandboxKey string // SandboxKey identifies the sandbox
|
||||||
|
SecondaryIPAddresses []network.Address
|
||||||
|
SecondaryIPv6Addresses []network.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultNetworkSettings holds network information
|
||||||
|
// during the 2 release deprecation period.
|
||||||
|
// It will be removed in Docker 1.11.
|
||||||
|
type DefaultNetworkSettings struct {
|
||||||
|
EndpointID string // EndpointID uniquely represents a service endpoint in a Sandbox
|
||||||
|
Gateway string // Gateway holds the gateway address for the network
|
||||||
|
GlobalIPv6Address string // GlobalIPv6Address holds network's global IPv6 address
|
||||||
|
GlobalIPv6PrefixLen int // GlobalIPv6PrefixLen represents mask length of network's global IPv6 address
|
||||||
|
IPAddress string // IPAddress holds the IPv4 address for the network
|
||||||
|
IPPrefixLen int // IPPrefixLen represents mask length of network's IPv4 address
|
||||||
|
IPv6Gateway string // IPv6Gateway holds gateway address specific for IPv6
|
||||||
|
MacAddress string // MacAddress holds the MAC address for the network
|
||||||
|
}
|
||||||
|
|
||||||
|
// MountPoint represents a mount point configuration inside the container.
|
||||||
|
// This is used for reporting the mountpoints in use by a container.
|
||||||
|
type MountPoint struct {
|
||||||
|
Type mount.Type `json:",omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Source string
|
||||||
|
Destination string
|
||||||
|
Driver string `json:",omitempty"`
|
||||||
|
Mode string
|
||||||
|
RW bool
|
||||||
|
Propagation mount.Propagation
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkResource is the body of the "get network" http response message
|
||||||
|
type NetworkResource struct {
|
||||||
|
Name string // Name is the requested name of the network
|
||||||
|
ID string `json:"Id"` // ID uniquely identifies a network on a single machine
|
||||||
|
Created time.Time // Created is the time the network created
|
||||||
|
Scope string // Scope describes the level at which the network exists (e.g. `global` for cluster-wide or `local` for machine level)
|
||||||
|
Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`)
|
||||||
|
EnableIPv6 bool // EnableIPv6 represents whether to enable IPv6
|
||||||
|
IPAM network.IPAM // IPAM is the network's IP Address Management
|
||||||
|
Internal bool // Internal represents if the network is used internal only
|
||||||
|
Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
|
||||||
|
Containers map[string]EndpointResource // Containers contains endpoints belonging to the network
|
||||||
|
Options map[string]string // Options holds the network specific options to use for when creating the network
|
||||||
|
Labels map[string]string // Labels holds metadata specific to the network being created
|
||||||
|
Peers []network.PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network
|
||||||
|
Services map[string]network.ServiceInfo `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointResource contains network resources allocated and used for a container in a network
|
||||||
|
type EndpointResource struct {
|
||||||
|
Name string
|
||||||
|
EndpointID string
|
||||||
|
MacAddress string
|
||||||
|
IPv4Address string
|
||||||
|
IPv6Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkCreate is the expected body of the "create network" http request message
|
||||||
|
type NetworkCreate struct {
|
||||||
|
// Check for networks with duplicate names.
|
||||||
|
// Network is primarily keyed based on a random ID and not on the name.
|
||||||
|
// Network name is strictly a user-friendly alias to the network
|
||||||
|
// which is uniquely identified using ID.
|
||||||
|
// And there is no guaranteed way to check for duplicates.
|
||||||
|
// Option CheckDuplicate is there to provide a best effort checking of any networks
|
||||||
|
// which has the same name but it is not guaranteed to catch all name collisions.
|
||||||
|
CheckDuplicate bool
|
||||||
|
Driver string
|
||||||
|
EnableIPv6 bool
|
||||||
|
IPAM *network.IPAM
|
||||||
|
Internal bool
|
||||||
|
Attachable bool
|
||||||
|
Options map[string]string
|
||||||
|
Labels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkCreateRequest is the request message sent to the server for network create call.
|
||||||
|
type NetworkCreateRequest struct {
|
||||||
|
NetworkCreate
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkCreateResponse is the response message sent by the server for network create call
|
||||||
|
type NetworkCreateResponse struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
Warning string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkConnect represents the data to be used to connect a container to the network
|
||||||
|
type NetworkConnect struct {
|
||||||
|
Container string
|
||||||
|
EndpointConfig *network.EndpointSettings `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkDisconnect represents the data to be used to disconnect a container from the network
|
||||||
|
type NetworkDisconnect struct {
|
||||||
|
Container string
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkpoint represents the details of a checkpoint
|
||||||
|
type Checkpoint struct {
|
||||||
|
Name string // Name is the name of the checkpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runtime describes an OCI runtime
|
||||||
|
type Runtime struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Args []string `json:"runtimeArgs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskUsage contains response of Engine API:
|
||||||
|
// GET "/system/df"
|
||||||
|
type DiskUsage struct {
|
||||||
|
LayersSize int64
|
||||||
|
Images []*ImageSummary
|
||||||
|
Containers []*Container
|
||||||
|
Volumes []*Volume
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainersPruneReport contains the response for Engine API:
|
||||||
|
// POST "/containers/prune"
|
||||||
|
type ContainersPruneReport struct {
|
||||||
|
ContainersDeleted []string
|
||||||
|
SpaceReclaimed uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumesPruneReport contains the response for Engine API:
|
||||||
|
// POST "/volumes/prune"
|
||||||
|
type VolumesPruneReport struct {
|
||||||
|
VolumesDeleted []string
|
||||||
|
SpaceReclaimed uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagesPruneReport contains the response for Engine API:
|
||||||
|
// POST "/images/prune"
|
||||||
|
type ImagesPruneReport struct {
|
||||||
|
ImagesDeleted []ImageDeleteResponseItem
|
||||||
|
SpaceReclaimed uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworksPruneReport contains the response for Engine API:
|
||||||
|
// POST "/networks/prune"
|
||||||
|
type NetworksPruneReport struct {
|
||||||
|
NetworksDeleted []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretCreateResponse contains the information returned to a client
|
||||||
|
// on the creation of a new secret.
|
||||||
|
type SecretCreateResponse struct {
|
||||||
|
// ID is the id of the created secret.
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretListOptions holds parameters to list secrets
|
||||||
|
type SecretListOptions struct {
|
||||||
|
Filters filters.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushResult contains the tag, manifest digest, and manifest size from the
|
||||||
|
// push. It's used to signal this information to the trust code in the client
|
||||||
|
// so it can sign the manifest if necessary.
|
||||||
|
type PushResult struct {
|
||||||
|
Tag string
|
||||||
|
Digest string
|
||||||
|
Size int
|
||||||
|
}
|
62
vendor/github.com/docker/docker/api/types/versions/compare.go
generated
vendored
Normal file
62
vendor/github.com/docker/docker/api/types/versions/compare.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package versions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// compare compares two version strings
|
||||||
|
// returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise.
|
||||||
|
func compare(v1, v2 string) int {
|
||||||
|
var (
|
||||||
|
currTab = strings.Split(v1, ".")
|
||||||
|
otherTab = strings.Split(v2, ".")
|
||||||
|
)
|
||||||
|
|
||||||
|
max := len(currTab)
|
||||||
|
if len(otherTab) > max {
|
||||||
|
max = len(otherTab)
|
||||||
|
}
|
||||||
|
for i := 0; i < max; i++ {
|
||||||
|
var currInt, otherInt int
|
||||||
|
|
||||||
|
if len(currTab) > i {
|
||||||
|
currInt, _ = strconv.Atoi(currTab[i])
|
||||||
|
}
|
||||||
|
if len(otherTab) > i {
|
||||||
|
otherInt, _ = strconv.Atoi(otherTab[i])
|
||||||
|
}
|
||||||
|
if currInt > otherInt {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if otherInt > currInt {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan checks if a version is less than another
|
||||||
|
func LessThan(v, other string) bool {
|
||||||
|
return compare(v, other) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThanOrEqualTo checks if a version is less than or equal to another
|
||||||
|
func LessThanOrEqualTo(v, other string) bool {
|
||||||
|
return compare(v, other) <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThan checks if a version is greater than another
|
||||||
|
func GreaterThan(v, other string) bool {
|
||||||
|
return compare(v, other) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThanOrEqualTo checks if a version is greater than or equal to another
|
||||||
|
func GreaterThanOrEqualTo(v, other string) bool {
|
||||||
|
return compare(v, other) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if a version is equal to another
|
||||||
|
func Equal(v, other string) bool {
|
||||||
|
return compare(v, other) == 0
|
||||||
|
}
|
58
vendor/github.com/docker/docker/api/types/volume.go
generated
vendored
Normal file
58
vendor/github.com/docker/docker/api/types/volume.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
// Volume volume
|
||||||
|
// swagger:model Volume
|
||||||
|
type Volume struct {
|
||||||
|
|
||||||
|
// Name of the volume driver used by the volume.
|
||||||
|
// Required: true
|
||||||
|
Driver string `json:"Driver"`
|
||||||
|
|
||||||
|
// User-defined key/value metadata.
|
||||||
|
// Required: true
|
||||||
|
Labels map[string]string `json:"Labels"`
|
||||||
|
|
||||||
|
// Mount path of the volume on the host.
|
||||||
|
// Required: true
|
||||||
|
Mountpoint string `json:"Mountpoint"`
|
||||||
|
|
||||||
|
// Name of the volume.
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
|
||||||
|
// The driver specific options used when creating the volume.
|
||||||
|
// Required: true
|
||||||
|
Options map[string]string `json:"Options"`
|
||||||
|
|
||||||
|
// The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level.
|
||||||
|
// Required: true
|
||||||
|
Scope string `json:"Scope"`
|
||||||
|
|
||||||
|
// Low-level details about the volume, provided by the volume driver.
|
||||||
|
// Details are returned as a map with key/value pairs:
|
||||||
|
// `{"key":"value","key2":"value2"}`.
|
||||||
|
//
|
||||||
|
// The `Status` field is optional, and is omitted if the volume driver
|
||||||
|
// does not support this feature.
|
||||||
|
//
|
||||||
|
Status map[string]interface{} `json:"Status,omitempty"`
|
||||||
|
|
||||||
|
// usage data
|
||||||
|
UsageData *VolumeUsageData `json:"UsageData,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeUsageData volume usage data
|
||||||
|
// swagger:model VolumeUsageData
|
||||||
|
type VolumeUsageData struct {
|
||||||
|
|
||||||
|
// The number of containers referencing this volume.
|
||||||
|
// Required: true
|
||||||
|
RefCount int64 `json:"RefCount"`
|
||||||
|
|
||||||
|
// The disk space used by the volume (local driver only)
|
||||||
|
// Required: true
|
||||||
|
Size int64 `json:"Size"`
|
||||||
|
}
|
29
vendor/github.com/docker/docker/api/types/volume/volumes_create.go
generated
vendored
Normal file
29
vendor/github.com/docker/docker/api/types/volume/volumes_create.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package volume
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// VolumesCreateBody volumes create body
|
||||||
|
// swagger:model VolumesCreateBody
|
||||||
|
type VolumesCreateBody struct {
|
||||||
|
|
||||||
|
// Name of the volume driver to use.
|
||||||
|
// Required: true
|
||||||
|
Driver string `json:"Driver"`
|
||||||
|
|
||||||
|
// A mapping of driver options and values. These options are passed directly to the driver and are driver specific.
|
||||||
|
// Required: true
|
||||||
|
DriverOpts map[string]string `json:"DriverOpts"`
|
||||||
|
|
||||||
|
// User-defined key/value metadata.
|
||||||
|
// Required: true
|
||||||
|
Labels map[string]string `json:"Labels"`
|
||||||
|
|
||||||
|
// The new volume's name. If not specified, Docker generates a name.
|
||||||
|
// Required: true
|
||||||
|
Name string `json:"Name"`
|
||||||
|
}
|
23
vendor/github.com/docker/docker/api/types/volume/volumes_list.go
generated
vendored
Normal file
23
vendor/github.com/docker/docker/api/types/volume/volumes_list.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package volume
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DO NOT EDIT THIS FILE
|
||||||
|
// This file was generated by `swagger generate operation`
|
||||||
|
//
|
||||||
|
// See hack/generate-swagger-api.sh
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import "github.com/docker/docker/api/types"
|
||||||
|
|
||||||
|
// VolumesListOKBody volumes list o k body
|
||||||
|
// swagger:model VolumesListOKBody
|
||||||
|
type VolumesListOKBody struct {
|
||||||
|
|
||||||
|
// List of volumes
|
||||||
|
// Required: true
|
||||||
|
Volumes []*types.Volume `json:"Volumes"`
|
||||||
|
|
||||||
|
// Warnings that occurred when fetching the list of volumes
|
||||||
|
// Required: true
|
||||||
|
Warnings []string `json:"Warnings"`
|
||||||
|
}
|
49
vendor/github.com/docker/docker/builder/dockerignore/dockerignore.go
generated
vendored
Normal file
49
vendor/github.com/docker/docker/builder/dockerignore/dockerignore.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package dockerignore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadAll reads a .dockerignore file and returns the list of file patterns
|
||||||
|
// to ignore. Note this will trim whitespace from each line as well
|
||||||
|
// as use GO's "clean" func to get the shortest/cleanest path for each.
|
||||||
|
func ReadAll(reader io.Reader) ([]string, error) {
|
||||||
|
if reader == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
var excludes []string
|
||||||
|
currentLine := 0
|
||||||
|
|
||||||
|
utf8bom := []byte{0xEF, 0xBB, 0xBF}
|
||||||
|
for scanner.Scan() {
|
||||||
|
scannedBytes := scanner.Bytes()
|
||||||
|
// We trim UTF8 BOM
|
||||||
|
if currentLine == 0 {
|
||||||
|
scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom)
|
||||||
|
}
|
||||||
|
pattern := string(scannedBytes)
|
||||||
|
currentLine++
|
||||||
|
// Lines starting with # (comments) are ignored before processing
|
||||||
|
if strings.HasPrefix(pattern, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pattern = strings.TrimSpace(pattern)
|
||||||
|
if pattern == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pattern = filepath.Clean(pattern)
|
||||||
|
pattern = filepath.ToSlash(pattern)
|
||||||
|
excludes = append(excludes, pattern)
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("Error reading .dockerignore: %v", err)
|
||||||
|
}
|
||||||
|
return excludes, nil
|
||||||
|
}
|
265
vendor/github.com/docker/docker/cli/command/image/build/context.go
generated
vendored
Normal file
265
vendor/github.com/docker/docker/cli/command/image/build/context.go
generated
vendored
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/fileutils"
|
||||||
|
"github.com/docker/docker/pkg/gitutils"
|
||||||
|
"github.com/docker/docker/pkg/httputils"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/docker/pkg/progress"
|
||||||
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
|
||||||
|
DefaultDockerfileName string = "Dockerfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateContextDirectory checks if all the contents of the directory
|
||||||
|
// can be read and returns an error if some files can't be read
|
||||||
|
// symlinks which point to non-existing files don't trigger an error
|
||||||
|
func ValidateContextDirectory(srcPath string, excludes []string) error {
|
||||||
|
contextRoot, err := getContextRoot(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return filepath.Walk(contextRoot, func(filePath string, f os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
return fmt.Errorf("can't stat '%s'", filePath)
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip this directory/file if it's not in the path, it won't get added to the context
|
||||||
|
if relFilePath, err := filepath.Rel(contextRoot, filePath); err != nil {
|
||||||
|
return err
|
||||||
|
} else if skip, err := fileutils.Matches(relFilePath, excludes); err != nil {
|
||||||
|
return err
|
||||||
|
} else if skip {
|
||||||
|
if f.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip checking if symlinks point to non-existing files, such symlinks can be useful
|
||||||
|
// also skip named pipes, because they hanging on open
|
||||||
|
if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.IsDir() {
|
||||||
|
currentFile, err := os.Open(filePath)
|
||||||
|
if err != nil && os.IsPermission(err) {
|
||||||
|
return fmt.Errorf("no permission to read from '%s'", filePath)
|
||||||
|
}
|
||||||
|
currentFile.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextFromReader will read the contents of the given reader as either a
|
||||||
|
// Dockerfile or tar archive. Returns a tar archive used as a context and a
|
||||||
|
// path to the Dockerfile inside the tar.
|
||||||
|
func GetContextFromReader(r io.ReadCloser, dockerfileName string) (out io.ReadCloser, relDockerfile string, err error) {
|
||||||
|
buf := bufio.NewReader(r)
|
||||||
|
|
||||||
|
magic, err := buf.Peek(archive.HeaderSize)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, "", fmt.Errorf("failed to peek context header from STDIN: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if archive.IsArchive(magic) {
|
||||||
|
return ioutils.NewReadCloserWrapper(buf, func() error { return r.Close() }), dockerfileName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input should be read as a Dockerfile.
|
||||||
|
tmpDir, err := ioutil.TempDir("", "docker-build-context-")
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("unable to create temporary context directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(filepath.Join(tmpDir, DefaultDockerfileName))
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(f, buf)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
if err := r.Close(); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tar, err := archive.Tar(tmpDir, archive.Uncompressed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutils.NewReadCloserWrapper(tar, func() error {
|
||||||
|
err := tar.Close()
|
||||||
|
os.RemoveAll(tmpDir)
|
||||||
|
return err
|
||||||
|
}), DefaultDockerfileName, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextFromGitURL uses a Git URL as context for a `docker build`. The
|
||||||
|
// git repo is cloned into a temporary directory used as the context directory.
|
||||||
|
// Returns the absolute path to the temporary context directory, the relative
|
||||||
|
// path of the dockerfile in that context directory, and a non-nil error on
|
||||||
|
// success.
|
||||||
|
func GetContextFromGitURL(gitURL, dockerfileName string) (absContextDir, relDockerfile string, err error) {
|
||||||
|
if _, err := exec.LookPath("git"); err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to find 'git': %v", err)
|
||||||
|
}
|
||||||
|
if absContextDir, err = gitutils.Clone(gitURL); err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to 'git clone' to temporary context directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDockerfileRelPath(absContextDir, dockerfileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextFromURL uses a remote URL as context for a `docker build`. The
|
||||||
|
// remote resource is downloaded as either a Dockerfile or a tar archive.
|
||||||
|
// Returns the tar archive used for the context and a path of the
|
||||||
|
// dockerfile inside the tar.
|
||||||
|
func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.ReadCloser, string, error) {
|
||||||
|
response, err := httputils.Download(remoteURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("unable to download remote context %s: %v", remoteURL, err)
|
||||||
|
}
|
||||||
|
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(out, true)
|
||||||
|
|
||||||
|
// Pass the response body through a progress reader.
|
||||||
|
progReader := progress.NewProgressReader(response.Body, progressOutput, response.ContentLength, "", fmt.Sprintf("Downloading build context from remote url: %s", remoteURL))
|
||||||
|
|
||||||
|
return GetContextFromReader(ioutils.NewReadCloserWrapper(progReader, func() error { return response.Body.Close() }), dockerfileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextFromLocalDir uses the given local directory as context for a
|
||||||
|
// `docker build`. Returns the absolute path to the local context directory,
|
||||||
|
// the relative path of the dockerfile in that context directory, and a non-nil
|
||||||
|
// error on success.
|
||||||
|
func GetContextFromLocalDir(localDir, dockerfileName string) (absContextDir, relDockerfile string, err error) {
|
||||||
|
// When using a local context directory, when the Dockerfile is specified
|
||||||
|
// with the `-f/--file` option then it is considered relative to the
|
||||||
|
// current directory and not the context directory.
|
||||||
|
if dockerfileName != "" {
|
||||||
|
if dockerfileName, err = filepath.Abs(dockerfileName); err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to get absolute path to Dockerfile: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDockerfileRelPath(localDir, dockerfileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDockerfileRelPath uses the given context directory for a `docker build`
|
||||||
|
// and returns the absolute path to the context directory, the relative path of
|
||||||
|
// the dockerfile in that context directory, and a non-nil error on success.
|
||||||
|
func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDir, relDockerfile string, err error) {
|
||||||
|
if absContextDir, err = filepath.Abs(givenContextDir); err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to get absolute context directory of given context directory %q: %v", givenContextDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The context dir might be a symbolic link, so follow it to the actual
|
||||||
|
// target directory.
|
||||||
|
//
|
||||||
|
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||||
|
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||||
|
// paths (those starting with \\). This hack means that when using links
|
||||||
|
// on UNC paths, they will not be followed.
|
||||||
|
if !isUNC(absContextDir) {
|
||||||
|
absContextDir, err = filepath.EvalSymlinks(absContextDir)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to evaluate symlinks in context path: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, err := os.Lstat(absContextDir)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to stat context directory %q: %v", absContextDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stat.IsDir() {
|
||||||
|
return "", "", fmt.Errorf("context must be a directory: %s", absContextDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
absDockerfile := givenDockerfile
|
||||||
|
if absDockerfile == "" {
|
||||||
|
// No -f/--file was specified so use the default relative to the
|
||||||
|
// context directory.
|
||||||
|
absDockerfile = filepath.Join(absContextDir, DefaultDockerfileName)
|
||||||
|
|
||||||
|
// Just to be nice ;-) look for 'dockerfile' too but only
|
||||||
|
// use it if we found it, otherwise ignore this check
|
||||||
|
if _, err = os.Lstat(absDockerfile); os.IsNotExist(err) {
|
||||||
|
altPath := filepath.Join(absContextDir, strings.ToLower(DefaultDockerfileName))
|
||||||
|
if _, err = os.Lstat(altPath); err == nil {
|
||||||
|
absDockerfile = altPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not already an absolute path, the Dockerfile path should be joined to
|
||||||
|
// the base directory.
|
||||||
|
if !filepath.IsAbs(absDockerfile) {
|
||||||
|
absDockerfile = filepath.Join(absContextDir, absDockerfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate symlinks in the path to the Dockerfile too.
|
||||||
|
//
|
||||||
|
// FIXME. We use isUNC (always false on non-Windows platforms) to workaround
|
||||||
|
// an issue in golang. On Windows, EvalSymLinks does not work on UNC file
|
||||||
|
// paths (those starting with \\). This hack means that when using links
|
||||||
|
// on UNC paths, they will not be followed.
|
||||||
|
if !isUNC(absDockerfile) {
|
||||||
|
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Lstat(absDockerfile); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", "", fmt.Errorf("Cannot locate Dockerfile: %q", absDockerfile)
|
||||||
|
}
|
||||||
|
return "", "", fmt.Errorf("unable to stat Dockerfile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if relDockerfile, err = filepath.Rel(absContextDir, absDockerfile); err != nil {
|
||||||
|
return "", "", fmt.Errorf("unable to get relative Dockerfile path: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||||
|
return "", "", fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", givenDockerfile, givenContextDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return absContextDir, relDockerfile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isUNC returns true if the path is UNC (one starting \\). It always returns
|
||||||
|
// false on Linux.
|
||||||
|
func isUNC(path string) bool {
|
||||||
|
return runtime.GOOS == "windows" && strings.HasPrefix(path, `\\`)
|
||||||
|
}
|
11
vendor/github.com/docker/docker/cli/command/image/build/context_unix.go
generated
vendored
Normal file
11
vendor/github.com/docker/docker/cli/command/image/build/context_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getContextRoot(srcPath string) (string, error) {
|
||||||
|
return filepath.Join(srcPath, "."), nil
|
||||||
|
}
|
17
vendor/github.com/docker/docker/cli/command/image/build/context_windows.go
generated
vendored
Normal file
17
vendor/github.com/docker/docker/cli/command/image/build/context_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/longpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getContextRoot(srcPath string) (string, error) {
|
||||||
|
cr, err := filepath.Abs(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return longpath.AddPrefix(cr), nil
|
||||||
|
}
|
120
vendor/github.com/docker/docker/cli/config/config.go
generated
vendored
Normal file
120
vendor/github.com/docker/docker/cli/config/config.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/cli/config/configfile"
|
||||||
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ConfigFileName is the name of config file
|
||||||
|
ConfigFileName = "config.json"
|
||||||
|
configFileDir = ".docker"
|
||||||
|
oldConfigfile = ".dockercfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configDir = os.Getenv("DOCKER_CONFIG")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if configDir == "" {
|
||||||
|
configDir = filepath.Join(homedir.Get(), configFileDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dir returns the directory the configuration file is stored in
|
||||||
|
func Dir() string {
|
||||||
|
return configDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDir sets the directory the configuration file is stored in
|
||||||
|
func SetDir(dir string) {
|
||||||
|
configDir = dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfigFile initializes an empty configuration file for the given filename 'fn'
|
||||||
|
func NewConfigFile(fn string) *configfile.ConfigFile {
|
||||||
|
return &configfile.ConfigFile{
|
||||||
|
AuthConfigs: make(map[string]types.AuthConfig),
|
||||||
|
HTTPHeaders: make(map[string]string),
|
||||||
|
Filename: fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
|
||||||
|
// a non-nested reader
|
||||||
|
func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
|
||||||
|
configFile := configfile.ConfigFile{
|
||||||
|
AuthConfigs: make(map[string]types.AuthConfig),
|
||||||
|
}
|
||||||
|
err := configFile.LegacyLoadFromReader(configData)
|
||||||
|
return &configFile, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFromReader is a convenience function that creates a ConfigFile object from
|
||||||
|
// a reader
|
||||||
|
func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
|
||||||
|
configFile := configfile.ConfigFile{
|
||||||
|
AuthConfigs: make(map[string]types.AuthConfig),
|
||||||
|
}
|
||||||
|
err := configFile.LoadFromReader(configData)
|
||||||
|
return &configFile, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load reads the configuration files in the given directory, and sets up
|
||||||
|
// the auth config information and returns values.
|
||||||
|
// FIXME: use the internal golang config parser
|
||||||
|
func Load(configDir string) (*configfile.ConfigFile, error) {
|
||||||
|
if configDir == "" {
|
||||||
|
configDir = Dir()
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := configfile.ConfigFile{
|
||||||
|
AuthConfigs: make(map[string]types.AuthConfig),
|
||||||
|
Filename: filepath.Join(configDir, ConfigFileName),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try happy path first - latest config file
|
||||||
|
if _, err := os.Stat(configFile.Filename); err == nil {
|
||||||
|
file, err := os.Open(configFile.Filename)
|
||||||
|
if err != nil {
|
||||||
|
return &configFile, fmt.Errorf("%s - %v", configFile.Filename, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
err = configFile.LoadFromReader(file)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("%s - %v", configFile.Filename, err)
|
||||||
|
}
|
||||||
|
return &configFile, err
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
// if file is there but we can't stat it for any reason other
|
||||||
|
// than it doesn't exist then stop
|
||||||
|
return &configFile, fmt.Errorf("%s - %v", configFile.Filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't find latest config file so check for the old one
|
||||||
|
confFile := filepath.Join(homedir.Get(), oldConfigfile)
|
||||||
|
if _, err := os.Stat(confFile); err != nil {
|
||||||
|
return &configFile, nil //missing file is not an error
|
||||||
|
}
|
||||||
|
file, err := os.Open(confFile)
|
||||||
|
if err != nil {
|
||||||
|
return &configFile, fmt.Errorf("%s - %v", confFile, err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
err = configFile.LegacyLoadFromReader(file)
|
||||||
|
if err != nil {
|
||||||
|
return &configFile, fmt.Errorf("%s - %v", confFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if configFile.HTTPHeaders == nil {
|
||||||
|
configFile.HTTPHeaders = map[string]string{}
|
||||||
|
}
|
||||||
|
return &configFile, nil
|
||||||
|
}
|
186
vendor/github.com/docker/docker/cli/config/configfile/file.go
generated
vendored
Normal file
186
vendor/github.com/docker/docker/cli/config/configfile/file.go
generated
vendored
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
package configfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// This constant is only used for really old config files when the
|
||||||
|
// URL wasn't saved as part of the config file and it was just
|
||||||
|
// assumed to be this value.
|
||||||
|
defaultIndexserver = "https://index.docker.io/v1/"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigFile ~/.docker/config.json file info
|
||||||
|
type ConfigFile struct {
|
||||||
|
AuthConfigs map[string]types.AuthConfig `json:"auths"`
|
||||||
|
HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"`
|
||||||
|
PsFormat string `json:"psFormat,omitempty"`
|
||||||
|
ImagesFormat string `json:"imagesFormat,omitempty"`
|
||||||
|
NetworksFormat string `json:"networksFormat,omitempty"`
|
||||||
|
PluginsFormat string `json:"pluginsFormat,omitempty"`
|
||||||
|
VolumesFormat string `json:"volumesFormat,omitempty"`
|
||||||
|
StatsFormat string `json:"statsFormat,omitempty"`
|
||||||
|
DetachKeys string `json:"detachKeys,omitempty"`
|
||||||
|
CredentialsStore string `json:"credsStore,omitempty"`
|
||||||
|
CredentialHelpers map[string]string `json:"credHelpers,omitempty"`
|
||||||
|
Filename string `json:"-"` // Note: for internal use only
|
||||||
|
ServiceInspectFormat string `json:"serviceInspectFormat,omitempty"`
|
||||||
|
ServicesFormat string `json:"servicesFormat,omitempty"`
|
||||||
|
TasksFormat string `json:"tasksFormat,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
|
||||||
|
// auth config information with given directory and populates the receiver object
|
||||||
|
func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
|
||||||
|
b, err := ioutil.ReadAll(configData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
|
||||||
|
arr := strings.Split(string(b), "\n")
|
||||||
|
if len(arr) < 2 {
|
||||||
|
return fmt.Errorf("The Auth config file is empty")
|
||||||
|
}
|
||||||
|
authConfig := types.AuthConfig{}
|
||||||
|
origAuth := strings.Split(arr[0], " = ")
|
||||||
|
if len(origAuth) != 2 {
|
||||||
|
return fmt.Errorf("Invalid Auth config file")
|
||||||
|
}
|
||||||
|
authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
authConfig.ServerAddress = defaultIndexserver
|
||||||
|
configFile.AuthConfigs[defaultIndexserver] = authConfig
|
||||||
|
} else {
|
||||||
|
for k, authConfig := range configFile.AuthConfigs {
|
||||||
|
authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
authConfig.Auth = ""
|
||||||
|
authConfig.ServerAddress = k
|
||||||
|
configFile.AuthConfigs[k] = authConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFromReader reads the configuration data given and sets up the auth config
|
||||||
|
// information with given directory and populates the receiver object
|
||||||
|
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||||
|
if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
for addr, ac := range configFile.AuthConfigs {
|
||||||
|
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ac.Auth = ""
|
||||||
|
ac.ServerAddress = addr
|
||||||
|
configFile.AuthConfigs[addr] = ac
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAuth returns whether there is authentication configured
|
||||||
|
// in this file or not.
|
||||||
|
func (configFile *ConfigFile) ContainsAuth() bool {
|
||||||
|
return configFile.CredentialsStore != "" ||
|
||||||
|
len(configFile.CredentialHelpers) > 0 ||
|
||||||
|
len(configFile.AuthConfigs) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveToWriter encodes and writes out all the authorization information to
|
||||||
|
// the given writer
|
||||||
|
func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||||
|
// Encode sensitive data into a new/temp struct
|
||||||
|
tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
|
||||||
|
for k, authConfig := range configFile.AuthConfigs {
|
||||||
|
authCopy := authConfig
|
||||||
|
// encode and save the authstring, while blanking out the original fields
|
||||||
|
authCopy.Auth = encodeAuth(&authCopy)
|
||||||
|
authCopy.Username = ""
|
||||||
|
authCopy.Password = ""
|
||||||
|
authCopy.ServerAddress = ""
|
||||||
|
tmpAuthConfigs[k] = authCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAuthConfigs := configFile.AuthConfigs
|
||||||
|
configFile.AuthConfigs = tmpAuthConfigs
|
||||||
|
defer func() { configFile.AuthConfigs = saveAuthConfigs }()
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(configFile, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write(data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save encodes and writes out all the authorization information
|
||||||
|
func (configFile *ConfigFile) Save() error {
|
||||||
|
if configFile.Filename == "" {
|
||||||
|
return fmt.Errorf("Can't save config with empty filename")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(configFile.Filename), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(configFile.Filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return configFile.SaveToWriter(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeAuth creates a base64 encoded string to containing authorization information
|
||||||
|
func encodeAuth(authConfig *types.AuthConfig) string {
|
||||||
|
if authConfig.Username == "" && authConfig.Password == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
authStr := authConfig.Username + ":" + authConfig.Password
|
||||||
|
msg := []byte(authStr)
|
||||||
|
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
|
||||||
|
base64.StdEncoding.Encode(encoded, msg)
|
||||||
|
return string(encoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeAuth decodes a base64 encoded string and returns username and password
|
||||||
|
func decodeAuth(authStr string) (string, string, error) {
|
||||||
|
if authStr == "" {
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
decLen := base64.StdEncoding.DecodedLen(len(authStr))
|
||||||
|
decoded := make([]byte, decLen)
|
||||||
|
authByte := []byte(authStr)
|
||||||
|
n, err := base64.StdEncoding.Decode(decoded, authByte)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if n > decLen {
|
||||||
|
return "", "", fmt.Errorf("Something went wrong decoding auth config")
|
||||||
|
}
|
||||||
|
arr := strings.SplitN(string(decoded), ":", 2)
|
||||||
|
if len(arr) != 2 {
|
||||||
|
return "", "", fmt.Errorf("Invalid auth configuration file")
|
||||||
|
}
|
||||||
|
password := strings.Trim(arr[1], "\x00")
|
||||||
|
return arr[0], password, nil
|
||||||
|
}
|
13
vendor/github.com/docker/docker/client/checkpoint_create.go
generated
vendored
Normal file
13
vendor/github.com/docker/docker/client/checkpoint_create.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointCreate creates a checkpoint from the given container with the given name
|
||||||
|
func (cli *Client) CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error {
|
||||||
|
resp, err := cli.post(ctx, "/containers/"+container+"/checkpoints", nil, options, nil)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return err
|
||||||
|
}
|
20
vendor/github.com/docker/docker/client/checkpoint_delete.go
generated
vendored
Normal file
20
vendor/github.com/docker/docker/client/checkpoint_delete.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointDelete deletes the checkpoint with the given name from the given container
|
||||||
|
func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options types.CheckpointDeleteOptions) error {
|
||||||
|
query := url.Values{}
|
||||||
|
if options.CheckpointDir != "" {
|
||||||
|
query.Set("dir", options.CheckpointDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+options.CheckpointID, query, nil)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return err
|
||||||
|
}
|
32
vendor/github.com/docker/docker/client/checkpoint_list.go
generated
vendored
Normal file
32
vendor/github.com/docker/docker/client/checkpoint_list.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointList returns the checkpoints of the given container in the docker host
|
||||||
|
func (cli *Client) CheckpointList(ctx context.Context, container string, options types.CheckpointListOptions) ([]types.Checkpoint, error) {
|
||||||
|
var checkpoints []types.Checkpoint
|
||||||
|
|
||||||
|
query := url.Values{}
|
||||||
|
if options.CheckpointDir != "" {
|
||||||
|
query.Set("dir", options.CheckpointDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
|
||||||
|
if err != nil {
|
||||||
|
if resp.statusCode == http.StatusNotFound {
|
||||||
|
return checkpoints, containerNotFoundError{container}
|
||||||
|
}
|
||||||
|
return checkpoints, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.body).Decode(&checkpoints)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return checkpoints, err
|
||||||
|
}
|
261
vendor/github.com/docker/docker/client/client.go
generated
vendored
Normal file
261
vendor/github.com/docker/docker/client/client.go
generated
vendored
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
/*
|
||||||
|
Package client is a Go client for the Docker Engine API.
|
||||||
|
|
||||||
|
The "docker" command uses this package to communicate with the daemon. It can also
|
||||||
|
be used by your own Go applications to do anything the command-line interface does
|
||||||
|
- running containers, pulling images, managing swarms, etc.
|
||||||
|
|
||||||
|
For more information about the Engine API, see the documentation:
|
||||||
|
https://docs.docker.com/engine/reference/api/
|
||||||
|
|
||||||
|
Usage
|
||||||
|
|
||||||
|
You use the library by creating a client object and calling methods on it. The
|
||||||
|
client can be created either from environment variables with NewEnvClient, or
|
||||||
|
configured manually with NewClient.
|
||||||
|
|
||||||
|
For example, to list running containers (the equivalent of "docker ps"):
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cli, err := client.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, container := range containers {
|
||||||
|
fmt.Printf("%s %s\n", container.ID[:10], container.Image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api"
|
||||||
|
"github.com/docker/go-connections/sockets"
|
||||||
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is the API client that performs all operations
|
||||||
|
// against a docker server.
|
||||||
|
type Client struct {
|
||||||
|
// scheme sets the scheme for the client
|
||||||
|
scheme string
|
||||||
|
// host holds the server address to connect to
|
||||||
|
host string
|
||||||
|
// proto holds the client protocol i.e. unix.
|
||||||
|
proto string
|
||||||
|
// addr holds the client address.
|
||||||
|
addr string
|
||||||
|
// basePath holds the path to prepend to the requests.
|
||||||
|
basePath string
|
||||||
|
// client used to send and receive http requests.
|
||||||
|
client *http.Client
|
||||||
|
// version of the server to talk to.
|
||||||
|
version string
|
||||||
|
// custom http headers configured by users.
|
||||||
|
customHTTPHeaders map[string]string
|
||||||
|
// manualOverride is set to true when the version was set by users.
|
||||||
|
manualOverride bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnvClient initializes a new API client based on environment variables.
|
||||||
|
// Use DOCKER_HOST to set the url to the docker server.
|
||||||
|
// Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
|
||||||
|
// Use DOCKER_CERT_PATH to load the TLS certificates from.
|
||||||
|
// Use DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
|
||||||
|
func NewEnvClient() (*Client, error) {
|
||||||
|
var client *http.Client
|
||||||
|
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
|
||||||
|
options := tlsconfig.Options{
|
||||||
|
CAFile: filepath.Join(dockerCertPath, "ca.pem"),
|
||||||
|
CertFile: filepath.Join(dockerCertPath, "cert.pem"),
|
||||||
|
KeyFile: filepath.Join(dockerCertPath, "key.pem"),
|
||||||
|
InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "",
|
||||||
|
}
|
||||||
|
tlsc, err := tlsconfig.Client(options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: tlsc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
host := os.Getenv("DOCKER_HOST")
|
||||||
|
if host == "" {
|
||||||
|
host = DefaultDockerHost
|
||||||
|
}
|
||||||
|
version := os.Getenv("DOCKER_API_VERSION")
|
||||||
|
if version == "" {
|
||||||
|
version = api.DefaultVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, err := NewClient(host, version, client, nil)
|
||||||
|
if err != nil {
|
||||||
|
return cli, err
|
||||||
|
}
|
||||||
|
if os.Getenv("DOCKER_API_VERSION") != "" {
|
||||||
|
cli.manualOverride = true
|
||||||
|
}
|
||||||
|
return cli, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient initializes a new API client for the given host and API version.
|
||||||
|
// It uses the given http client as transport.
|
||||||
|
// It also initializes the custom http headers to add to each request.
|
||||||
|
//
|
||||||
|
// It won't send any version information if the version number is empty. It is
|
||||||
|
// highly recommended that you set a version or your client may break if the
|
||||||
|
// server is upgraded.
|
||||||
|
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
|
||||||
|
proto, addr, basePath, err := ParseHost(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if client != nil {
|
||||||
|
if _, ok := client.Transport.(*http.Transport); !ok {
|
||||||
|
return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", client.Transport)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transport := new(http.Transport)
|
||||||
|
sockets.ConfigureTransport(transport, proto, addr)
|
||||||
|
client = &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheme := "http"
|
||||||
|
tlsConfig := resolveTLSConfig(client.Transport)
|
||||||
|
if tlsConfig != nil {
|
||||||
|
// TODO(stevvooe): This isn't really the right way to write clients in Go.
|
||||||
|
// `NewClient` should probably only take an `*http.Client` and work from there.
|
||||||
|
// Unfortunately, the model of having a host-ish/url-thingy as the connection
|
||||||
|
// string has us confusing protocol and transport layers. We continue doing
|
||||||
|
// this to avoid breaking existing clients but this should be addressed.
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
scheme: scheme,
|
||||||
|
host: host,
|
||||||
|
proto: proto,
|
||||||
|
addr: addr,
|
||||||
|
basePath: basePath,
|
||||||
|
client: client,
|
||||||
|
version: version,
|
||||||
|
customHTTPHeaders: httpHeaders,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close ensures that transport.Client is closed
|
||||||
|
// especially needed while using NewClient with *http.Client = nil
|
||||||
|
// for example
|
||||||
|
// client.NewClient("unix:///var/run/docker.sock", nil, "v1.18", map[string]string{"User-Agent": "engine-api-cli-1.0"})
|
||||||
|
func (cli *Client) Close() error {
|
||||||
|
|
||||||
|
if t, ok := cli.client.Transport.(*http.Transport); ok {
|
||||||
|
t.CloseIdleConnections()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAPIPath returns the versioned request path to call the api.
|
||||||
|
// It appends the query parameters to the path if they are not empty.
|
||||||
|
func (cli *Client) getAPIPath(p string, query url.Values) string {
|
||||||
|
var apiPath string
|
||||||
|
if cli.version != "" {
|
||||||
|
v := strings.TrimPrefix(cli.version, "v")
|
||||||
|
apiPath = fmt.Sprintf("%s/v%s%s", cli.basePath, v, p)
|
||||||
|
} else {
|
||||||
|
apiPath = fmt.Sprintf("%s%s", cli.basePath, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := &url.URL{
|
||||||
|
Path: apiPath,
|
||||||
|
}
|
||||||
|
if len(query) > 0 {
|
||||||
|
u.RawQuery = query.Encode()
|
||||||
|
}
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientVersion returns the version string associated with this
|
||||||
|
// instance of the Client. Note that this value can be changed
|
||||||
|
// via the DOCKER_API_VERSION env var.
|
||||||
|
// This operation doesn't acquire a mutex.
|
||||||
|
func (cli *Client) ClientVersion() string {
|
||||||
|
return cli.version
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateClientVersion updates the version string associated with this
|
||||||
|
// instance of the Client. This operation doesn't acquire a mutex.
|
||||||
|
func (cli *Client) UpdateClientVersion(v string) {
|
||||||
|
if !cli.manualOverride {
|
||||||
|
cli.version = v
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHost verifies that the given host strings is valid.
|
||||||
|
func ParseHost(host string) (string, string, string, error) {
|
||||||
|
protoAddrParts := strings.SplitN(host, "://", 2)
|
||||||
|
if len(protoAddrParts) == 1 {
|
||||||
|
return "", "", "", fmt.Errorf("unable to parse docker host `%s`", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
var basePath string
|
||||||
|
proto, addr := protoAddrParts[0], protoAddrParts[1]
|
||||||
|
if proto == "tcp" {
|
||||||
|
parsed, err := url.Parse("tcp://" + addr)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", "", err
|
||||||
|
}
|
||||||
|
addr = parsed.Host
|
||||||
|
basePath = parsed.Path
|
||||||
|
}
|
||||||
|
return proto, addr, basePath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomHTTPHeaders returns the custom http headers associated with this
|
||||||
|
// instance of the Client. This operation doesn't acquire a mutex.
|
||||||
|
func (cli *Client) CustomHTTPHeaders() map[string]string {
|
||||||
|
m := make(map[string]string)
|
||||||
|
for k, v := range cli.customHTTPHeaders {
|
||||||
|
m[k] = v
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCustomHTTPHeaders updates the custom http headers associated with this
|
||||||
|
// instance of the Client. This operation doesn't acquire a mutex.
|
||||||
|
func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) {
|
||||||
|
cli.customHTTPHeaders = headers
|
||||||
|
}
|
6
vendor/github.com/docker/docker/client/client_unix.go
generated
vendored
Normal file
6
vendor/github.com/docker/docker/client/client_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// +build linux freebsd solaris openbsd darwin
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset
|
||||||
|
const DefaultDockerHost = "unix:///var/run/docker.sock"
|
4
vendor/github.com/docker/docker/client/client_windows.go
generated
vendored
Normal file
4
vendor/github.com/docker/docker/client/client_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset
|
||||||
|
const DefaultDockerHost = "npipe:////./pipe/docker_engine"
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue