diff --git a/libnetwork/Godeps/Godeps.json b/libnetwork/Godeps/Godeps.json index 6ef8ba03ec..476e88db76 100644 --- a/libnetwork/Godeps/Godeps.json +++ b/libnetwork/Godeps/Godeps.json @@ -1,7 +1,6 @@ { "ImportPath": "github.com/docker/libnetwork", "GoVersion": "go1.5", - "GodepVersion": "v74", "Packages": [ "./..." ], @@ -81,105 +80,110 @@ "Comment": "v1-26-gef32fa3", "Rev": "ef32fa3046d9f249d399f98ebaf9be944430fd1d" }, + { + "ImportPath": "github.com/docker/docker/api/types/filters", + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" + }, + { + "ImportPath": "github.com/docker/docker/api/types/network", + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" + }, + { + "ImportPath": "github.com/docker/docker/api/types/versions", + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" + }, { "ImportPath": "github.com/docker/docker/opts", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/discovery", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/discovery/kv", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/homedir", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/ioutils", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/longpath", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" - }, - { - "ImportPath": "github.com/docker/docker/pkg/mflag", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/mount", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/parsers/kernel", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/plugins", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/plugins/transport", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/random", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/reexec", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/signal", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/stringid", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/symlink", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/system", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/term", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/docker/pkg/term/windows", - "Comment": "v1.4.1-12227-g86a7632", - "Rev": "86a7632d63bdddb95aaf1472648056a4fb737d38" - }, - { - "ImportPath": "github.com/docker/engine-api/types/network", - "Comment": "v0.3.1-210-g25941ec", - "Rev": "25941ecf6e8351810e8530c60de8dda7d5e1baba" + "Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af", + "Rev": "426a0af0759798d8e3332b38236ee40df6d14798" }, { "ImportPath": "github.com/docker/go-connections/sockets", @@ -289,6 +293,11 @@ "Comment": "v0.7.0-47-g598c548", "Rev": "598c54895cc5a7b1a24a398d635e8c0ea0959870" }, + { + "ImportPath": "github.com/mattn/go-shellwords", + "Comment": "v1.0.0-1-g525bede", + "Rev": "525bedee691b5a8df547cb5cf9f86b7fb1883e24" + }, { "ImportPath": "github.com/miekg/dns", "Rev": "d27455715200c7d3e321a1e5cadb27c9ee0b0f02" diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/filters/parse.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/filters/parse.go new file mode 100644 index 0000000000..12e5a3b0d5 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/filters/parse.go @@ -0,0 +1,307 @@ +// 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 + buf := []byte{} + err := errors.New("") + 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) + } +} + +// 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 sources == nil || 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 +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/engine-api/types/network/network.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/network/network.go similarity index 100% rename from libnetwork/Godeps/_workspace/src/github.com/docker/engine-api/types/network/network.go rename to libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/network/network.go diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/versions/README.md b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/versions/README.md new file mode 100644 index 0000000000..cdac50a53c --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/versions/README.md @@ -0,0 +1,14 @@ +## Legacy API type versions + +This package includes types for legacy API versions. The stable version of the API types live in `api/types/*.go`. + +Consider moving a type here when you need to keep backwards compatibility in the API. This legacy types are organized by the latest API version they appear in. For instance, types in the `v1p19` package are valid for API versions below or equal `1.19`. Types in the `v1p20` package are valid for the API version `1.20`, since the versions below that will use the legacy types in `v1p19`. + +### Package name conventions + +The package name convention is to use `v` as a prefix for the version number and `p`(patch) as a separator. We use this nomenclature due to a few restrictions in the Go package name convention: + +1. We cannot use `.` because it's interpreted by the language, think of `v1.20.CallFunction`. +2. We cannot use `_` because golint complains about it. The code is actually valid, but it looks probably more weird: `v1_20.CallFunction`. + +For instance, if you want to modify a type that was available in the version `1.21` of the API but it will have different fields in the version `1.22`, you want to create a new package under `api/types/versions/v1p21`. diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/versions/compare.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/versions/compare.go new file mode 100644 index 0000000000..611d4fed66 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/api/types/versions/compare.go @@ -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 +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/hosts.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/hosts.go index ad16759236..266df1e537 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/hosts.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/hosts.go @@ -63,14 +63,14 @@ func ParseHost(defaultToTLS bool, val string) (string, error) { // parseDockerDaemonHost parses the specified address and returns an address that will be used as the host. // Depending of the address specified, this may return one of the global Default* strings defined in hosts.go. func parseDockerDaemonHost(addr string) (string, error) { - addrParts := strings.Split(addr, "://") + addrParts := strings.SplitN(addr, "://", 2) if len(addrParts) == 1 && addrParts[0] != "" { addrParts = []string{"tcp", addrParts[0]} } switch addrParts[0] { case "tcp": - return parseTCPAddr(addrParts[1], DefaultTCPHost) + return ParseTCPAddr(addrParts[1], DefaultTCPHost) case "unix": return parseSimpleProtoAddr("unix", addrParts[1], DefaultUnixSocket) case "npipe": @@ -97,12 +97,12 @@ func parseSimpleProtoAddr(proto, addr, defaultAddr string) (string, error) { return fmt.Sprintf("%s://%s", proto, addr), nil } -// parseTCPAddr parses and validates that the specified address is a valid TCP +// ParseTCPAddr parses and validates that the specified address is a valid TCP // address. It returns a formatted TCP address, either using the address parsed // from tryAddr, or the contents of defaultAddr if tryAddr is a blank string. // tryAddr is expected to have already been Trim()'d // defaultAddr must be in the full `tcp://host:port` form -func parseTCPAddr(tryAddr string, defaultAddr string) (string, error) { +func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) { if tryAddr == "" || tryAddr == "tcp://" { return defaultAddr, nil } @@ -127,8 +127,11 @@ func parseTCPAddr(tryAddr string, defaultAddr string) (string, error) { if err != nil { return "", err } - host, port, err := net.SplitHostPort(u.Host) + if err != nil { + // try port addition once + host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort)) + } if err != nil { return "", fmt.Errorf("Invalid bind address format: %s", tryAddr) } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/ip.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/ip.go index c7b0dc9947..fb03b50111 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/ip.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/ip.go @@ -40,3 +40,8 @@ func (o *IPOpt) String() string { } return o.IP.String() } + +// Type returns the type of the option +func (o *IPOpt) Type() string { + return "ip" +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/opts.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/opts.go index 0b09981778..f8bb3ba745 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/opts.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/opts/opts.go @@ -5,6 +5,8 @@ import ( "net" "regexp" "strings" + + "github.com/docker/docker/api/types/filters" ) var ( @@ -100,6 +102,11 @@ func (opts *ListOpts) Len() int { return len((*opts.values)) } +// Type returns a string name for this Option type +func (opts *ListOpts) Type() string { + return "list" +} + // NamedOption is an interface that list and map options // with names implement. type NamedOption interface { @@ -129,7 +136,7 @@ func (o *NamedListOpts) Name() string { return o.name } -//MapOpts holds a map of values and a validation function. +// MapOpts holds a map of values and a validation function. type MapOpts struct { values map[string]string validator ValidatorFctType @@ -163,6 +170,11 @@ func (opts *MapOpts) String() string { return fmt.Sprintf("%v", map[string]string((opts.values))) } +// Type returns a string name for this Option type +func (opts *MapOpts) Type() string { + return "map" +} + // NewMapOpts creates a new MapOpts with the specified map of values and a validator. func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts { if values == nil { @@ -241,7 +253,7 @@ func ValidateLabel(val string) (string, error) { return val, nil } -// ValidateSysctl validates an sysctl and returns it. +// ValidateSysctl validates a sysctl and returns it. func ValidateSysctl(val string) (string, error) { validSysctlMap := map[string]bool{ "kernel.msgmax": true, @@ -272,3 +284,38 @@ func ValidateSysctl(val string) (string, error) { } return "", fmt.Errorf("sysctl '%s' is not whitelisted", val) } + +// FilterOpt is a flag type for validating filters +type FilterOpt struct { + filter filters.Args +} + +// NewFilterOpt returns a new FilterOpt +func NewFilterOpt() FilterOpt { + return FilterOpt{filter: filters.NewArgs()} +} + +func (o *FilterOpt) String() string { + repr, err := filters.ToParam(o.filter) + if err != nil { + return "invalid filters" + } + return repr +} + +// Set sets the value of the opt by parsing the command line value +func (o *FilterOpt) Set(value string) error { + var err error + o.filter, err = filters.ParseFlag(value, o.filter) + return err +} + +// Type returns the option type +func (o *FilterOpt) Type() string { + return "filter" +} + +// Value returns the value of this option +func (o *FilterOpt) Value() filters.Args { + return o.filter +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/backends.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/backends.go index 65364c9ae8..10febb5442 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/backends.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/backends.go @@ -6,7 +6,7 @@ import ( "strings" "time" - log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" ) var ( @@ -21,7 +21,7 @@ func Register(scheme string, d Backend) error { if _, exists := backends[scheme]; exists { return fmt.Errorf("scheme already registered %s", scheme) } - log.WithField("name", scheme).Debug("Registering discovery service") + logrus.WithField("name", scheme).Debugf("Registering discovery service") backends[scheme] = d return nil } @@ -57,7 +57,7 @@ func ParseAdvertise(advertise string) (string, error) { return advertise, nil } - // If advertise is a valid interface name, get the valid ipv4 address and use it to advertise + // If advertise is a valid interface name, get the valid IPv4 address and use it to advertise ifaceName := addr iface, err = net.InterfaceByName(ifaceName) if err != nil { @@ -98,7 +98,7 @@ func ParseAdvertise(advertise string) (string, error) { func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) { scheme, uri := parse(rawurl) if backend, exists := backends[scheme]; exists { - log.WithFields(log.Fields{"name": scheme, "uri": uri}).Debug("Initializing discovery service") + logrus.WithFields(logrus.Fields{"name": scheme, "uri": uri}).Debugf("Initializing discovery service") err := backend.Initialize(uri, heartbeat, ttl, clusterOpts) return backend, err } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/kv/kv.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/kv/kv.go index f371c0cba0..2e0f69dcea 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/kv/kv.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/kv/kv.go @@ -6,7 +6,7 @@ import ( "strings" "time" - log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/discovery" "github.com/docker/go-connections/tlsconfig" "github.com/docker/libkv" @@ -73,7 +73,7 @@ func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Du var config *store.Config if clusterOpts["kv.cacertfile"] != "" && clusterOpts["kv.certfile"] != "" && clusterOpts["kv.keyfile"] != "" { - log.Info("Initializing discovery with TLS") + logrus.Infof("Initializing discovery with TLS") tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ CAFile: clusterOpts["kv.cacertfile"], CertFile: clusterOpts["kv.certfile"], @@ -93,7 +93,7 @@ func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Du TLS: tlsConfig, } } else { - log.Info("Initializing discovery without TLS") + logrus.Infof("Initializing discovery without TLS") } // Creates a new store, will ignore options given @@ -112,7 +112,7 @@ func (s *Discovery) watchOnce(stopCh <-chan struct{}, watchCh <-chan []*store.KV return true } - log.WithField("discovery", s.backend).Debugf("Watch triggered with %d nodes", len(pairs)) + logrus.WithField("discovery", s.backend).Debugf("Watch triggered with %d nodes", len(pairs)) // Convert `KVPair` into `discovery.Entry`. addrs := make([]string, len(pairs)) diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/bytespipe.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/bytespipe.go index eca129be39..72a04f3491 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/bytespipe.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/bytespipe.go @@ -133,8 +133,9 @@ func (bp *BytesPipe) Read(p []byte) (n int, err error) { } bp.wait.Wait() if bp.bufLen == 0 && bp.closeErr != nil { + err := bp.closeErr bp.mu.Unlock() - return 0, bp.closeErr + return 0, err } } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/fswriters.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/fswriters.go index ca97670724..6dc50a03dc 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/fswriters.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/fswriters.go @@ -15,13 +15,15 @@ func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, err if err != nil { return nil, err } + abspath, err := filepath.Abs(filename) if err != nil { return nil, err } return &atomicFileWriter{ - f: f, - fn: abspath, + f: f, + fn: abspath, + perm: perm, }, nil } @@ -34,6 +36,7 @@ func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { n, err := f.Write(data) if err == nil && n < len(data) { err = io.ErrShortWrite + f.(*atomicFileWriter).writeErr = err } if err1 := f.Close(); err == nil { err = err1 @@ -45,6 +48,7 @@ type atomicFileWriter struct { f *os.File fn string writeErr error + perm os.FileMode } func (w *atomicFileWriter) Write(dt []byte) (int, error) { @@ -57,7 +61,7 @@ func (w *atomicFileWriter) Write(dt []byte) (int, error) { func (w *atomicFileWriter) Close() (retErr error) { defer func() { - if retErr != nil { + if retErr != nil || w.writeErr != nil { os.Remove(w.f.Name()) } }() @@ -68,6 +72,9 @@ func (w *atomicFileWriter) Close() (retErr error) { if err := w.f.Close(); err != nil { return err } + if err := os.Chmod(w.f.Name(), w.perm); err != nil { + return err + } if w.writeErr == nil { return os.Rename(w.f.Name(), w.fn) } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/multireader.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/multireader.go index 0d2d76b479..234999bc92 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/multireader.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/multireader.go @@ -97,27 +97,24 @@ func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) { } func (r *multiReadSeeker) getReaderForOffset(offset int64) (io.ReadSeeker, int64, error) { - var rdr io.ReadSeeker - var rdrOffset int64 - for i, rdr := range r.readers { - offsetTo, err := r.getOffsetToReader(rdr) + var offsetTo int64 + + for _, rdr := range r.readers { + size, err := getReadSeekerSize(rdr) if err != nil { return nil, -1, err } - if offsetTo > offset { - rdr = r.readers[i-1] - rdrOffset = offsetTo - offset - break + if offsetTo+size > offset { + return rdr, offset - offsetTo, nil } - if rdr == r.readers[len(r.readers)-1] { - rdrOffset = offsetTo + offset - break + return rdr, offsetTo + offset, nil } + offsetTo += size } - return rdr, rdrOffset, nil + return nil, 0, nil } func (r *multiReadSeeker) getCurOffset() (int64, error) { diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/readers.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/readers.go index e73b02bbf1..63f3c07f46 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/readers.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/ioutils/readers.go @@ -55,7 +55,7 @@ func HashData(src io.Reader) (string, error) { return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil } -// OnEOFReader wraps a io.ReadCloser and a function +// OnEOFReader wraps an io.ReadCloser and a function // the function will run at the end of file or close the file. type OnEOFReader struct { Rc io.ReadCloser diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/flags.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/flags.go index d2fb1fb4d0..607dbed43a 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/flags.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/mount/flags.go @@ -5,6 +5,112 @@ import ( "strings" ) +var flags = map[string]struct { + clear bool + flag int +}{ + "defaults": {false, 0}, + "ro": {false, RDONLY}, + "rw": {true, RDONLY}, + "suid": {true, NOSUID}, + "nosuid": {false, NOSUID}, + "dev": {true, NODEV}, + "nodev": {false, NODEV}, + "exec": {true, NOEXEC}, + "noexec": {false, NOEXEC}, + "sync": {false, SYNCHRONOUS}, + "async": {true, SYNCHRONOUS}, + "dirsync": {false, DIRSYNC}, + "remount": {false, REMOUNT}, + "mand": {false, MANDLOCK}, + "nomand": {true, MANDLOCK}, + "atime": {true, NOATIME}, + "noatime": {false, NOATIME}, + "diratime": {true, NODIRATIME}, + "nodiratime": {false, NODIRATIME}, + "bind": {false, BIND}, + "rbind": {false, RBIND}, + "unbindable": {false, UNBINDABLE}, + "runbindable": {false, RUNBINDABLE}, + "private": {false, PRIVATE}, + "rprivate": {false, RPRIVATE}, + "shared": {false, SHARED}, + "rshared": {false, RSHARED}, + "slave": {false, SLAVE}, + "rslave": {false, RSLAVE}, + "relatime": {false, RELATIME}, + "norelatime": {true, RELATIME}, + "strictatime": {false, STRICTATIME}, + "nostrictatime": {true, STRICTATIME}, +} + +var validFlags = map[string]bool{ + "": true, + "size": true, + "mode": true, + "uid": true, + "gid": true, + "nr_inodes": true, + "nr_blocks": true, + "mpol": true, +} + +var propagationFlags = map[string]bool{ + "bind": true, + "rbind": true, + "unbindable": true, + "runbindable": true, + "private": true, + "rprivate": true, + "shared": true, + "rshared": true, + "slave": true, + "rslave": true, +} + +// MergeTmpfsOptions merge mount options to make sure there is no duplicate. +func MergeTmpfsOptions(options []string) ([]string, error) { + // We use collisions maps to remove duplicates. + // For flag, the key is the flag value (the key for propagation flag is -1) + // For data=value, the key is the data + flagCollisions := map[int]bool{} + dataCollisions := map[string]bool{} + + var newOptions []string + // We process in reverse order + for i := len(options) - 1; i >= 0; i-- { + option := options[i] + if option == "defaults" { + continue + } + if f, ok := flags[option]; ok && f.flag != 0 { + // There is only one propagation mode + key := f.flag + if propagationFlags[option] { + key = -1 + } + // Check to see if there is collision for flag + if !flagCollisions[key] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + flagCollisions[key] = true + } + continue + } + opt := strings.SplitN(option, "=", 2) + if len(opt) != 2 || !validFlags[opt[0]] { + return nil, fmt.Errorf("Invalid tmpfs option %q", opt) + } + if !dataCollisions[opt[0]] { + // We prepend the option and add to collision map + newOptions = append([]string{option}, newOptions...) + dataCollisions[opt[0]] = true + } + } + + return newOptions, nil +} + // Parse fstab type mount options into mount() flags // and device specific data func parseOptions(options string) (int, string) { @@ -13,45 +119,6 @@ func parseOptions(options string) (int, string) { data []string ) - flags := map[string]struct { - clear bool - flag int - }{ - "defaults": {false, 0}, - "ro": {false, RDONLY}, - "rw": {true, RDONLY}, - "suid": {true, NOSUID}, - "nosuid": {false, NOSUID}, - "dev": {true, NODEV}, - "nodev": {false, NODEV}, - "exec": {true, NOEXEC}, - "noexec": {false, NOEXEC}, - "sync": {false, SYNCHRONOUS}, - "async": {true, SYNCHRONOUS}, - "dirsync": {false, DIRSYNC}, - "remount": {false, REMOUNT}, - "mand": {false, MANDLOCK}, - "nomand": {true, MANDLOCK}, - "atime": {true, NOATIME}, - "noatime": {false, NOATIME}, - "diratime": {true, NODIRATIME}, - "nodiratime": {false, NODIRATIME}, - "bind": {false, BIND}, - "rbind": {false, RBIND}, - "unbindable": {false, UNBINDABLE}, - "runbindable": {false, RUNBINDABLE}, - "private": {false, PRIVATE}, - "rprivate": {false, RPRIVATE}, - "shared": {false, SHARED}, - "rshared": {false, RSHARED}, - "slave": {false, SLAVE}, - "rslave": {false, RSLAVE}, - "relatime": {false, RELATIME}, - "norelatime": {true, RELATIME}, - "strictatime": {false, STRICTATIME}, - "nostrictatime": {true, STRICTATIME}, - } - for _, o := range strings.Split(options, ",") { // If the option does not exist in the flags table or the flag // is not supported on the platform, @@ -72,16 +139,6 @@ func parseOptions(options string) (int, string) { // ParseTmpfsOptions parse fstab type mount options into flags and data func ParseTmpfsOptions(options string) (int, string, error) { flags, data := parseOptions(options) - validFlags := map[string]bool{ - "": true, - "size": true, - "mode": true, - "uid": true, - "gid": true, - "nr_inodes": true, - "nr_blocks": true, - "mpol": true, - } for _, o := range strings.Split(data, ",") { opt := strings.SplitN(o, "=", 2) if !validFlags[opt[0]] { diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go index a21ba137e3..7738fc7411 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go @@ -5,7 +5,6 @@ package kernel import ( - "bytes" "errors" "fmt" ) @@ -46,31 +45,6 @@ func CompareKernelVersion(a, b VersionInfo) int { return 0 } -// GetKernelVersion gets the current kernel version. -func GetKernelVersion() (*VersionInfo, error) { - var ( - err error - ) - - uts, err := uname() - if err != nil { - return nil, err - } - - release := make([]byte, len(uts.Release)) - - i := 0 - for _, c := range uts.Release { - release[i] = byte(c) - i++ - } - - // Remove the \x00 from the release for Atoi to parse correctly - release = release[:bytes.IndexByte(release, 0)] - - return ParseRelease(string(release)) -} - // ParseRelease parses a string and creates a VersionInfo based on it. func ParseRelease(release string) (*VersionInfo, error) { var ( diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_darwin.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_darwin.go new file mode 100644 index 0000000000..71f205b285 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_darwin.go @@ -0,0 +1,56 @@ +// +build darwin + +// Package kernel provides helper function to get, parse and compare kernel +// versions for different platforms. +package kernel + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/mattn/go-shellwords" +) + +// GetKernelVersion gets the current kernel version. +func GetKernelVersion() (*VersionInfo, error) { + release, err := getRelease() + if err != nil { + return nil, err + } + + return ParseRelease(release) +} + +// getRelease uses `system_profiler SPSoftwareDataType` to get OSX kernel version +func getRelease() (string, error) { + cmd := exec.Command("system_profiler", "SPSoftwareDataType") + osName, err := cmd.Output() + if err != nil { + return "", err + } + + var release string + data := strings.Split(string(osName), "\n") + for _, line := range data { + if strings.Contains(line, "Kernel Version") { + // It has the format like ' Kernel Version: Darwin 14.5.0' + content := strings.SplitN(line, ":", 2) + if len(content) != 2 { + return "", fmt.Errorf("Kernel Version is invalid") + } + + prettyNames, err := shellwords.Parse(content[1]) + if err != nil { + return "", fmt.Errorf("Kernel Version is invalid: %s", err.Error()) + } + + if len(prettyNames) != 2 { + return "", fmt.Errorf("Kernel Version needs to be 'Darwin x.x.x' ") + } + release = prettyNames[1] + } + } + + return release, nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_unix.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_unix.go new file mode 100644 index 0000000000..744d5e1f83 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_unix.go @@ -0,0 +1,45 @@ +// +build linux freebsd solaris + +// Package kernel provides helper function to get, parse and compare kernel +// versions for different platforms. +package kernel + +import ( + "bytes" + + "github.com/Sirupsen/logrus" +) + +// GetKernelVersion gets the current kernel version. +func GetKernelVersion() (*VersionInfo, error) { + uts, err := uname() + if err != nil { + return nil, err + } + + release := make([]byte, len(uts.Release)) + + i := 0 + for _, c := range uts.Release { + release[i] = byte(c) + i++ + } + + // Remove the \x00 from the release for Atoi to parse correctly + release = release[:bytes.IndexByte(release, 0)] + + return ParseRelease(string(release)) +} + +// CheckKernelVersion checks if current kernel is newer than (or equal to) +// the given version. +func CheckKernelVersion(k, major, minor int) bool { + if v, err := GetKernelVersion(); err != nil { + logrus.Warnf("error getting kernel version: %s", err) + } else { + if CompareKernelVersion(*v, VersionInfo{Kernel: k, Major: major, Minor: minor}) < 0 { + return false + } + } + return true +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go index 85ca250c9f..80fab8ff64 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel_windows.go @@ -1,3 +1,5 @@ +// +build windows + package kernel import ( diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client.go index e3fd326ed5..a778677f7c 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/client.go @@ -20,14 +20,16 @@ const ( ) // NewClient creates a new plugin client (http). -func NewClient(addr string, tlsConfig tlsconfig.Options) (*Client, error) { +func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) { tr := &http.Transport{} - c, err := tlsconfig.Client(tlsConfig) - if err != nil { - return nil, err + if tlsConfig != nil { + c, err := tlsconfig.Client(*tlsConfig) + if err != nil { + return nil, err + } + tr.TLSClientConfig = c } - tr.TLSClientConfig = c u, err := url.Parse(addr) if err != nil { @@ -130,7 +132,7 @@ func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool) return nil, err } retries++ - logrus.Warnf("Unable to connect to plugin: %s:%s, retrying in %v", req.URL.Host, req.URL.Path, timeOff) + logrus.Warnf("Unable to connect to plugin: %s%s: %v, retrying in %v", req.URL.Host, req.URL.Path, err, timeOff) time.Sleep(timeOff) continue } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery.go index 9dc64194f2..e99581c573 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery.go @@ -16,7 +16,6 @@ var ( // ErrNotFound plugin not found ErrNotFound = errors.New("plugin not found") socketsPath = "/run/docker/plugins" - specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"} ) // localRegistry defines a registry that is local (using unix socket). @@ -64,7 +63,7 @@ func (l *localRegistry) Plugin(name string) (*Plugin, error) { for _, p := range socketpaths { if fi, err := os.Stat(p); err == nil && fi.Mode()&os.ModeSocket != 0 { - return newLocalPlugin(name, "unix://"+p), nil + return NewLocalPlugin(name, "unix://"+p), nil } } @@ -101,7 +100,7 @@ func readPluginInfo(name, path string) (*Plugin, error) { return nil, fmt.Errorf("Unknown protocol") } - return newLocalPlugin(name, addr), nil + return NewLocalPlugin(name, addr), nil } func readPluginJSONInfo(name, path string) (*Plugin, error) { @@ -115,8 +114,8 @@ func readPluginJSONInfo(name, path string) (*Plugin, error) { if err := json.NewDecoder(f).Decode(&p); err != nil { return nil, err } - p.Name = name - if len(p.TLSConfig.CAFile) == 0 { + p.name = name + if p.TLSConfig != nil && len(p.TLSConfig.CAFile) == 0 { p.TLSConfig.InsecureSkipVerify = true } p.activateWait = sync.NewCond(&sync.Mutex{}) diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_unix.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_unix.go new file mode 100644 index 0000000000..693a47e394 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_unix.go @@ -0,0 +1,5 @@ +// +build !windows + +package plugins + +var specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_windows.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_windows.go new file mode 100644 index 0000000000..d7c1fe4942 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/discovery_windows.go @@ -0,0 +1,8 @@ +package plugins + +import ( + "os" + "path/filepath" +) + +var specsPaths = []string{filepath.Join(os.Getenv("programdata"), "docker", "plugins")} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/plugins.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/plugins.go index b83b5ae61d..debcd087c9 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/plugins.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/plugins/plugins.go @@ -55,13 +55,13 @@ type Manifest struct { // Plugin is the definition of a docker plugin. type Plugin struct { // Name of the plugin - Name string `json:"-"` + name string // Address of the plugin Addr string // TLS configuration of the plugin - TLSConfig tlsconfig.Options + TLSConfig *tlsconfig.Options // Client attached to the plugin - Client *Client `json:"-"` + client *Client // Manifest of the plugin (see above) Manifest *Manifest `json:"-"` @@ -73,11 +73,28 @@ type Plugin struct { activateWait *sync.Cond } -func newLocalPlugin(name, addr string) *Plugin { +// Name returns the name of the plugin. +func (p *Plugin) Name() string { + return p.name +} + +// Client returns a ready-to-use plugin client that can be used to communicate with the plugin. +func (p *Plugin) Client() *Client { + return p.client +} + +// IsLegacy returns true for legacy plugins and false otherwise. +func (p *Plugin) IsLegacy() bool { + return true +} + +// NewLocalPlugin creates a new local plugin. +func NewLocalPlugin(name, addr string) *Plugin { return &Plugin{ - Name: name, - Addr: addr, - TLSConfig: tlsconfig.Options{InsecureSkipVerify: true}, + name: name, + Addr: addr, + // TODO: change to nil + TLSConfig: &tlsconfig.Options{InsecureSkipVerify: true}, activateWait: sync.NewCond(&sync.Mutex{}), } } @@ -102,10 +119,10 @@ func (p *Plugin) activateWithLock() error { if err != nil { return err } - p.Client = c + p.client = c m := new(Manifest) - if err = p.Client.Call("Plugin.Activate", nil, m); err != nil { + if err = p.client.Call("Plugin.Activate", nil, m); err != nil { return err } @@ -116,7 +133,7 @@ func (p *Plugin) activateWithLock() error { if !handled { continue } - handler(p.Name, p.Client) + handler(p.name, p.client) } return nil } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_linux.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_linux.go index 3c3a73a9d5..34ae2a9dcd 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_linux.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_linux.go @@ -13,7 +13,7 @@ func Self() string { return "/proc/self/exe" } -// Command returns *exec.Cmd which have Path as current binary. Also it setting +// Command returns *exec.Cmd which has Path as current binary. Also it setting // SysProcAttr.Pdeathsig to SIGTERM. // This will use the in-memory version (/proc/self/exe) of the current binary, // it is thus safe to delete or replace the on-disk binary (os.Args[0]). diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unix.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unix.go index b70edcb316..778a720e3b 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unix.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unix.go @@ -1,4 +1,4 @@ -// +build freebsd solaris +// +build freebsd solaris darwin package reexec @@ -12,7 +12,7 @@ func Self() string { return naiveSelf() } -// Command returns *exec.Cmd which have Path as current binary. +// Command returns *exec.Cmd which has Path as current binary. // For example if current binary is "docker" at "/usr/bin/", then cmd.Path will // be set to "/usr/bin/docker". func Command(args ...string) *exec.Cmd { diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go index 9aed004e86..76edd82427 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!windows,!freebsd,!solaris +// +build !linux,!windows,!freebsd,!solaris,!darwin package reexec @@ -6,7 +6,7 @@ import ( "os/exec" ) -// Command is unsupported on operating systems apart from Linux and Windows. +// Command is unsupported on operating systems apart from Linux, Windows, Solaris and Darwin. func Command(args ...string) *exec.Cmd { return nil } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_windows.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_windows.go index 8d65e0ae1a..ca871c4227 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_windows.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/reexec/command_windows.go @@ -12,7 +12,7 @@ func Self() string { return naiveSelf() } -// Command returns *exec.Cmd which have Path as current binary. +// Command returns *exec.Cmd which has Path as current binary. // For example if current binary is "docker.exe" at "C:\", then cmd.Path will // be set to "C:\docker.exe". func Command(args ...string) *exec.Cmd { diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/signal/trap.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/signal/trap.go index 2cf5ccf0d2..bd8675c9aa 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/signal/trap.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/signal/trap.go @@ -3,9 +3,11 @@ package signal import ( "os" gosignal "os/signal" + "path/filepath" "runtime" "sync/atomic" "syscall" + "time" "github.com/Sirupsen/logrus" ) @@ -18,15 +20,22 @@ import ( // * If SIGINT or SIGTERM are received 3 times before cleanup is complete, then cleanup is // skipped and the process is terminated immediately (allows force quit of stuck daemon) // * A SIGQUIT always causes an exit without cleanup, with a goroutine dump preceding exit. +// * Ignore SIGPIPE events. These are generated by systemd when journald is restarted while +// the docker daemon is not restarted and also running under systemd. +// Fixes https://github.com/docker/docker/issues/19728 // func Trap(cleanup func()) { c := make(chan os.Signal, 1) - // we will handle INT, TERM, QUIT here - signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT} + // we will handle INT, TERM, QUIT, SIGPIPE here + signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE} gosignal.Notify(c, signals...) go func() { interruptCount := uint32(0) for sig := range c { + if sig == syscall.SIGPIPE { + continue + } + go func(sig os.Signal) { logrus.Infof("Processing signal '%v'", sig) switch sig { @@ -42,11 +51,11 @@ func Trap(cleanup func()) { } } else { // 3 SIGTERM/INT signals received; force exit without cleanup - logrus.Infof("Forcing docker daemon shutdown without cleanup; 3 interrupts received") + logrus.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received") } case syscall.SIGQUIT: - DumpStacks() - logrus.Infof("Forcing docker daemon shutdown without cleanup on SIGQUIT") + DumpStacks("") + logrus.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT") } //for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal # os.Exit(128 + int(sig.(syscall.Signal))) @@ -56,7 +65,7 @@ func Trap(cleanup func()) { } // DumpStacks dumps the runtime stack. -func DumpStacks() { +func DumpStacks(root string) { var ( buf []byte stackSize int @@ -70,5 +79,30 @@ func DumpStacks() { buf = buf[:stackSize] // Note that if the daemon is started with a less-verbose log-level than "info" (the default), the goroutine // traces won't show up in the log. - logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) + if root == "" { + logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) + } else { + // Dumps the stacks to a file in the root directory of the daemon + // On Windows, this overcomes two issues - one being that if the stack is too big, it doesn't + // get written to the event log when the Windows daemon is running as a service. + // Second, using logrus, the tabs and new-lines end up getting written as literal + // \t and \n's, meaning you need to use something like notepad++ to convert the + // output into something readable using 'type' from a command line or notepad/notepad++ etc. + path := filepath.Join(root, "goroutine-stacks.log") + f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666) + if err != nil { + logrus.Warnf("Could not open %s to write the goroutine stacks: %v", path, err) + return + } + defer f.Close() + f.WriteString("=== BEGIN goroutine stack dump ===\n") + f.WriteString(time.Now().String() + "\n") + if _, err := f.Write(buf); err != nil { + logrus.Warnf("Could not write goroutine stacks to %s: %v", path, err) + return + } + f.WriteString("=== END goroutine stack dump ===\n") + f.Sync() + logrus.Infof("goroutine stacks written to %s", path) + } } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid.go index 161184ff8a..fa35d8bad5 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/stringid/stringid.go @@ -29,11 +29,10 @@ func TruncateID(id string) string { if i := strings.IndexRune(id, ':'); i >= 0 { id = id[i+1:] } - trimTo := shortLen - if len(id) < shortLen { - trimTo = len(id) + if len(id) > shortLen { + id = id[:shortLen] } - return id[:trimTo] + return id } func generateID(crypto bool) string { @@ -60,7 +59,6 @@ func generateID(crypto bool) string { // GenerateRandomID returns a unique id. func GenerateRandomID() string { return generateID(true) - } // GenerateNonCryptoID generates unique id without using cryptographically diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_darwin.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_darwin.go new file mode 100644 index 0000000000..f0742f59e5 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_darwin.go @@ -0,0 +1,32 @@ +package system + +import ( + "syscall" +) + +// fromStatT creates a system.StatT type from a syscall.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtimespec}, nil +} + +// FromStatT loads a system.StatT from a syscall.Stat_t. +func FromStatT(s *syscall.Stat_t) (*StatT, error) { + return fromStatT(s) +} + +// Stat takes a path to a file and returns +// a system.StatT type pertaining to that file. +// +// Throws an error if the file does not exist +func Stat(path string) (*StatT, error) { + s := &syscall.Stat_t{} + if err := syscall.Stat(path, s); err != nil { + return nil, err + } + return fromStatT(s) +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_unsupported.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_unsupported.go index f53e9de4d1..5d85f523cf 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_unsupported.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/stat_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!windows,!freebsd,!solaris,!openbsd +// +build !linux,!windows,!freebsd,!solaris,!openbsd,!darwin package system diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_darwin.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_darwin.go deleted file mode 100644 index 0a16197544..0000000000 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_darwin.go +++ /dev/null @@ -1,8 +0,0 @@ -package system - -import "syscall" - -// LUtimesNano is not supported by darwin platform. -func LUtimesNano(path string, ts []syscall.Timespec) error { - return ErrNotSupportedPlatform -} diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_unsupported.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_unsupported.go index 50c3a04364..139714544d 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_unsupported.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/system/utimes_unsupported.go @@ -1,10 +1,10 @@ -// +build !linux,!freebsd,!darwin +// +build !linux,!freebsd package system import "syscall" -// LUtimesNano is not supported on platforms other than linux, freebsd and darwin. +// LUtimesNano is only supported on linux and freebsd. func LUtimesNano(path string, ts []syscall.Timespec) error { return ErrNotSupportedPlatform } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term.go index 8f554847f0..fe59faa949 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term.go @@ -1,11 +1,12 @@ // +build !windows -// Package term provides provides structures and helper functions to work with +// Package term provides structures and helper functions to work with // terminal (state, sizes). package term import ( "errors" + "fmt" "io" "os" "os/signal" @@ -88,7 +89,8 @@ func DisableEcho(fd uintptr, state *State) error { } // SetRawTerminal puts the terminal connected to the given file descriptor into -// raw mode and returns the previous state. +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. func SetRawTerminal(fd uintptr) (*State, error) { oldState, err := MakeRaw(fd) if err != nil { @@ -98,12 +100,24 @@ func SetRawTerminal(fd uintptr) (*State, error) { return oldState, err } +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + return nil, nil +} + func handleInterrupt(fd uintptr, state *State) { sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, os.Interrupt) - go func() { - _ = <-sigchan - RestoreTerminal(fd, state) + for range sigchan { + // quit cleanly and the new terminal item is on a new line + fmt.Println() + signal.Stop(sigchan) + close(sigchan) + RestoreTerminal(fd, state) + os.Exit(1) + } }() } diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term_windows.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term_windows.go index 9bc52a8c65..dc50da4577 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term_windows.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/term_windows.go @@ -9,14 +9,12 @@ import ( "syscall" "github.com/Azure/go-ansiterm/winterm" - "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/term/windows" ) // State holds the console mode for the terminal. type State struct { - inMode, outMode uint32 - inHandle, outHandle syscall.Handle + mode uint32 } // Winsize is used for window size. @@ -32,143 +30,72 @@ const ( disableNewlineAutoReturn = 0x0008 ) -// usingNativeConsole is true if we are using the Windows native console -var usingNativeConsole bool +// vtInputSupported is true if enableVirtualTerminalInput is supported by the console +var vtInputSupported bool // StdStreams returns the standard streams (stdin, stdout, stedrr). func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { - switch { - case os.Getenv("ConEmuANSI") == "ON": + // Turn on VT handling on all std handles, if possible. This might + // fail, in which case we will fall back to terminal emulation. + var emulateStdin, emulateStdout, emulateStderr bool + fd := os.Stdin.Fd() + if mode, err := winterm.GetConsoleMode(fd); err == nil { + // Validate that enableVirtualTerminalInput is supported, but do not set it. + if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalInput); err != nil { + emulateStdin = true + } else { + vtInputSupported = true + } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + winterm.SetConsoleMode(fd, mode) + } + + fd = os.Stdout.Fd() + if mode, err := winterm.GetConsoleMode(fd); err == nil { + // Validate disableNewlineAutoReturn is supported, but do not set it. + if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { + emulateStdout = true + } else { + winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) + } + } + + fd = os.Stderr.Fd() + if mode, err := winterm.GetConsoleMode(fd); err == nil { + // Validate disableNewlineAutoReturn is supported, but do not set it. + if err = winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing|disableNewlineAutoReturn); err != nil { + emulateStderr = true + } else { + winterm.SetConsoleMode(fd, mode|enableVirtualTerminalProcessing) + } + } + + if os.Getenv("ConEmuANSI") == "ON" { // The ConEmu terminal emulates ANSI on output streams well. - return windows.ConEmuStreams() - case os.Getenv("MSYSTEM") != "": - // MSYS (mingw) does not emulate ANSI well. - return windows.ConsoleStreams() - default: - if useNativeConsole() { - usingNativeConsole = true - return os.Stdin, os.Stdout, os.Stderr - } - return windows.ConsoleStreams() - } -} - -// useNativeConsole determines if the docker client should use the built-in -// console which supports ANSI emulation, or fall-back to the golang emulator -// (github.com/azure/go-ansiterm). -func useNativeConsole() bool { - osv := system.GetOSVersion() - - // Native console is not available before major version 10 - if osv.MajorVersion < 10 { - return false + emulateStdout = false + emulateStderr = false } - // Get the console modes. If this fails, we can't use the native console - state, err := getNativeConsole() - if err != nil { - return false + if emulateStdin { + stdIn = windows.NewAnsiReader(syscall.STD_INPUT_HANDLE) + } else { + stdIn = os.Stdin } - // Probe the console to see if it can be enabled. - if nil != probeNativeConsole(state) { - return false + if emulateStdout { + stdOut = windows.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE) + } else { + stdOut = os.Stdout } - // Environment variable override - if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" { - if e == "1" { - return true - } - return false + if emulateStderr { + stdErr = windows.NewAnsiWriter(syscall.STD_ERROR_HANDLE) + } else { + stdErr = os.Stderr } - // Must have a post-TP5 RS1 build of Windows Server 2016/Windows 10 for - // the native console to be usable. - if osv.Build < 14350 { - return false - } - - return true -} - -// getNativeConsole returns the console modes ('state') for the native Windows console -func getNativeConsole() (State, error) { - var ( - err error - state State - ) - - // Get the handle to stdout - if state.outHandle, err = syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE); err != nil { - return state, err - } - - // Get the console mode from the consoles stdout handle - if err = syscall.GetConsoleMode(state.outHandle, &state.outMode); err != nil { - return state, err - } - - // Get the handle to stdin - if state.inHandle, err = syscall.GetStdHandle(syscall.STD_INPUT_HANDLE); err != nil { - return state, err - } - - // Get the console mode from the consoles stdin handle - if err = syscall.GetConsoleMode(state.inHandle, &state.inMode); err != nil { - return state, err - } - - return state, nil -} - -// probeNativeConsole probes the console to determine if native can be supported, -func probeNativeConsole(state State) error { - if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil { - return err - } - defer winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) - - if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil { - return err - } - defer winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode) - - return nil -} - -// enableNativeConsole turns on native console mode -func enableNativeConsole(state State) error { - // First attempt both enableVirtualTerminalProcessing and disableNewlineAutoReturn - if err := winterm.SetConsoleMode(uintptr(state.outHandle), - state.outMode|(enableVirtualTerminalProcessing|disableNewlineAutoReturn)); err != nil { - - // That may fail, so fallback to trying just enableVirtualTerminalProcessing - if err := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode|enableVirtualTerminalProcessing); err != nil { - return err - } - } - - if err := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode|enableVirtualTerminalInput); err != nil { - winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) // restore out if we can - return err - } - - return nil -} - -// disableNativeConsole turns off native console mode -func disableNativeConsole(state *State) error { - // Try and restore both in an out before error checking. - errout := winterm.SetConsoleMode(uintptr(state.outHandle), state.outMode) - errin := winterm.SetConsoleMode(uintptr(state.inHandle), state.inMode) - if errout != nil { - return errout - } - if errin != nil { - return errin - } - return nil + return } // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. @@ -199,34 +126,23 @@ func IsTerminal(fd uintptr) bool { // RestoreTerminal restores the terminal connected to the given file descriptor // to a previous state. func RestoreTerminal(fd uintptr, state *State) error { - if usingNativeConsole { - return disableNativeConsole(state) - } - return winterm.SetConsoleMode(fd, state.outMode) + return winterm.SetConsoleMode(fd, state.mode) } // SaveState saves the state of the terminal connected to the given file descriptor. func SaveState(fd uintptr) (*State, error) { - if usingNativeConsole { - state, err := getNativeConsole() - if err != nil { - return nil, err - } - return &state, nil - } - mode, e := winterm.GetConsoleMode(fd) if e != nil { return nil, e } - return &State{outMode: mode}, nil + return &State{mode: mode}, nil } // DisableEcho disables echo for the terminal connected to the given file descriptor. // -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx func DisableEcho(fd uintptr, state *State) error { - mode := state.inMode + mode := state.mode mode &^= winterm.ENABLE_ECHO_INPUT mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT err := winterm.SetConsoleMode(fd, mode) @@ -239,8 +155,9 @@ func DisableEcho(fd uintptr, state *State) error { return nil } -// SetRawTerminal puts the terminal connected to the given file descriptor into raw -// mode and returns the previous state. +// SetRawTerminal puts the terminal connected to the given file descriptor into +// raw mode and returns the previous state. On UNIX, this puts both the input +// and output into raw mode. On Windows, it only puts the input into raw mode. func SetRawTerminal(fd uintptr) (*State, error) { state, err := MakeRaw(fd) if err != nil { @@ -252,6 +169,21 @@ func SetRawTerminal(fd uintptr) (*State, error) { return state, err } +// SetRawTerminalOutput puts the output of terminal connected to the given file +// descriptor into raw mode. On UNIX, this does nothing and returns nil for the +// state. On Windows, it disables LF -> CRLF translation. +func SetRawTerminalOutput(fd uintptr) (*State, error) { + state, err := SaveState(fd) + if err != nil { + return nil, err + } + + // Ignore failures, since disableNewlineAutoReturn might not be supported on this + // version of Windows. + winterm.SetConsoleMode(fd, state.mode|disableNewlineAutoReturn) + return state, err +} + // MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw // mode and returns the previous state of the terminal so that it can be restored. func MakeRaw(fd uintptr) (*State, error) { @@ -260,13 +192,7 @@ func MakeRaw(fd uintptr) (*State, error) { return nil, err } - mode := state.inMode - if usingNativeConsole { - if err := enableNativeConsole(*state); err != nil { - return nil, err - } - mode |= enableVirtualTerminalInput - } + mode := state.mode // See // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx @@ -283,6 +209,9 @@ func MakeRaw(fd uintptr) (*State, error) { mode |= winterm.ENABLE_EXTENDED_FLAGS mode |= winterm.ENABLE_INSERT_MODE mode |= winterm.ENABLE_QUICK_EDIT_MODE + if vtInputSupported { + mode |= enableVirtualTerminalInput + } err = winterm.SetConsoleMode(fd, mode) if err != nil { diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_reader.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_reader.go index 5b91b78342..58452ad786 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_reader.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_reader.go @@ -6,6 +6,7 @@ import ( "bytes" "errors" "fmt" + "io" "os" "strings" "unsafe" @@ -27,7 +28,10 @@ type ansiReader struct { command []byte } -func newAnsiReader(nFile int) *ansiReader { +// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a +// Windows console input handle. +func NewAnsiReader(nFile int) io.ReadCloser { + initLogger() file, fd := winterm.GetStdFile(nFile) return &ansiReader{ file: file, diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_writer.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_writer.go index 9f3232c093..a3ce5697d9 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_writer.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/ansi_writer.go @@ -3,16 +3,13 @@ package windows import ( - "io/ioutil" + "io" "os" ansiterm "github.com/Azure/go-ansiterm" "github.com/Azure/go-ansiterm/winterm" - "github.com/Sirupsen/logrus" ) -var logger *logrus.Logger - // ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation. type ansiWriter struct { file *os.File @@ -24,19 +21,10 @@ type ansiWriter struct { parser *ansiterm.AnsiParser } -func newAnsiWriter(nFile int) *ansiWriter { - logFile := ioutil.Discard - - if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { - logFile, _ = os.Create("ansiReaderWriter.log") - } - - logger = &logrus.Logger{ - Out: logFile, - Formatter: new(logrus.TextFormatter), - Level: logrus.DebugLevel, - } - +// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a +// Windows console output handle. +func NewAnsiWriter(nFile int) io.Writer { + initLogger() file, fd := winterm.GetStdFile(nFile) info, err := winterm.GetConsoleScreenBufferInfo(fd) if err != nil { diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/console.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/console.go index 3036a04605..ca5c3b2e53 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/console.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/console.go @@ -3,73 +3,11 @@ package windows import ( - "io" "os" - "syscall" "github.com/Azure/go-ansiterm/winterm" - - ansiterm "github.com/Azure/go-ansiterm" - "github.com/Sirupsen/logrus" - "io/ioutil" ) -// ConEmuStreams returns prepared versions of console streams, -// for proper use in ConEmu terminal. -// The ConEmu terminal emulates ANSI on output streams well by default. -func ConEmuStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { - if IsConsole(os.Stdin.Fd()) { - stdIn = newAnsiReader(syscall.STD_INPUT_HANDLE) - } else { - stdIn = os.Stdin - } - - stdOut = os.Stdout - stdErr = os.Stderr - - // WARNING (BEGIN): sourced from newAnsiWriter - - logFile := ioutil.Discard - - if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { - logFile, _ = os.Create("ansiReaderWriter.log") - } - - logger = &logrus.Logger{ - Out: logFile, - Formatter: new(logrus.TextFormatter), - Level: logrus.DebugLevel, - } - - // WARNING (END): sourced from newAnsiWriter - - return stdIn, stdOut, stdErr -} - -// ConsoleStreams returns a wrapped version for each standard stream referencing a console, -// that handles ANSI character sequences. -func ConsoleStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { - if IsConsole(os.Stdin.Fd()) { - stdIn = newAnsiReader(syscall.STD_INPUT_HANDLE) - } else { - stdIn = os.Stdin - } - - if IsConsole(os.Stdout.Fd()) { - stdOut = newAnsiWriter(syscall.STD_OUTPUT_HANDLE) - } else { - stdOut = os.Stdout - } - - if IsConsole(os.Stderr.Fd()) { - stdErr = newAnsiWriter(syscall.STD_ERROR_HANDLE) - } else { - stdErr = os.Stderr - } - - return stdIn, stdOut, stdErr -} - // GetHandleInfo returns file descriptor and bool indicating whether the file is a console. func GetHandleInfo(in interface{}) (uintptr, bool) { switch t := in.(type) { diff --git a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/windows.go b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/windows.go index bf4c7b5025..ce4cb5990e 100644 --- a/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/windows.go +++ b/libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/term/windows/windows.go @@ -3,3 +3,31 @@ // and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls. package windows + +import ( + "io/ioutil" + "os" + "sync" + + ansiterm "github.com/Azure/go-ansiterm" + "github.com/Sirupsen/logrus" +) + +var logger *logrus.Logger +var initOnce sync.Once + +func initLogger() { + initOnce.Do(func() { + logFile := ioutil.Discard + + if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { + logFile, _ = os.Create("ansiReaderWriter.log") + } + + logger = &logrus.Logger{ + Out: logFile, + Formatter: new(logrus.TextFormatter), + Level: logrus.DebugLevel, + } + }) +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/.travis.yml b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/.travis.yml new file mode 100644 index 0000000000..16d1430aa2 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken 2FMhp57u8LcstKL9B190fLTcEnBtAAiEL diff --git a/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/README.md b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/README.md new file mode 100644 index 0000000000..56f357fad7 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/README.md @@ -0,0 +1,47 @@ +# go-shellwords + +[![Coverage Status](https://coveralls.io/repos/mattn/go-shellwords/badge.png?branch=master)](https://coveralls.io/r/mattn/go-shellwords?branch=master) +[![Build Status](https://travis-ci.org/mattn/go-shellwords.svg?branch=master)](https://travis-ci.org/mattn/go-shellwords) + +Parse line as shell words. + +## Usage + +```go +args, err := shellwords.Parse("./foo --bar=baz") +// args should be ["./foo", "--bar=baz"] +``` + +```go +os.Setenv("FOO", "bar") +p := shellwords.NewParser() +p.ParseEnv = true +args, err := p.Parse("./foo $FOO") +// args should be ["./foo", "bar"] +``` + +```go +p := shellwords.NewParser() +p.ParseBacktick = true +args, err := p.Parse("./foo `echo $SHELL`") +// args should be ["./foo", "/bin/bash"] +``` + +```go +shellwords.ParseBacktick = true +p := shellwords.NewParser() +args, err := p.Parse("./foo `echo $SHELL`") +// args should be ["./foo", "/bin/bash"] +``` + +# Thanks + +This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine). + +# License + +under the MIT License: http://mattn.mit-license.org/2014 + +# Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/shellwords.go b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/shellwords.go new file mode 100644 index 0000000000..1abaa6c9df --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/shellwords.go @@ -0,0 +1,134 @@ +package shellwords + +import ( + "errors" + "os" + "regexp" + "strings" +) + +var ( + ParseEnv bool = false + ParseBacktick bool = false +) + +var envRe = regexp.MustCompile(`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`) + +func isSpace(r rune) bool { + switch r { + case ' ', '\t', '\r', '\n': + return true + } + return false +} + +func replaceEnv(s string) string { + return envRe.ReplaceAllStringFunc(s, func(s string) string { + s = s[1:] + if s[0] == '{' { + s = s[1 : len(s)-1] + } + return os.Getenv(s) + }) +} + +type Parser struct { + ParseEnv bool + ParseBacktick bool +} + +func NewParser() *Parser { + return &Parser{ParseEnv, ParseBacktick} +} + +func (p *Parser) Parse(line string) ([]string, error) { + line = strings.TrimSpace(line) + + args := []string{} + buf := "" + var escaped, doubleQuoted, singleQuoted, backQuote bool + backtick := "" + + for _, r := range line { + if escaped { + buf += string(r) + escaped = false + continue + } + + if r == '\\' { + if singleQuoted { + buf += string(r) + } else { + escaped = true + } + continue + } + + if isSpace(r) { + if singleQuoted || doubleQuoted || backQuote { + buf += string(r) + backtick += string(r) + } else if buf != "" { + if p.ParseEnv { + buf = replaceEnv(buf) + } + args = append(args, buf) + buf = "" + } + continue + } + + switch r { + case '`': + if !singleQuoted && !doubleQuoted { + if p.ParseBacktick { + if backQuote { + out, err := shellRun(backtick) + if err != nil { + return nil, err + } + buf = out + } + backtick = "" + backQuote = !backQuote + continue + } + backtick = "" + backQuote = !backQuote + } + case '"': + if !singleQuoted { + doubleQuoted = !doubleQuoted + continue + } + case '\'': + if !doubleQuoted { + singleQuoted = !singleQuoted + continue + } + } + + buf += string(r) + if backQuote { + backtick += string(r) + } + } + + if buf != "" { + if p.ParseEnv { + buf = replaceEnv(buf) + } + args = append(args, buf) + } + + if escaped || singleQuoted || doubleQuoted || backQuote { + return nil, errors.New("invalid command line string") + } + + return args, nil +} + +func Parse(line string) ([]string, error) { + return NewParser().Parse(line) +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/util_posix.go b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/util_posix.go new file mode 100644 index 0000000000..4f8ac55e47 --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/util_posix.go @@ -0,0 +1,19 @@ +// +build !windows + +package shellwords + +import ( + "errors" + "os" + "os/exec" + "strings" +) + +func shellRun(line string) (string, error) { + shell := os.Getenv("SHELL") + b, err := exec.Command(shell, "-c", line).Output() + if err != nil { + return "", errors.New(err.Error() + ":" + string(b)) + } + return strings.TrimSpace(string(b)), nil +} diff --git a/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/util_windows.go b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/util_windows.go new file mode 100644 index 0000000000..7cad4cf06f --- /dev/null +++ b/libnetwork/Godeps/_workspace/src/github.com/mattn/go-shellwords/util_windows.go @@ -0,0 +1,17 @@ +package shellwords + +import ( + "errors" + "os" + "os/exec" + "strings" +) + +func shellRun(line string) (string, error) { + shell := os.Getenv("COMSPEC") + b, err := exec.Command(shell, "/c", line).Output() + if err != nil { + return "", errors.New(err.Error() + ":" + string(b)) + } + return strings.TrimSpace(string(b)), nil +}