Merge pull request #528 from andrewhsu/ven

vndr github.com/docker/docker to 84144a8 to fix stuff
This commit is contained in:
Tibor Vass 2017-09-12 17:52:55 -07:00 committed by GitHub
commit f697de32b9
9 changed files with 228 additions and 214 deletions

View File

@ -4,7 +4,7 @@ github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20
github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa github.com/cpuguy83/go-md2man a65d4d2de4d5f7c74868dfa9b202a3c8be315aaa
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
github.com/docker/docker ea220e70a13963da544645376cd9331021eec6b4 github.com/docker/docker 84144a8c66c1bb2af8fa997288f51ef2719971b4
github.com/docker/docker-credential-helpers v0.5.1 github.com/docker/docker-credential-helpers v0.5.1
# the docker/go package contains a customized version of canonical/json # the docker/go package contains a customized version of canonical/json

View File

@ -181,7 +181,7 @@ type ImageBuildOptions struct {
SessionID string SessionID string
// TODO @jhowardmsft LCOW Support: This will require extending to include // TODO @jhowardmsft LCOW Support: This will require extending to include
// `Platform string`, but is ommited for now as it's hard-coded temporarily // `Platform string`, but is omitted for now as it's hard-coded temporarily
// to avoid API changes. // to avoid API changes.
} }

View File

