From 9164801bd50bf2e6de1581f3cda115b606442d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iago=20L=C3=B3pez=20Galeiras?= Date: Fri, 19 Feb 2016 14:23:05 +0100 Subject: [PATCH 1/2] Godeps: bump docker2aci Fixes https://github.com/coreos/rkt/issues/2008 Closes https://github.com/coreos/rkt/pull/2134 --- Godeps/Godeps.json | 4 +- .../appc/docker2aci/lib/backend/file/file.go | 6 +- .../lib/backend/repository/repository.go | 5 +- .../appc/docker2aci/lib/common/common.go | 21 ++-- .../docker2aci/lib/common/docker_functions.go | 103 +++++++++++++++++- rkt/image/dockerfetcher.go | 5 +- 6 files changed, 127 insertions(+), 17 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 8eeffa8967..97f3bb27a7 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -81,11 +81,11 @@ }, { "ImportPath": "github.com/appc/docker2aci/lib", - "Rev": "bb4c29887d329bbd135ffee16ffd0dfa0c1ab97e" + "Rev": "669f6823272a9da620a86658a1da3c5195df3897" }, { "ImportPath": "github.com/appc/docker2aci/tarball", - "Rev": "bb4c29887d329bbd135ffee16ffd0dfa0c1ab97e" + "Rev": "669f6823272a9da620a86658a1da3c5195df3897" }, { "ImportPath": "github.com/appc/goaci/proj2aci", diff --git a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/file/file.go b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/file/file.go index 76b36e37a0..7bdd547b54 100644 --- a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/file/file.go +++ b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/file/file.go @@ -29,7 +29,11 @@ func NewFileBackend(file *os.File) *FileBackend { } func (lb *FileBackend) GetImageInfo(dockerURL string) ([]string, *types.ParsedDockerURL, error) { - parsedDockerURL := common.ParseDockerURL(dockerURL) + parsedDockerURL, err := common.ParseDockerURL(dockerURL) + if err != nil { + // a missing Docker URL could mean that the file only contains one + // image, so we ignore the error here, we'll handle it in getImageID + } appImageID, parsedDockerURL, err := getImageID(lb.file, parsedDockerURL) if err != nil { diff --git a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/repository/repository.go b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/repository/repository.go index 36869e45ef..19379c0ce3 100644 --- a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/repository/repository.go +++ b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/backend/repository/repository.go @@ -53,7 +53,10 @@ func NewRepositoryBackend(username string, password string, insecure bool) *Repo } func (rb *RepositoryBackend) GetImageInfo(url string) ([]string, *types.ParsedDockerURL, error) { - dockerURL := common.ParseDockerURL(url) + dockerURL, err := common.ParseDockerURL(url) + if err != nil { + return nil, nil, err + } var supportsV2, ok bool var URLSchema string diff --git a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/common.go b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/common.go index 02f2f9b12e..31b7ab2a8f 100644 --- a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/common.go +++ b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/common.go @@ -4,6 +4,7 @@ import ( "archive/tar" "compress/gzip" "encoding/json" + "errors" "fmt" "io" "os" @@ -40,9 +41,13 @@ const ( GzipCompression ) -func ParseDockerURL(arg string) *types.ParsedDockerURL { +func ParseDockerURL(arg string) (*types.ParsedDockerURL, error) { if arg == "" { - return nil + return nil, errors.New("empty Docker image reference") + } + + if !referenceRegexp.MatchString(arg) { + return nil, fmt.Errorf("invalid Docker image reference %q", arg) } taglessRemote, tag := parseRepositoryTag(arg) @@ -51,19 +56,19 @@ func ParseDockerURL(arg string) *types.ParsedDockerURL { } indexURL, imageName := SplitReposName(taglessRemote) - if indexURL == "" && !strings.Contains(imageName, "/") { + // the Docker client considers images referenced only by a name (e.g. + // "busybox" or "ubuntu") as valid, and, in that case, it adds the + // "library/" prefix because that's how they're stored in the official + // registry + if indexURL == defaultIndexURL && !strings.Contains(imageName, "/") { imageName = "library/" + imageName } - if indexURL == "" { - indexURL = defaultIndexURL - } - return &types.ParsedDockerURL{ IndexURL: indexURL, ImageName: imageName, Tag: tag, - } + }, nil } func GenerateACI(layerNumber int, layerData types.DockerImageData, dockerURL *types.ParsedDockerURL, outputDir string, layerFile *os.File, curPwl []string, compression Compression) (string, *schema.ImageManifest, error) { diff --git a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/docker_functions.go b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/docker_functions.go index 2d28808027..c3b90c547c 100644 --- a/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/docker_functions.go +++ b/Godeps/_workspace/src/github.com/appc/docker2aci/lib/common/docker_functions.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" "path" + "regexp" "runtime" "strings" @@ -31,16 +32,110 @@ const ( dockercfgFileName = ".dockercfg" ) -// splitReposName breaks a reposName into an index name and remote name +// Image references supported by docker2aci. +// Taken from https://github.com/docker/distribution/blob/master/reference/reference.go +// +// reference := name [ ":" tag ] +// name := [hostname '/'] component ['/' component]* +// hostname := hostcomponent ['.' hostcomponent]* [':' port-number] +// hostcomponent := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/ +// port-number := /[0-9]+/ +// component := alpha-numeric [separator alpha-numeric]* +// alpha-numeric := /[a-z0-9]+/ +// separator := /[_.]|__|[-]*/ +// tag := /[\w][\w.-]{0,127}/ +// +// TODO: check for digests if/when we support them +var ( + referenceRegexp = anchored(capture(nameRegexp), + optional(literal(":"), capture(tagRegexp))) + + nameRegexp = expression( + optional(hostnameRegexp, literal(`/`)), + componentRegexp, + optional(repeated(literal(`/`), componentRegexp))) + + hostnameRegexp = expression( + hostComponentRegexp, + optional(repeated(literal(`.`), hostComponentRegexp)), + optional(literal(`:`), portNumberRegexp)) + + hostComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) + + portNumberRegexp = match(`[0-9]+`) + + componentRegexp = expression( + alphaNumericRegexp, + optional(repeated(separatorRegexp, alphaNumericRegexp))) + + alphaNumericRegexp = match(`[a-z0-9]+`) + + separatorRegexp = match(`(?:[._]|__|[-]*)`) + + tagRegexp = match(`[\w][\w.-]{0,127}`) +) + +// match compiles the string to a regular expression. +var match = regexp.MustCompile + +// literal compiles s into a literal regular expression, escaping any regexp +// reserved characters. +func literal(s string) *regexp.Regexp { + re := match(regexp.QuoteMeta(s)) + + if _, complete := re.LiteralPrefix(); !complete { + panic("must be a literal") + } + + return re +} + +// expression defines a full expression, where each regular expression must +// follow the previous. +func expression(res ...*regexp.Regexp) *regexp.Regexp { + var s string + for _, re := range res { + s += re.String() + } + + return match(s) +} + +// optional wraps the expression in a non-capturing group and makes the +// production optional. +func optional(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `?`) +} + +// repeated wraps the regexp in a non-capturing group to get one or more +// matches. +func repeated(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `+`) +} + +// group wraps the regexp in a non-capturing group. +func group(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(?:` + expression(res...).String() + `)`) +} + +// capture wraps the expression in a capturing group. +func capture(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(` + expression(res...).String() + `)`) +} + +// anchored anchors the regular expression by adding start and end delimiters. +func anchored(res ...*regexp.Regexp) *regexp.Regexp { + return match(`^` + expression(res...).String() + `$`) +} + +// SplitReposName breaks a reposName into an index name and remote name func SplitReposName(reposName string) (string, string) { nameParts := strings.SplitN(reposName, "/", 2) var indexName, remoteName string if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") { // This is a Docker Index repos (ex: samalba/hipache or ubuntu) - // The URL for the index is different depending on the version of the - // API used to fetch it, so it cannot be inferred here. - indexName = "" + indexName = defaultIndexURL remoteName = reposName } else { indexName = nameParts[0] diff --git a/rkt/image/dockerfetcher.go b/rkt/image/dockerfetcher.go index 0b52312cec..a97e8b34a8 100644 --- a/rkt/image/dockerfetcher.go +++ b/rkt/image/dockerfetcher.go @@ -50,7 +50,10 @@ type dockerFetcher struct { // ACI, then stores it in the store and returns the hash. func (f *dockerFetcher) GetHash(u *url.URL) (string, error) { ensureLogger(f.Debug) - dockerURL := d2acommon.ParseDockerURL(path.Join(u.Host, u.Path)) + dockerURL, err := d2acommon.ParseDockerURL(path.Join(u.Host, u.Path)) + if err != nil { + return "", fmt.Errorf(`invalid docker URL %q; expected syntax is "docker://[REGISTRY_HOST[:REGISTRY_PORT]/]IMAGE_NAME[:TAG]"`, u) + } latest := dockerURL.Tag == "latest" return f.fetchImageFrom(u, latest) } From 578f5afac93881213312634d87f10858b96a23bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iago=20L=C3=B3pez=20Galeiras?= Date: Fri, 19 Feb 2016 15:55:33 +0100 Subject: [PATCH 2/2] CHANGELOG: add entry about auth fetching from docker registry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index edcbe144a1..da55035213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Bug fixes - Socket activation was not working if the port on the host is different from the app port as set in the image manifest ([#2137](https://github.com/coreos/rkt/pull/2137)). +- Fix a bug when fetching images from private repositories in the official Docker registry ([#2197](https://github.com/coreos/rkt/pull/2197)). ## v1.0.0