diff --git a/go.mod b/go.mod index 6b9a2c3f..7f6d8d6e 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,14 @@ go 1.17 require ( github.com/aws/aws-sdk-go-v2/config v1.15.5 - github.com/compose-spec/compose-go v1.5.0 + github.com/compose-spec/compose-go v1.6.0 github.com/containerd/console v1.0.3 github.com/containerd/containerd v1.6.6 github.com/docker/cli v20.10.17+incompatible // v22.06.x - see "replace" for the actual version github.com/docker/cli-docs-tool v0.5.0 github.com/docker/distribution v2.8.1+incompatible github.com/docker/docker v20.10.17+incompatible // v22.06.x - see "replace" for the actual version - github.com/docker/go-units v0.4.0 + github.com/docker/go-units v0.5.0 github.com/gofrs/flock v0.7.3 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 diff --git a/go.sum b/go.sum index 919bfa06..8cc3ffa6 100644 --- a/go.sum +++ b/go.sum @@ -143,8 +143,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/compose-spec/compose-go v1.5.0 h1:yOmYpIm13pYt2o+oKVe/JAD6o2Tv+eUyOcRhf0qF4fA= -github.com/compose-spec/compose-go v1.5.0/go.mod h1:l7RUULbFFLzlQHuxtJr7SVLyWdqEpbJEGTWCgcu6Eqw= +github.com/compose-spec/compose-go v1.6.0 h1:7Ol/UULMUtbPmB0EYrETASRoum821JpOh/XaEf+hN+Q= +github.com/compose-spec/compose-go v1.6.0/go.mod h1:os+Ulh2jlZxY1XT1hbciERadjSUU/BtZ6+gcN7vD7J0= github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= @@ -196,8 +196,9 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4 h1:k8TfKGeAcDQFFQOGCQMRN04N4a9YrPlRMMKnzAuvM9Q= github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= diff --git a/vendor/github.com/compose-spec/compose-go/dotenv/godotenv.go b/vendor/github.com/compose-spec/compose-go/dotenv/godotenv.go index 543df9b1..c1c12eaf 100644 --- a/vendor/github.com/compose-spec/compose-go/dotenv/godotenv.go +++ b/vendor/github.com/compose-spec/compose-go/dotenv/godotenv.go @@ -4,17 +4,17 @@ // // The TL;DR is that you make a .env file that looks something like // -// SOME_ENV_VAR=somevalue +// SOME_ENV_VAR=somevalue // // and then in your go code you can call // -// godotenv.Load() +// godotenv.Load() // // and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR") package dotenv import ( - "errors" + "bytes" "fmt" "io" "os" @@ -27,7 +27,9 @@ import ( "github.com/compose-spec/compose-go/template" ) -const doubleQuoteSpecialChars = "\\\n\r\"!$`" +var utf8BOM = []byte("\uFEFF") + +var startsWithDigitRegex = regexp.MustCompile(`^\s*\d.*`) // Keys starting with numbers are ignored // LookupFn represents a lookup function to resolve variables from type LookupFn func(string) (string, bool) @@ -48,18 +50,22 @@ func ParseWithLookup(r io.Reader, lookupFn LookupFn) (map[string]string, error) return nil, err } + // seek past the UTF-8 BOM if it exists (particularly on Windows, some + // editors tend to add it, and it'll cause parsing to fail) + data = bytes.TrimPrefix(data, utf8BOM) + return UnmarshalBytesWithLookup(data, lookupFn) } // Load will read your env file(s) and load them into ENV for this process. // -// Call this function as close as possible to the start of your program (ideally in main) +// Call this function as close as possible to the start of your program (ideally in main). // -// If you call Load without any args it will default to loading .env in the current path +// If you call Load without any args it will default to loading .env in the current path. // -// You can otherwise tell it which files to load (there can be more than one) like +// You can otherwise tell it which files to load (there can be more than one) like: // -// godotenv.Load("fileone", "filetwo") +// godotenv.Load("fileone", "filetwo") // // It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults func Load(filenames ...string) error { @@ -68,13 +74,13 @@ func Load(filenames ...string) error { // Overload will read your env file(s) and load them into ENV for this process. // -// Call this function as close as possible to the start of your program (ideally in main) +// Call this function as close as possible to the start of your program (ideally in main). // -// If you call Overload without any args it will default to loading .env in the current path +// If you call Overload without any args it will default to loading .env in the current path. // -// You can otherwise tell it which files to load (there can be more than one) like +// You can otherwise tell it which files to load (there can be more than one) like: // -// godotenv.Overload("fileone", "filetwo") +// godotenv.Overload("fileone", "filetwo") // // It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefilly set all vars. func Overload(filenames ...string) error { @@ -92,8 +98,6 @@ func load(overload bool, filenames ...string) error { return nil } -var startsWithDigitRegex = regexp.MustCompile(`^\s*\d.*`) // Keys starting with numbers are ignored - // ReadWithLookup gets all env vars from the files and/or lookup function and return values as // a map rather than automatically writing values into env func ReadWithLookup(lookupFn LookupFn, filenames ...string) (map[string]string, error) { @@ -148,6 +152,8 @@ func UnmarshalBytesWithLookup(src []byte, lookupFn LookupFn) (map[string]string, // // If you want more fine grained control over your command it's recommended // that you use `Load()` or `Read()` and the `os/exec` package yourself. +// +// Deprecated: Use the `os/exec` package directly. func Exec(filenames []string, cmd string, cmdArgs []string) error { if err := Load(filenames...); err != nil { return err @@ -161,7 +167,10 @@ func Exec(filenames []string, cmd string, cmdArgs []string) error { } // Write serializes the given environment and writes it to a file +// +// Deprecated: The serialization functions are untested and unmaintained. func Write(envMap map[string]string, filename string) error { + //goland:noinspection GoDeprecation content, err := Marshal(envMap) if err != nil { return err @@ -180,6 +189,8 @@ func Write(envMap map[string]string, filename string) error { // Marshal outputs the given environment as a dotenv-formatted environment file. // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. +// +// Deprecated: The serialization functions are untested and unmaintained. func Marshal(envMap map[string]string) (string, error) { lines := make([]string, 0, len(envMap)) for k, v := range envMap { @@ -232,104 +243,6 @@ func readFile(filename string, lookupFn LookupFn) (map[string]string, error) { return ParseWithLookup(file, lookupFn) } -var exportRegex = regexp.MustCompile(`^\s*(?:export\s+)?(.*?)\s*$`) - -func parseLine(line string, envMap map[string]string) (string, string, error) { - return parseLineWithLookup(line, envMap, nil) -} -func parseLineWithLookup(line string, envMap map[string]string, lookupFn LookupFn) (string, string, error) { - if line == "" { - return "", "", errors.New("zero length string") - } - - // ditch the comments (but keep quoted hashes) - if strings.HasPrefix(strings.TrimSpace(line), "#") || strings.Contains(line, " #") { - segmentsBetweenHashes := strings.Split(line, "#") - quotesAreOpen := false - var segmentsToKeep []string - for _, segment := range segmentsBetweenHashes { - if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 { - if quotesAreOpen { - segmentsToKeep = append(segmentsToKeep, segment) - } - quotesAreOpen = !quotesAreOpen - } - - if len(segmentsToKeep) == 0 || quotesAreOpen { - segmentsToKeep = append(segmentsToKeep, segment) - } - } - - line = strings.Join(segmentsToKeep, "#") - } - - firstEquals := strings.Index(line, "=") - firstColon := strings.Index(line, ":") - splitString := strings.SplitN(line, "=", 2) - if firstColon != -1 && (firstColon < firstEquals || firstEquals == -1) { - // This is a yaml-style line - splitString = strings.SplitN(line, ":", 2) - } - - if len(splitString) != 2 { - return "", "", errors.New("can't separate key from value") - } - key := exportRegex.ReplaceAllString(splitString[0], "$1") - - // Parse the value - value := parseValue(splitString[1], envMap, lookupFn) - - return key, value, nil -} - -var ( - singleQuotesRegex = regexp.MustCompile(`\A'(.*)'\z`) - doubleQuotesRegex = regexp.MustCompile(`\A"(.*)"\z`) - escapeRegex = regexp.MustCompile(`\\.`) - unescapeCharsRegex = regexp.MustCompile(`\\([^$])`) -) - -func parseValue(value string, envMap map[string]string, lookupFn LookupFn) string { - - // trim - value = strings.Trim(value, " ") - - // check if we've got quoted values or possible escapes - if len(value) > 1 { - singleQuotes := singleQuotesRegex.FindStringSubmatch(value) - - doubleQuotes := doubleQuotesRegex.FindStringSubmatch(value) - - if singleQuotes != nil || doubleQuotes != nil { - // pull the quotes off the edges - value = value[1 : len(value)-1] - } - - if doubleQuotes != nil { - // expand newlines - value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string { - c := strings.TrimPrefix(match, `\`) - switch c { - case "n": - return "\n" - case "r": - return "\r" - default: - return match - } - }) - // unescape characters - value = unescapeCharsRegex.ReplaceAllString(value, "$1") - } - - if singleQuotes == nil { - value, _ = expandVariables(value, envMap, lookupFn) - } - } - - return value -} - func expandVariables(value string, envMap map[string]string, lookupFn LookupFn) (string, error) { retVal, err := template.Substitute(value, func(k string) (string, bool) { if v, ok := envMap[k]; ok { @@ -343,7 +256,9 @@ func expandVariables(value string, envMap map[string]string, lookupFn LookupFn) return retVal, nil } +// Deprecated: only used by unsupported/untested code for Marshal/Write. func doubleQuoteEscape(line string) string { + const doubleQuoteSpecialChars = "\\\n\r\"!$`" for _, c := range doubleQuoteSpecialChars { toReplace := "\\" + string(c) if c == '\n' { diff --git a/vendor/github.com/compose-spec/compose-go/dotenv/parser.go b/vendor/github.com/compose-spec/compose-go/dotenv/parser.go index a0c862b8..9a8d0f60 100644 --- a/vendor/github.com/compose-spec/compose-go/dotenv/parser.go +++ b/vendor/github.com/compose-spec/compose-go/dotenv/parser.go @@ -4,6 +4,8 @@ import ( "bytes" "errors" "fmt" + "regexp" + "strconv" "strings" "unicode" ) @@ -12,8 +14,11 @@ const ( charComment = '#' prefixSingleQuote = '\'' prefixDoubleQuote = '"' +) - exportPrefix = "export" +var ( + escapeSeqRegex = regexp.MustCompile(`(\\(?:[abcfnrtv$"\\]|0\d{0,3}))`) + exportRegex = regexp.MustCompile(`^export\s+`) ) func parseBytes(src []byte, out map[string]string, lookupFn LookupFn) error { @@ -85,7 +90,7 @@ func locateKeyName(src []byte) (string, []byte, bool, error) { var key string var inherited bool // trim "export" and space at beginning - src = bytes.TrimLeftFunc(bytes.TrimPrefix(src, []byte(exportPrefix)), isSpace) + src = bytes.TrimLeftFunc(exportRegex.ReplaceAll(src, nil), isSpace) // locate key name end and validate it in single loop offset := 0 @@ -131,21 +136,12 @@ func extractVarValue(src []byte, envMap map[string]string, lookupFn LookupFn) (s quote, isQuoted := hasQuotePrefix(src) if !isQuoted { // unquoted value - read until new line - end := bytes.IndexFunc(src, isNewLine) - var rest []byte - if end < 0 { - value := strings.Split(string(src), "#")[0] // Remove inline comments on unquoted lines - value = strings.TrimRightFunc(value, unicode.IsSpace) + value, rest, _ := bytes.Cut(src, []byte("\n")) - retVal, err := expandVariables(value, envMap, lookupFn) - return retVal, nil, err - } - - value := strings.Split(string(src[0:end]), "#")[0] - value = strings.TrimRightFunc(value, unicode.IsSpace) - rest = src[end:] - - retVal, err := expandVariables(value, envMap, lookupFn) + // Remove inline comments on unquoted lines + value, _, _ = bytes.Cut(value, []byte(" #")) + value = bytes.TrimRightFunc(value, unicode.IsSpace) + retVal, err := expandVariables(string(value), envMap, lookupFn) return retVal, rest, err } @@ -161,12 +157,10 @@ func extractVarValue(src []byte, envMap map[string]string, lookupFn LookupFn) (s } // trim quotes - trimFunc := isCharFunc(rune(quote)) - value := string(bytes.TrimLeftFunc(bytes.TrimRightFunc(src[0:i], trimFunc), trimFunc)) + value := string(src[1:i]) if quote == prefixDoubleQuote { - // unescape newlines for double quote (this is compat feature) - // and expand environment variables - + // expand standard shell escape sequences & then interpolate + // variables on the result retVal, err := expandVariables(expandEscapes(value), envMap, lookupFn) if err != nil { return "", nil, err @@ -187,20 +181,35 @@ func extractVarValue(src []byte, envMap map[string]string, lookupFn LookupFn) (s } func expandEscapes(str string) string { - out := escapeRegex.ReplaceAllStringFunc(str, func(match string) string { - c := strings.TrimPrefix(match, `\`) - switch c { - case "n": - return "\n" - case "r": - return "\r" - case "$": + out := escapeSeqRegex.ReplaceAllStringFunc(str, func(match string) string { + if match == `\$` { + // `\$` is not a Go escape sequence, the expansion parser uses + // the special `$$` syntax + // both `FOO=\$bar` and `FOO=$$bar` are valid in an env file and + // will result in FOO w/ literal value of "$bar" (no interpolation) return "$$" - default: + } + + if strings.HasPrefix(match, `\0`) { + // octal escape sequences in Go are not prefixed with `\0`, so + // rewrite the prefix, e.g. `\0123` -> `\123` -> literal value "S" + match = strings.Replace(match, `\0`, `\`, 1) + } + + // use Go to unquote (unescape) the literal + // see https://go.dev/ref/spec#Rune_literals + // + // NOTE: Go supports ADDITIONAL escapes like `\x` & `\u` & `\U`! + // These are NOT supported, which is why we use a regex to find + // only matches we support and then use `UnquoteChar` instead of a + // `Unquote` on the entire value + v, _, _, err := strconv.UnquoteChar(match, '"') + if err != nil { return match } + return string(v) }) - return unescapeCharsRegex.ReplaceAllString(out, "$1") + return out } func indexOfNonSpaceChar(src []byte) int { @@ -239,8 +248,3 @@ func isSpace(r rune) bool { } return false } - -// isNewLine reports whether the rune is a new line character -func isNewLine(r rune) bool { - return r == '\n' -} diff --git a/vendor/github.com/compose-spec/compose-go/template/template.go b/vendor/github.com/compose-spec/compose-go/template/template.go index 1e497ab3..27f7067a 100644 --- a/vendor/github.com/compose-spec/compose-go/template/template.go +++ b/vendor/github.com/compose-spec/compose-go/template/template.go @@ -31,7 +31,7 @@ var substitutionNamed = "[_a-z][_a-z0-9]*" var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-+?](.*}|[^}]*))?" var patternString = fmt.Sprintf( - "%s(?i:(?P%s)|(?P%s)|{(?P%s)}|(?P))", + "%s(?i:(?P%s)|(?P%s)|{(?:(?P%s)}|(?P)))", delimiter, delimiter, substitutionNamed, substitutionBraced, ) @@ -62,6 +62,7 @@ type SubstituteFunc func(string, Mapping) (string, bool, error) // It accepts additional substitute function. func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) { var outerErr error + var returnErr error result := pattern.ReplaceAllStringFunc(template, func(substring string) string { _, subsFunc := getSubstitutionFunctionForTemplate(substring) @@ -91,6 +92,9 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su if substitution == "" { outerErr = &InvalidTemplateError{Template: template} + if returnErr == nil { + returnErr = outerErr + } return "" } @@ -101,6 +105,9 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su ) value, applied, outerErr = subsFunc(substitution, mapping) if outerErr != nil { + if returnErr == nil { + returnErr = outerErr + } return "" } if applied { @@ -119,7 +126,7 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su return value }) - return result, outerErr + return result, returnErr } func getSubstitutionFunctionForTemplate(template string) (string, SubstituteFunc) { diff --git a/vendor/github.com/docker/go-units/size.go b/vendor/github.com/docker/go-units/size.go index 85f6ab07..c245a895 100644 --- a/vendor/github.com/docker/go-units/size.go +++ b/vendor/github.com/docker/go-units/size.go @@ -2,7 +2,6 @@ package units import ( "fmt" - "regexp" "strconv" "strings" ) @@ -26,16 +25,17 @@ const ( PiB = 1024 * TiB ) -type unitMap map[string]int64 +type unitMap map[byte]int64 var ( - decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} - binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} - sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`) + decimalMap = unitMap{'k': KB, 'm': MB, 'g': GB, 't': TB, 'p': PB} + binaryMap = unitMap{'k': KiB, 'm': MiB, 'g': GiB, 't': TiB, 'p': PiB} ) -var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} -var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} +var ( + decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} +) func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) { i := 0 @@ -89,20 +89,66 @@ func RAMInBytes(size string) (int64, error) { // Parses the human-readable size string into the amount it represents. func parseSize(sizeStr string, uMap unitMap) (int64, error) { - matches := sizeRegex.FindStringSubmatch(sizeStr) - if len(matches) != 4 { + // TODO: rewrite to use strings.Cut if there's a space + // once Go < 1.18 is deprecated. + sep := strings.LastIndexAny(sizeStr, "01234567890. ") + if sep == -1 { + // There should be at least a digit. return -1, fmt.Errorf("invalid size: '%s'", sizeStr) } + var num, sfx string + if sizeStr[sep] != ' ' { + num = sizeStr[:sep+1] + sfx = sizeStr[sep+1:] + } else { + // Omit the space separator. + num = sizeStr[:sep] + sfx = sizeStr[sep+1:] + } - size, err := strconv.ParseFloat(matches[1], 64) + size, err := strconv.ParseFloat(num, 64) if err != nil { return -1, err } + // Backward compatibility: reject negative sizes. + if size < 0 { + return -1, fmt.Errorf("invalid size: '%s'", sizeStr) + } - unitPrefix := strings.ToLower(matches[3]) - if mul, ok := uMap[unitPrefix]; ok { + if len(sfx) == 0 { + return int64(size), nil + } + + // Process the suffix. + + if len(sfx) > 3 { // Too long. + goto badSuffix + } + sfx = strings.ToLower(sfx) + // Trivial case: b suffix. + if sfx[0] == 'b' { + if len(sfx) > 1 { // no extra characters allowed after b. + goto badSuffix + } + return int64(size), nil + } + // A suffix from the map. + if mul, ok := uMap[sfx[0]]; ok { size *= float64(mul) + } else { + goto badSuffix + } + + // The suffix may have extra "b" or "ib" (e.g. KiB or MB). + switch { + case len(sfx) == 2 && sfx[1] != 'b': + goto badSuffix + case len(sfx) == 3 && sfx[1:] != "ib": + goto badSuffix } return int64(size), nil + +badSuffix: + return -1, fmt.Errorf("invalid suffix: '%s'", sfx) } diff --git a/vendor/modules.txt b/vendor/modules.txt index d37d651a..4e0ed985 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -134,8 +134,8 @@ github.com/cenkalti/backoff/v4 github.com/cespare/xxhash/v2 # github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e ## explicit -# github.com/compose-spec/compose-go v1.5.0 -## explicit; go 1.17 +# github.com/compose-spec/compose-go v1.6.0 +## explicit; go 1.18 github.com/compose-spec/compose-go/consts github.com/compose-spec/compose-go/dotenv github.com/compose-spec/compose-go/errdefs @@ -286,7 +286,7 @@ github.com/docker/go-connections/tlsconfig # github.com/docker/go-metrics v0.0.1 ## explicit; go 1.11 github.com/docker/go-metrics -# github.com/docker/go-units v0.4.0 +# github.com/docker/go-units v0.5.0 ## explicit github.com/docker/go-units # github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4