@ -1,5 +1,6 @@
// Package filters provides helper function to parse and handle command line /*Package filters provides tools for encoding a mapping of keys to a set of
// filter, used for example in docker ps or docker images commands. multiple values.
*/
package filters package filters
import ( import (
@ -11,27 +12,34 @@ import (
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
) )
// Args stores filter arguments as map key:{map key: bool}. // Args stores a mapping of keys to a set of multiple values.
// 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 { type Args struct {
fields map[string]map[string]bool fields map[string]map[string]bool
} }
// NewArgs initializes a new Args struct. // KeyValuePair are used to initialize a new Args
func NewArgs() Args { type KeyValuePair struct {
return Args{fields: map[string]map[string]bool{}} Key string
Value string
} }
// ParseFlag parses the argument to the filter flag. Like // Arg creates a new KeyValuePair for initializing Args
func Arg(key, value string) KeyValuePair {
return KeyValuePair{Key: key, Value: value}
}
// NewArgs returns a new Args populated with the initial args
func NewArgs(initialArgs ...KeyValuePair) Args {
args := Args{fields: map[string]map[string]bool{}}
for _, arg := range initialArgs {
args.Add(arg.Key, arg.Value)
}
return args
}
// ParseFlag parses a key=value string and adds it to an Args.
// //
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'` // Deprecated: Use Args.Add()
//
// 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) { func ParseFlag(arg string, prev Args) (Args, error) {
filters := prev filters := prev
if len(arg) == 0 { if len(arg) == 0 {
@ -52,74 +60,95 @@ func ParseFlag(arg string, prev Args) (Args, error) {
return filters, nil return filters, nil
} }
// ErrBadFormat is an error returned in case of bad format for a filter. // ErrBadFormat is an error returned when a filter is not in the form key=value
//
// Deprecated: this error will be removed in a future version
var ErrBadFormat = errors.New("bad format of filter (expected name=value)") 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. // ToParam encodes the Args as args JSON encoded string
//
// Deprecated: use ToJSON
func ToParam(a Args) (string, error) { func ToParam(a Args) (string, error) {
// this way we don't URL encode {}, just empty space return ToJSON(a)
}
// MarshalJSON returns a JSON byte representation of the Args
func (args Args) MarshalJSON() ([]byte, error) {
if len(args.fields) == 0 {
return []byte{}, nil
}
return json.Marshal(args.fields)
}
// ToJSON returns the Args as a JSON encoded string
func ToJSON(a Args) (string, error) {
if a.Len() == 0 { if a.Len() == 0 {
return "", nil return "", nil
} }
buf, err := json.Marshal(a)
buf, err := json.Marshal(a.fields) return string(buf), err
if err != nil {
return "", err
}
return string(buf), nil
} }
// ToParamWithVersion packs the Args into a string for easy transport from client to server. // ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22
// The generated string will depend on the specified version (corresponding to the API version). // then the encoded format will use an older legacy format where the values are a
// list of strings, instead of a set.
//
// Deprecated: Use ToJSON
func ToParamWithVersion(version string, a Args) (string, error) { func ToParamWithVersion(version string, a Args) (string, error) {
// this way we don't URL encode {}, just empty space
if a.Len() == 0 { if a.Len() == 0 {
return "", nil 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") { if version != "" && versions.LessThan(version, "1.22") {
buf, err = json.Marshal(convertArgsToSlice(a.fields)) buf, err := json.Marshal(convertArgsToSlice(a.fields))
} else { return string(buf), err
buf, err = json.Marshal(a.fields)
} }
if err != nil {
return "", err return ToJSON(a)
}
return string(buf), nil
} }
// FromParam unpacks the filter Args. // FromParam decodes a JSON encoded string into Args
//
// Deprecated: use FromJSON
func FromParam(p string) (Args, error) { func FromParam(p string) (Args, error) {
if len(p) == 0 { return FromJSON(p)
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. // FromJSON decodes a JSON encoded string into Args
// It returns a slice of strings to keep backwards compatibility with old code. func FromJSON(p string) (Args, error) {
func (filters Args) Get(field string) []string { args := NewArgs()
values := filters.fields[field]
if p == "" {
return args, nil
}
raw := []byte(p)
err := json.Unmarshal(raw, &args)
if err == nil {
return args, nil
}
// Fallback to parsing arguments in the legacy slice format
deprecated := map[string][]string{}
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
return args, err
}
args.fields = deprecatedArgs(deprecated)
return args, nil
}
// UnmarshalJSON populates the Args from JSON encode bytes
func (args Args) UnmarshalJSON(raw []byte) error {
if len(raw) == 0 {
return nil
}
return json.Unmarshal(raw, &args.fields)
}
// Get returns the list of values associated with the key
func (args Args) Get(key string) []string {
values := args.fields[key]
if values == nil { if values == nil {
return make([]string, 0) return make([]string, 0)
} }
@ -130,37 +159,34 @@ func (filters Args) Get(field string) []string {
return slice return slice
} }
// Add adds a new value to a filter field. // Add a new value to the set of values
func (filters Args) Add(name, value string) { func (args Args) Add(key, value string) {
if _, ok := filters.fields[name]; ok { if _, ok := args.fields[key]; ok {
filters.fields[name][value] = true args.fields[key][value] = true
} else { } else {
filters.fields[name] = map[string]bool{value: true} args.fields[key] = map[string]bool{value: true}
} }
} }
// Del removes a value from a filter field. // Del removes a value from the set
func (filters Args) Del(name, value string) { func (args Args) Del(key, value string) {
if _, ok := filters.fields[name]; ok { if _, ok := args.fields[key]; ok {
delete(filters.fields[name], value) delete(args.fields[key], value)
if len(filters.fields[name]) == 0 { if len(args.fields[key]) == 0 {
delete(filters.fields, name) delete(args.fields, key)
} }
} }
} }
// Len returns the number of fields in the arguments. // Len returns the number of keys in the mapping
func (filters Args) Len() int { func (args Args) Len() int {
return len(filters.fields) return len(args.fields)
} }
// MatchKVList returns true if the values for the specified field matches the ones // MatchKVList returns true if all the pairs in sources exist as key=value
// from the sources. // pairs in the mapping at key, or if there are no values at key.
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}}, func (args Args) MatchKVList(key string, sources map[string]string) bool {
// field is 'label' and sources are {'label1': '1', 'label2': '2'} fieldValues := args.fields[key]
// 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 //do not filter if there is no filter set or cannot determine filter
if len(fieldValues) == 0 { if len(fieldValues) == 0 {
@ -171,8 +197,8 @@ func (filters Args) MatchKVList(field string, sources map[string]string) bool {
return false return false
} }
for name2match := range fieldValues { for value := range fieldValues {
testKV := strings.SplitN(name2match, "=", 2) testKV := strings.SplitN(value, "=", 2)
v, ok := sources[testKV[0]] v, ok := sources[testKV[0]]
if !ok { if !ok {
@ -186,16 +212,13 @@ func (filters Args) MatchKVList(field string, sources map[string]string) bool {
return true return true
} }
// Match returns true if the values for the specified field matches the source string // Match returns true if any of the values at key match the source string
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}}, func (args Args) Match(field, source string) bool {
// field is 'image.name' and source is 'ubuntu' if args.ExactMatch(field, source) {
// it returns true.
func (filters Args) Match(field, source string) bool {
if filters.ExactMatch(field, source) {
return true return true
} }
fieldValues := filters.fields[field] fieldValues := args.fields[field]
for name2match := range fieldValues { for name2match := range fieldValues {
match, err := regexp.MatchString(name2match, source) match, err := regexp.MatchString(name2match, source)
if err != nil { if err != nil {
@ -208,9 +231,9 @@ func (filters Args) Match(field, source string) bool {
return false return false
} }
// ExactMatch returns true if the source matches exactly one of the filters. // ExactMatch returns true if the source matches exactly one of the values.
func (filters Args) ExactMatch(field, source string) bool { func (args Args) ExactMatch(key, source string) bool {
fieldValues, ok := filters.fields[field] fieldValues, ok := args.fields[key]
//do not filter if there is no filter set or cannot determine filter //do not filter if there is no filter set or cannot determine filter
if !ok || len(fieldValues) == 0 { if !ok || len(fieldValues) == 0 {
return true return true
@ -220,14 +243,15 @@ func (filters Args) ExactMatch(field, source string) bool {
return fieldValues[source] return fieldValues[source]
} }
// UniqueExactMatch returns true if there is only one filter and the source matches exactly this one. // UniqueExactMatch returns true if there is only one value and the source
func (filters Args) UniqueExactMatch(field, source string) bool { // matches exactly the value.
fieldValues := filters.fields[field] func (args Args) UniqueExactMatch(key, source string) bool {
fieldValues := args.fields[key]
//do not filter if there is no filter set or cannot determine filter //do not filter if there is no filter set or cannot determine filter
if len(fieldValues) == 0 { if len(fieldValues) == 0 {
return true return true
} }
if len(filters.fields[field]) != 1 { if len(args.fields[key]) != 1 {
return false return false
} }
@ -235,14 +259,14 @@ func (filters Args) UniqueExactMatch(field, source string) bool {
return fieldValues[source] return fieldValues[source]
} }
// FuzzyMatch returns true if the source matches exactly one of the filters, // FuzzyMatch returns true if the source matches exactly one value, or the
// or the source has one of the filters as a prefix. // source has one of the values as a prefix.
func (filters Args) FuzzyMatch(field, source string) bool { func (args Args) FuzzyMatch(key, source string) bool {
if filters.ExactMatch(field, source) { if args.ExactMatch(key, source) {
return true return true
} }
fieldValues := filters.fields[field] fieldValues := args.fields[key]
for prefix := range fieldValues { for prefix := range fieldValues {
if strings.HasPrefix(source, prefix) { if strings.HasPrefix(source, prefix) {
return true return true
@ -251,9 +275,17 @@ func (filters Args) FuzzyMatch(field, source string) bool {
return false return false
} }
// Include returns true if the name of the field to filter is in the filters. // Include returns true if the key exists in the mapping
func (filters Args) Include(field string) bool { //
_, ok := filters.fields[field] // Deprecated: use Contains
func (args Args) Include(field string) bool {
_, ok := args.fields[field]
return ok
}
// Contains returns true if the key exists in the mapping
func (args Args) Contains(field string) bool {
_, ok := args.fields[field]
return ok return ok
} }
@ -265,10 +297,10 @@ func (e invalidFilter) Error() string {
func (invalidFilter) InvalidParameter() {} func (invalidFilter) InvalidParameter() {}
// Validate ensures that all the fields in the filter are valid. // Validate compared the set of accepted keys against the keys in the mapping.
// It returns an error as soon as it finds an invalid field. // An error is returned if any mapping keys are not in the accepted set.
func (filters Args) Validate(accepted map[string]bool) error { func (args Args) Validate(accepted map[string]bool) error {
for name := range filters.fields { for name := range args.fields {
if !accepted[name] { if !accepted[name] {
return invalidFilter(name) return invalidFilter(name)
} }
@ -276,13 +308,14 @@ func (filters Args) Validate(accepted map[string]bool) error {
return nil return nil
} }
// WalkValues iterates over the list of filtered values for a field. // WalkValues iterates over the list of values for a key in the mapping and calls
// It stops the iteration if it finds an error and it returns that error. // op() for each value. If op returns an error the iteration stops and the
func (filters Args) WalkValues(field string, op func(value string) error) error { // error is returned.
if _, ok := filters.fields[field]; !ok { func (args Args) WalkValues(field string, op func(value string) error) error {
if _, ok := args.fields[field]; !ok {
return nil return nil
} }
for v := range filters.fields[field] { for v := range args.fields[field] {
if err := op(v); err != nil { if err := op(v); err != nil {
return err return err
} }

View File

@ -1,10 +1,6 @@
/* /*
Package client is a Go client for the Docker Engine API. 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: For more information about the Engine API, see the documentation:
https://docs.docker.com/engine/reference/api/ https://docs.docker.com/engine/reference/api/
@ -160,7 +156,7 @@ func NewEnvClient() (*Client, error) {
// highly recommended that you set a version or your client may break if the // highly recommended that you set a version or your client may break if the
// server is upgraded. // server is upgraded.
func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) { func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) {
proto, addr, basePath, err := ParseHost(host) hostURL, err := ParseHostURL(host)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -171,7 +167,7 @@ func NewClient(host string, version string, client *http.Client, httpHeaders map
} }
} else { } else {
transport := new(http.Transport) transport := new(http.Transport)
sockets.ConfigureTransport(transport, proto, addr) sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
client = &http.Client{ client = &http.Client{
Transport: transport, Transport: transport,
CheckRedirect: CheckRedirect, CheckRedirect: CheckRedirect,
@ -189,28 +185,24 @@ func NewClient(host string, version string, client *http.Client, httpHeaders map
scheme = "https" scheme = "https"
} }
// TODO: store URL instead of proto/addr/basePath
return &Client{ return &Client{
scheme: scheme, scheme: scheme,
host: host, host: host,
proto: proto, proto: hostURL.Scheme,
addr: addr, addr: hostURL.Host,
basePath: basePath, basePath: hostURL.Path,
client: client, client: client,
version: version, version: version,
customHTTPHeaders: httpHeaders, customHTTPHeaders: httpHeaders,
}, nil }, nil
} }
// Close ensures that transport.Client is closed // Close the transport used by the client
// 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 { func (cli *Client) Close() error {
if t, ok := cli.client.Transport.(*http.Transport); ok { if t, ok := cli.client.Transport.(*http.Transport); ok {
t.CloseIdleConnections() t.CloseIdleConnections()
} }
return nil return nil
} }
@ -220,37 +212,27 @@ func (cli *Client) getAPIPath(p string, query url.Values) string {
var apiPath string var apiPath string
if cli.version != "" { if cli.version != "" {
v := strings.TrimPrefix(cli.version, "v") v := strings.TrimPrefix(cli.version, "v")
apiPath = path.Join(cli.basePath, "/v"+v+p) apiPath = path.Join(cli.basePath, "/v"+v, p)
} else { } else {
apiPath = path.Join(cli.basePath, p) apiPath = path.Join(cli.basePath, p)
} }
return (&url.URL{Path: apiPath, RawQuery: query.Encode()}).String()
u := &url.URL{
Path: apiPath,
}
if len(query) > 0 {
u.RawQuery = query.Encode()
}
return u.String()
} }
// ClientVersion returns the version string associated with this // ClientVersion returns the API version used by this client.
// 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 { func (cli *Client) ClientVersion() string {
return cli.version return cli.version
} }
// NegotiateAPIVersion updates the version string associated with this // NegotiateAPIVersion queries the API and updates the version to match the
// instance of the Client to match the latest version the server supports // API version. Any errors are silently ignored.
func (cli *Client) NegotiateAPIVersion(ctx context.Context) { func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
ping, _ := cli.Ping(ctx) ping, _ := cli.Ping(ctx)
cli.NegotiateAPIVersionPing(ping) cli.NegotiateAPIVersionPing(ping)
} }
// NegotiateAPIVersionPing updates the version string associated with this // NegotiateAPIVersionPing updates the client version to match the Ping.APIVersion
// instance of the Client to match the latest version the server supports // if the ping version is less than the default version.
func (cli *Client) NegotiateAPIVersionPing(p types.Ping) { func (cli *Client) NegotiateAPIVersionPing(p types.Ping) {
if cli.manualOverride { if cli.manualOverride {
return return
@ -272,17 +254,28 @@ func (cli *Client) NegotiateAPIVersionPing(p types.Ping) {
} }
} }
// DaemonHost returns the host associated with this instance of the Client. // DaemonHost returns the host address used by the client
// This operation doesn't acquire a mutex.
func (cli *Client) DaemonHost() string { func (cli *Client) DaemonHost() string {
return cli.host return cli.host
} }
// ParseHost verifies that the given host strings is valid. // ParseHost parses a url string, validates the strings is a host url, and returns
// the parsed host as: protocol, address, and base path
// Deprecated: use ParseHostURL
func ParseHost(host string) (string, string, string, error) { func ParseHost(host string) (string, string, string, error) {
hostURL, err := ParseHostURL(host)
if err != nil {
return "", "", "", err
}
return hostURL.Scheme, hostURL.Host, hostURL.Path, nil
}
// ParseHostURL parses a url string, validates the string is a host url, and
// returns the parsed URL
func ParseHostURL(host string) (*url.URL, error) {
protoAddrParts := strings.SplitN(host, "://", 2) protoAddrParts := strings.SplitN(host, "://", 2)
if len(protoAddrParts) == 1 { if len(protoAddrParts) == 1 {
return "", "", "", fmt.Errorf("unable to parse docker host `%s`", host) return nil, fmt.Errorf("unable to parse docker host `%s`", host)
} }
var basePath string var basePath string
@ -290,16 +283,19 @@ func ParseHost(host string) (string, string, string, error) {
if proto == "tcp" { if proto == "tcp" {
parsed, err := url.Parse("tcp://" + addr) parsed, err := url.Parse("tcp://" + addr)
if err != nil { if err != nil {
return "", "", "", err return nil, err
} }
addr = parsed.Host addr = parsed.Host
basePath = parsed.Path basePath = parsed.Path
} }
return proto, addr, basePath, nil return &url.URL{
Scheme: proto,
Host: addr,
Path: basePath,
}, nil
} }
// CustomHTTPHeaders returns the custom http headers associated with this // CustomHTTPHeaders returns the custom http headers stored by the client.
// instance of the Client. This operation doesn't acquire a mutex.
func (cli *Client) CustomHTTPHeaders() map[string]string { func (cli *Client) CustomHTTPHeaders() map[string]string {
m := make(map[string]string) m := make(map[string]string)
for k, v := range cli.customHTTPHeaders { for k, v := range cli.customHTTPHeaders {
@ -308,8 +304,7 @@ func (cli *Client) CustomHTTPHeaders() map[string]string {
return m return m
} }
// SetCustomHTTPHeaders updates the custom http headers associated with this // SetCustomHTTPHeaders that will be set on every HTTP request made by the client.
// instance of the Client. This operation doesn't acquire a mutex.
func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) { func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) {
cli.customHTTPHeaders = headers cli.customHTTPHeaders = headers
} }

View File

@ -36,8 +36,8 @@ type notFound interface {
NotFound() bool // Is the error a NotFound error NotFound() bool // Is the error a NotFound error
} }
// IsErrNotFound returns true if the error is caused with an // IsErrNotFound returns true if the error is a NotFound error, which is returned
// object (image, container, network, volume, …) is not found in the docker host. // by the API when some object is not found.
func IsErrNotFound(err error) bool { func IsErrNotFound(err error) bool {
te, ok := err.(notFound) te, ok := err.(notFound)
return ok && te.NotFound() return ok && te.NotFound()
@ -60,6 +60,8 @@ func (e imageNotFoundError) Error() string {
// IsErrImageNotFound returns true if the error is caused // IsErrImageNotFound returns true if the error is caused
// when an image is not found in the docker host. // when an image is not found in the docker host.
//
// Deprecated: Use IsErrNotFound
func IsErrImageNotFound(err error) bool { func IsErrImageNotFound(err error) bool {
return IsErrNotFound(err) return IsErrNotFound(err)
} }
@ -81,6 +83,8 @@ func (e containerNotFoundError) Error() string {
// IsErrContainerNotFound returns true if the error is caused // IsErrContainerNotFound returns true if the error is caused
// when a container is not found in the docker host. // when a container is not found in the docker host.
//
// Deprecated: Use IsErrNotFound
func IsErrContainerNotFound(err error) bool { func IsErrContainerNotFound(err error) bool {
return IsErrNotFound(err) return IsErrNotFound(err)
} }
@ -102,6 +106,8 @@ func (e networkNotFoundError) Error() string {
// IsErrNetworkNotFound returns true if the error is caused // IsErrNetworkNotFound returns true if the error is caused
// when a network is not found in the docker host. // when a network is not found in the docker host.
//
// Deprecated: Use IsErrNotFound
func IsErrNetworkNotFound(err error) bool { func IsErrNetworkNotFound(err error) bool {
return IsErrNotFound(err) return IsErrNotFound(err)
} }
@ -123,6 +129,8 @@ func (e volumeNotFoundError) Error() string {
// IsErrVolumeNotFound returns true if the error is caused // IsErrVolumeNotFound returns true if the error is caused
// when a volume is not found in the docker host. // when a volume is not found in the docker host.
//
// Deprecated: Use IsErrNotFound
func IsErrVolumeNotFound(err error) bool { func IsErrVolumeNotFound(err error) bool {
return IsErrNotFound(err) return IsErrNotFound(err)
} }
@ -161,6 +169,8 @@ func (e nodeNotFoundError) NotFound() bool {
// IsErrNodeNotFound returns true if the error is caused // IsErrNodeNotFound returns true if the error is caused
// when a node is not found. // when a node is not found.
//
// Deprecated: Use IsErrNotFound
func IsErrNodeNotFound(err error) bool { func IsErrNodeNotFound(err error) bool {
_, ok := err.(nodeNotFoundError) _, ok := err.(nodeNotFoundError)
return ok return ok
@ -183,6 +193,8 @@ func (e serviceNotFoundError) NotFound() bool {
// IsErrServiceNotFound returns true if the error is caused // IsErrServiceNotFound returns true if the error is caused
// when a service is not found. // when a service is not found.
//
// Deprecated: Use IsErrNotFound
func IsErrServiceNotFound(err error) bool { func IsErrServiceNotFound(err error) bool {
_, ok := err.(serviceNotFoundError) _, ok := err.(serviceNotFoundError)
return ok return ok
@ -205,6 +217,8 @@ func (e taskNotFoundError) NotFound() bool {
// IsErrTaskNotFound returns true if the error is caused // IsErrTaskNotFound returns true if the error is caused
// when a task is not found. // when a task is not found.
//
// Deprecated: Use IsErrNotFound
func IsErrTaskNotFound(err error) bool { func IsErrTaskNotFound(err error) bool {
_, ok := err.(taskNotFoundError) _, ok := err.(taskNotFoundError)
return ok return ok
@ -251,6 +265,8 @@ func (e secretNotFoundError) NotFound() bool {
// IsErrSecretNotFound returns true if the error is caused // IsErrSecretNotFound returns true if the error is caused
// when a secret is not found. // when a secret is not found.
//
// Deprecated: Use IsErrNotFound
func IsErrSecretNotFound(err error) bool { func IsErrSecretNotFound(err error) bool {
_, ok := err.(secretNotFoundError) _, ok := err.(secretNotFoundError)
return ok return ok
@ -273,6 +289,8 @@ func (e configNotFoundError) NotFound() bool {
// IsErrConfigNotFound returns true if the error is caused // IsErrConfigNotFound returns true if the error is caused
// when a config is not found. // when a config is not found.
//
// Deprecated: Use IsErrNotFound
func IsErrConfigNotFound(err error) bool { func IsErrConfigNotFound(err error) bool {
_, ok := err.(configNotFoundError) _, ok := err.(configNotFoundError)
return ok return ok
@ -295,6 +313,8 @@ func (e pluginNotFoundError) Error() string {
// IsErrPluginNotFound returns true if the error is caused // IsErrPluginNotFound returns true if the error is caused
// when a plugin is not found in the docker host. // when a plugin is not found in the docker host.
//
// Deprecated: Use IsErrNotFound
func IsErrPluginNotFound(err error) bool { func IsErrPluginNotFound(err error) bool {
return IsErrNotFound(err) return IsErrNotFound(err)
} }

View File

@ -1,41 +0,0 @@
package client
// parse_logs.go contains utility helpers for getting information out of docker
// log lines. really, it only contains ParseDetails right now. maybe in the
// future there will be some desire to parse log messages back into a struct?
// that would go here if we did
import (
"net/url"
"strings"
"github.com/pkg/errors"
)
// ParseLogDetails takes a details string of key value pairs in the form
// "k=v,l=w", where the keys and values are url query escaped, and each pair
// is separated by a comma, returns a map. returns an error if the details
// string is not in a valid format
// the exact form of details encoding is implemented in
// api/server/httputils/write_log_stream.go
func ParseLogDetails(details string) (map[string]string, error) {
pairs := strings.Split(details, ",")
detailsMap := make(map[string]string, len(pairs))
for _, pair := range pairs {
p := strings.SplitN(pair, "=", 2)
// if there is no equals sign, we will only get 1 part back
if len(p) != 2 {
return nil, errors.New("invalid details format")
}
k, err := url.QueryUnescape(p[0])
if err != nil {
return nil, err
}
v, err := url.QueryUnescape(p[1])
if err != nil {
return nil, err
}
detailsMap[k] = v
}
return detailsMap, nil
}

View File

@ -88,13 +88,12 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, image, encodedAuth string) (string, []swarm.Platform, error) { func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, image, encodedAuth string) (string, []swarm.Platform, error) {
distributionInspect, err := cli.DistributionInspect(ctx, image, encodedAuth) distributionInspect, err := cli.DistributionInspect(ctx, image, encodedAuth)
imageWithDigest := image
var platforms []swarm.Platform var platforms []swarm.Platform
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
imageWithDigest = imageWithDigestString(image, distributionInspect.Descriptor.Digest) imageWithDigest := imageWithDigestString(image, distributionInspect.Descriptor.Digest)
if len(distributionInspect.Platforms) > 0 { if len(distributionInspect.Platforms) > 0 {
platforms = make([]swarm.Platform, 0, len(distributionInspect.Platforms)) platforms = make([]swarm.Platform, 0, len(distributionInspect.Platforms))
@ -105,12 +104,12 @@ func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, ima
// something like "armv7l" (includes the variant), which causes arm images // something like "armv7l" (includes the variant), which causes arm images
// to stop working with swarm mode. This patch removes the architecture // to stop working with swarm mode. This patch removes the architecture
// constraint for arm images to ensure tasks get scheduled. // constraint for arm images to ensure tasks get scheduled.
arch := strings.ToLower(p.Architecture) arch := p.Architecture
if arch == "arm" { if strings.ToLower(arch) == "arm" {
arch = "" arch = ""
} }
platforms = append(platforms, swarm.Platform{ platforms = append(platforms, swarm.Platform{
Architecture: p.Architecture, Architecture: arch,
OS: p.OS, OS: p.OS,
}) })
} }

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"path"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -18,8 +19,15 @@ func (cli *Client) VolumeInspect(ctx context.Context, volumeID string) (types.Vo
// VolumeInspectWithRaw returns the information about a specific volume in the docker host and its raw representation // VolumeInspectWithRaw returns the information about a specific volume in the docker host and its raw representation
func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error) { func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error) {
// The empty ID needs to be handled here because with an empty ID the
// request url will not contain a trailing / which calls the volume list API
// instead of volume inspect
if volumeID == "" {
return types.Volume{}, nil, volumeNotFoundError{volumeID}
}
var volume types.Volume var volume types.Volume
resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil) resp, err := cli.get(ctx, path.Join("/volumes", volumeID), nil, nil)
if err != nil { if err != nil {
if resp.statusCode == http.StatusNotFound { if resp.statusCode == http.StatusNotFound {
return volume, nil, volumeNotFoundError{volumeID} return volume, nil, volumeNotFoundError{volumeID}

View File

@ -1,6 +1,6 @@
# the following lines are in sorted order, FYI # the following lines are in sorted order, FYI
github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e
github.com/Microsoft/hcsshim v0.6.3 github.com/Microsoft/hcsshim v0.6.4
github.com/Microsoft/go-winio v0.4.5 github.com/Microsoft/go-winio v0.4.5
github.com/moby/buildkit da2b9dc7dab99e824b2b1067ad7d0523e32dd2d9 https://github.com/dmcgowan/buildkit.git github.com/moby/buildkit da2b9dc7dab99e824b2b1067ad7d0523e32dd2d9 https://github.com/dmcgowan/buildkit.git
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
@ -8,7 +8,7 @@ github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
github.com/gorilla/context v1.1 github.com/gorilla/context v1.1
github.com/gorilla/mux v1.1 github.com/gorilla/mux v1.1
github.com/Microsoft/opengcs v0.3.2 github.com/Microsoft/opengcs v0.3.3
github.com/kr/pty 5cf931ef8f github.com/kr/pty 5cf931ef8f
github.com/mattn/go-shellwords v1.0.3 github.com/mattn/go-shellwords v1.0.3
github.com/sirupsen/logrus v1.0.1 github.com/sirupsen/logrus v1.0.1
@ -63,7 +63,7 @@ github.com/pborman/uuid v1.0
google.golang.org/grpc v1.3.0 google.golang.org/grpc v1.3.0
# When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly # When updating, also update RUNC_COMMIT in hack/dockerfile/binaries-commits accordingly
github.com/opencontainers/runc 3f2f8b84a77f73d38244dd690525642a72156c64 github.com/opencontainers/runc 1c81e2a794c6e26a4c650142ae8893c47f619764
github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13 github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
github.com/opencontainers/runtime-spec v1.0.0 github.com/opencontainers/runtime-spec v1.0.0