From 1f908f0890f23c0dcc73fd720ad9f5d123bfa364 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 7 Sep 2017 11:57:16 +0200 Subject: [PATCH] pkg: storage: fix additional registries Signed-off-by: Antonio Murdaca --- contrib/test/crio-integration-playbook.yaml | 13 +++ pkg/storage/image.go | 109 ++++++++++++++++++-- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/contrib/test/crio-integration-playbook.yaml b/contrib/test/crio-integration-playbook.yaml index 77492cf5..dcf0e80b 100644 --- a/contrib/test/crio-integration-playbook.yaml +++ b/contrib/test/crio-integration-playbook.yaml @@ -319,6 +319,19 @@ replace: 'storage_driver = "overlay2"' name: /etc/crio/crio.conf backup: yes + - name: run with systemd cgroup manager + replace: + regexp: 'cgroup_manager = "cgroupfs"' + replace: 'cgroup_manager = "systemd"' + name: /etc/crio/crio.conf + backup: yes + - name: add docker.io default registry + lineinfile: + dest: /etc/crio/crio.conf + line: '"docker.io"' + insertafter: 'registries = \[' + regexp: 'docker\.io' + state: present - name: add overlay2 storage opts on RHEL/CentOS lineinfile: dest: /etc/crio/crio.conf diff --git a/pkg/storage/image.go b/pkg/storage/image.go index 53db051a..29e3c746 100644 --- a/pkg/storage/image.go +++ b/pkg/storage/image.go @@ -2,8 +2,10 @@ package storage import ( "errors" + "fmt" "net" "path/filepath" + "regexp" "strings" "github.com/containers/image/copy" @@ -14,6 +16,7 @@ import ( "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" "github.com/containers/storage" + distreference "github.com/docker/distribution/reference" ) // ImageResult wraps a subset of information about an image: its ID, its names, @@ -310,13 +313,107 @@ func isValidHostname(hostname string) bool { strings.Contains(hostname, ":") || hostname == "localhost") } +func isReferenceFullyQualified(reposName reference.Named) bool { + indexName, _, _ := splitReposName(reposName) + return indexName != "" +} + +const ( + // defaultHostname is the default built-in hostname + defaultHostname = "docker.io" + // legacyDefaultHostname is automatically converted to DefaultHostname + legacyDefaultHostname = "index.docker.io" + // defaultRepoPrefix is the prefix used for default repositories in default host + defaultRepoPrefix = "library/" +) + +// splitReposName breaks a reposName into an index name and remote name +func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) { + var remoteNameStr string + indexName, remoteNameStr = distreference.SplitHostname(reposName) + if !isValidHostname(indexName) { + // This is a Docker Index repos (ex: samalba/hipache or ubuntu) + // 'docker.io' + indexName = "" + remoteName = reposName + } else { + remoteName, err = withName(remoteNameStr) + } + return +} + +func validateName(name string) error { + if err := validateID(strings.TrimPrefix(name, defaultHostname+"/")); err == nil { + return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name) + } + return nil +} + +var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`) + +// validateID checks whether an ID string is a valid image ID. +func validateID(id string) error { + if ok := validHex.MatchString(id); !ok { + return fmt.Errorf("image ID %q is invalid", id) + } + return nil +} + +// withName returns a named object representing the given string. If the input +// is invalid ErrReferenceInvalidFormat will be returned. +func withName(name string) (reference.Named, error) { + name, err := normalize(name) + if err != nil { + return nil, err + } + if err := validateName(name); err != nil { + return nil, err + } + r, err := distreference.WithName(name) + return r, err +} + +// splitHostname splits a repository name to hostname and remotename string. +// If no valid hostname is found, empty string will be returned as a resulting +// hostname. Repository name needs to be already validated before. +func splitHostname(name string) (hostname, remoteName string) { + i := strings.IndexRune(name, '/') + if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") { + hostname, remoteName = "", name + } else { + hostname, remoteName = name[:i], name[i+1:] + } + if hostname == legacyDefaultHostname { + hostname = defaultHostname + } + if hostname == defaultHostname && !strings.ContainsRune(remoteName, '/') { + remoteName = defaultRepoPrefix + remoteName + } + return +} + +// normalize returns a repository name in its normalized form, meaning it +// will contain library/ prefix for official images. +func normalize(name string) (string, error) { + host, remoteName := splitHostname(name) + if strings.ToLower(remoteName) != remoteName { + return "", errors.New("invalid reference format: repository name must be lowercase") + } + if host == defaultHostname { + if strings.HasPrefix(remoteName, defaultRepoPrefix) { + remoteName = strings.TrimPrefix(remoteName, defaultRepoPrefix) + } + return host + "/" + remoteName, nil + } + return name, nil +} + func (svc *imageService) ResolveNames(imageName string) ([]string, error) { r, err := reference.ParseNormalizedNamed(imageName) if err != nil { return nil, err } - domain, rest := splitDomain(r.Name()) - if len(domain) != 0 && isValidHostname(domain) { + if isReferenceFullyQualified(r) { // this means the image is already fully qualified return []string{imageName}, nil } @@ -328,14 +425,10 @@ func (svc *imageService) ResolveNames(imageName string) ([]string, error) { // this means we got an image in the form of "busybox" // we need to use additional registries... // normalize the unqualified image to be domain/repo/image... + _, rest := splitDomain(r.Name()) images := []string{} for _, r := range svc.registries { - path := rest - if !isValidHostname(domain) { - // This is the case where we have an image like "runcom/busybox" - path = imageName - } - images = append(images, filepath.Join(r, path)) + images = append(images, filepath.Join(r, rest)) } return images, nil }