Moving NewIndexInfo, NewRepositoryInfo and associated helpers into config.go
Signed-off-by: Don Kjer <don.kjer@gmail.com>master
							parent
							
								
									64b000c3ea
								
							
						
					
					
						commit
						c899a49a95
					
				
							
								
								
									
										15
									
								
								docs/auth.go
								
								
								
								
							
							
						
						
									
										15
									
								
								docs/auth.go
								
								
								
								
							| 
						 | 
				
			
			@ -17,13 +17,6 @@ import (
 | 
			
		|||
const (
 | 
			
		||||
	// Where we store the config file
 | 
			
		||||
	CONFIGFILE = ".dockercfg"
 | 
			
		||||
 | 
			
		||||
	// Only used for user auth + account creation
 | 
			
		||||
	INDEXSERVER    = "https://index.docker.io/v1/"
 | 
			
		||||
	REGISTRYSERVER = "https://registry-1.docker.io/v1/"
 | 
			
		||||
	INDEXNAME      = "docker.io"
 | 
			
		||||
 | 
			
		||||
	// INDEXSERVER = "https://registry-stage.hub.docker.com/v1/"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			@ -43,14 +36,6 @@ type ConfigFile struct {
 | 
			
		|||
	rootPath string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IndexServerAddress() string {
 | 
			
		||||
	return INDEXSERVER
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IndexServerName() string {
 | 
			
		||||
	return INDEXNAME
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// create a base64 encoded auth string to store in config
 | 
			
		||||
func encodeAuth(authConfig *AuthConfig) string {
 | 
			
		||||
	authStr := authConfig.Username + ":" + authConfig.Password
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										312
									
								
								docs/config.go
								
								
								
								
							
							
						
						
									
										312
									
								
								docs/config.go
								
								
								
								
							| 
						 | 
				
			
			@ -2,12 +2,16 @@ package registry
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/opts"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Options holds command line options.
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +20,30 @@ type Options struct {
 | 
			
		|||
	InsecureRegistries opts.ListOpts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Only used for user auth + account creation
 | 
			
		||||
	INDEXSERVER    = "https://index.docker.io/v1/"
 | 
			
		||||
	REGISTRYSERVER = "https://registry-1.docker.io/v1/"
 | 
			
		||||
	INDEXNAME      = "docker.io"
 | 
			
		||||
 | 
			
		||||
	// INDEXSERVER = "https://registry-stage.hub.docker.com/v1/"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
 | 
			
		||||
	emptyServiceConfig       = NewServiceConfig(nil)
 | 
			
		||||
	validNamespaceChars      = regexp.MustCompile(`^([a-z0-9-_]*)$`)
 | 
			
		||||
	validRepo                = regexp.MustCompile(`^([a-z0-9-_.]+)$`)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func IndexServerAddress() string {
 | 
			
		||||
	return INDEXSERVER
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IndexServerName() string {
 | 
			
		||||
	return INDEXNAME
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstallFlags adds command-line options to the top-level flag parser for
 | 
			
		||||
// the current process.
 | 
			
		||||
func (options *Options) InstallFlags() {
 | 
			
		||||
| 
						 | 
				
			
			@ -25,34 +53,6 @@ func (options *Options) InstallFlags() {
 | 
			
		|||
	flag.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateMirror validates an HTTP(S) registry mirror
 | 
			
		||||
func ValidateMirror(val string) (string, error) {
 | 
			
		||||
	uri, err := url.Parse(val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("%s is not a valid URI", val)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if uri.Scheme != "http" && uri.Scheme != "https" {
 | 
			
		||||
		return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
 | 
			
		||||
		return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s://%s/v1/", uri.Scheme, uri.Host), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateIndexName validates an index name.
 | 
			
		||||
func ValidateIndexName(val string) (string, error) {
 | 
			
		||||
	// 'index.docker.io' => 'docker.io'
 | 
			
		||||
	if val == "index."+IndexServerName() {
 | 
			
		||||
		val = IndexServerName()
 | 
			
		||||
	}
 | 
			
		||||
	// *TODO: Check if valid hostname[:port]/ip[:port]?
 | 
			
		||||
	return val, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type netIPNet net.IPNet
 | 
			
		||||
 | 
			
		||||
func (ipnet *netIPNet) MarshalJSON() ([]byte, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -124,3 +124,259 @@ func NewServiceConfig(options *Options) *ServiceConfig {
 | 
			
		|||
 | 
			
		||||
	return config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isSecureIndex returns false if the provided indexName is part of the list of insecure registries
 | 
			
		||||
// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
 | 
			
		||||
//
 | 
			
		||||
// The list of insecure registries can contain an element with CIDR notation to specify a whole subnet.
 | 
			
		||||
// If the subnet contains one of the IPs of the registry specified by indexName, the latter is considered
 | 
			
		||||
// insecure.
 | 
			
		||||
//
 | 
			
		||||
// indexName should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
 | 
			
		||||
// or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained
 | 
			
		||||
// in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element
 | 
			
		||||
// of insecureRegistries.
 | 
			
		||||
func (config *ServiceConfig) isSecureIndex(indexName string) bool {
 | 
			
		||||
	// Check for configured index, first.  This is needed in case isSecureIndex
 | 
			
		||||
	// is called from anything besides NewIndexInfo, in order to honor per-index configurations.
 | 
			
		||||
	if index, ok := config.IndexConfigs[indexName]; ok {
 | 
			
		||||
		return index.Secure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host, _, err := net.SplitHostPort(indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// assume indexName is of the form `host` without the port and go on.
 | 
			
		||||
		host = indexName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addrs, err := lookupIP(host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ip := net.ParseIP(host)
 | 
			
		||||
		if ip != nil {
 | 
			
		||||
			addrs = []net.IP{ip}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// if ip == nil, then `host` is neither an IP nor it could be looked up,
 | 
			
		||||
		// either because the index is unreachable, or because the index is behind an HTTP proxy.
 | 
			
		||||
		// So, len(addrs) == 0 and we're not aborting.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
 | 
			
		||||
	for _, addr := range addrs {
 | 
			
		||||
		for _, ipnet := range config.InsecureRegistryCIDRs {
 | 
			
		||||
			// check if the addr falls in the subnet
 | 
			
		||||
			if (*net.IPNet)(ipnet).Contains(addr) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateMirror validates an HTTP(S) registry mirror
 | 
			
		||||
func ValidateMirror(val string) (string, error) {
 | 
			
		||||
	uri, err := url.Parse(val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("%s is not a valid URI", val)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if uri.Scheme != "http" && uri.Scheme != "https" {
 | 
			
		||||
		return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
 | 
			
		||||
		return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s://%s/v1/", uri.Scheme, uri.Host), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateIndexName validates an index name.
 | 
			
		||||
func ValidateIndexName(val string) (string, error) {
 | 
			
		||||
	// 'index.docker.io' => 'docker.io'
 | 
			
		||||
	if val == "index."+IndexServerName() {
 | 
			
		||||
		val = IndexServerName()
 | 
			
		||||
	}
 | 
			
		||||
	// *TODO: Check if valid hostname[:port]/ip[:port]?
 | 
			
		||||
	return val, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRemoteName(remoteName string) error {
 | 
			
		||||
	var (
 | 
			
		||||
		namespace string
 | 
			
		||||
		name      string
 | 
			
		||||
	)
 | 
			
		||||
	nameParts := strings.SplitN(remoteName, "/", 2)
 | 
			
		||||
	if len(nameParts) < 2 {
 | 
			
		||||
		namespace = "library"
 | 
			
		||||
		name = nameParts[0]
 | 
			
		||||
 | 
			
		||||
		// the repository name must not be a valid image ID
 | 
			
		||||
		if err := utils.ValidateID(name); err == nil {
 | 
			
		||||
			return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		namespace = nameParts[0]
 | 
			
		||||
		name = nameParts[1]
 | 
			
		||||
	}
 | 
			
		||||
	if !validNamespaceChars.MatchString(namespace) {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Only [a-z0-9-_] are allowed.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if len(namespace) < 4 || len(namespace) > 30 {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Cannot be fewer than 4 or more than 30 characters.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasPrefix(namespace, "-") || strings.HasSuffix(namespace, "-") {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Cannot begin or end with a hyphen.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if strings.Contains(namespace, "--") {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Cannot contain consecutive hyphens.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if !validRepo.MatchString(name) {
 | 
			
		||||
		return fmt.Errorf("Invalid repository name (%s), only [a-z0-9-_.] are allowed", name)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateNoSchema(reposName string) error {
 | 
			
		||||
	if strings.Contains(reposName, "://") {
 | 
			
		||||
		// It cannot contain a scheme!
 | 
			
		||||
		return ErrInvalidRepositoryName
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateRepositoryName validates a repository name
 | 
			
		||||
func ValidateRepositoryName(reposName string) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	if err = validateNoSchema(reposName); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	indexName, remoteName := splitReposName(reposName)
 | 
			
		||||
	if _, err = ValidateIndexName(indexName); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return validateRemoteName(remoteName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewIndexInfo returns IndexInfo configuration from indexName
 | 
			
		||||
func (config *ServiceConfig) NewIndexInfo(indexName string) (*IndexInfo, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	indexName, err = ValidateIndexName(indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Return any configured index info, first.
 | 
			
		||||
	if index, ok := config.IndexConfigs[indexName]; ok {
 | 
			
		||||
		return index, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Construct a non-configured index info.
 | 
			
		||||
	index := &IndexInfo{
 | 
			
		||||
		Name:     indexName,
 | 
			
		||||
		Mirrors:  make([]string, 0),
 | 
			
		||||
		Official: false,
 | 
			
		||||
	}
 | 
			
		||||
	index.Secure = config.isSecureIndex(indexName)
 | 
			
		||||
	return index, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAuthConfigKey special-cases using the full index address of the official
 | 
			
		||||
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
 | 
			
		||||
func (index *IndexInfo) GetAuthConfigKey() string {
 | 
			
		||||
	if index.Official {
 | 
			
		||||
		return IndexServerAddress()
 | 
			
		||||
	}
 | 
			
		||||
	return index.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
		// 'docker.io'
 | 
			
		||||
		indexName = IndexServerName()
 | 
			
		||||
		remoteName = reposName
 | 
			
		||||
	} else {
 | 
			
		||||
		indexName = nameParts[0]
 | 
			
		||||
		remoteName = nameParts[1]
 | 
			
		||||
	}
 | 
			
		||||
	return indexName, remoteName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
 | 
			
		||||
func (config *ServiceConfig) NewRepositoryInfo(reposName string) (*RepositoryInfo, error) {
 | 
			
		||||
	if err := validateNoSchema(reposName); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	indexName, remoteName := splitReposName(reposName)
 | 
			
		||||
	if err := validateRemoteName(remoteName); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repoInfo := &RepositoryInfo{
 | 
			
		||||
		RemoteName: remoteName,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	repoInfo.Index, err = config.NewIndexInfo(indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if repoInfo.Index.Official {
 | 
			
		||||
		normalizedName := repoInfo.RemoteName
 | 
			
		||||
		if strings.HasPrefix(normalizedName, "library/") {
 | 
			
		||||
			// If pull "library/foo", it's stored locally under "foo"
 | 
			
		||||
			normalizedName = strings.SplitN(normalizedName, "/", 2)[1]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		repoInfo.LocalName = normalizedName
 | 
			
		||||
		repoInfo.RemoteName = normalizedName
 | 
			
		||||
		// If the normalized name does not contain a '/' (e.g. "foo")
 | 
			
		||||
		// then it is an official repo.
 | 
			
		||||
		if strings.IndexRune(normalizedName, '/') == -1 {
 | 
			
		||||
			repoInfo.Official = true
 | 
			
		||||
			// Fix up remote name for official repos.
 | 
			
		||||
			repoInfo.RemoteName = "library/" + normalizedName
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// *TODO: Prefix this with 'docker.io/'.
 | 
			
		||||
		repoInfo.CanonicalName = repoInfo.LocalName
 | 
			
		||||
	} else {
 | 
			
		||||
		// *TODO: Decouple index name from hostname (via registry configuration?)
 | 
			
		||||
		repoInfo.LocalName = repoInfo.Index.Name + "/" + repoInfo.RemoteName
 | 
			
		||||
		repoInfo.CanonicalName = repoInfo.LocalName
 | 
			
		||||
	}
 | 
			
		||||
	return repoInfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSearchTerm special-cases using local name for official index, and
 | 
			
		||||
// remote name for private indexes.
 | 
			
		||||
func (repoInfo *RepositoryInfo) GetSearchTerm() string {
 | 
			
		||||
	if repoInfo.Index.Official {
 | 
			
		||||
		return repoInfo.LocalName
 | 
			
		||||
	}
 | 
			
		||||
	return repoInfo.RemoteName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
 | 
			
		||||
// lacks registry configuration.
 | 
			
		||||
func ParseRepositoryInfo(reposName string) (*RepositoryInfo, error) {
 | 
			
		||||
	return emptyServiceConfig.NewRepositoryInfo(reposName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NormalizeLocalName transforms a repository name into a normalize LocalName
 | 
			
		||||
// Passes through the name without transformation on error (image id, etc)
 | 
			
		||||
func NormalizeLocalName(name string) string {
 | 
			
		||||
	repoInfo, err := ParseRepositoryInfo(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
	return repoInfo.LocalName
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,52 +157,3 @@ func (e Endpoint) Ping() (RegistryInfo, error) {
 | 
			
		|||
	log.Debugf("RegistryInfo.Standalone: %t", info.Standalone)
 | 
			
		||||
	return info, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isSecureIndex returns false if the provided indexName is part of the list of insecure registries
 | 
			
		||||
// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
 | 
			
		||||
//
 | 
			
		||||
// The list of insecure registries can contain an element with CIDR notation to specify a whole subnet.
 | 
			
		||||
// If the subnet contains one of the IPs of the registry specified by indexName, the latter is considered
 | 
			
		||||
// insecure.
 | 
			
		||||
//
 | 
			
		||||
// indexName should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
 | 
			
		||||
// or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained
 | 
			
		||||
// in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element
 | 
			
		||||
// of insecureRegistries.
 | 
			
		||||
func (config *ServiceConfig) isSecureIndex(indexName string) bool {
 | 
			
		||||
	// Check for configured index, first.  This is needed in case isSecureIndex
 | 
			
		||||
	// is called from anything besides NewIndexInfo, in order to honor per-index configurations.
 | 
			
		||||
	if index, ok := config.IndexConfigs[indexName]; ok {
 | 
			
		||||
		return index.Secure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	host, _, err := net.SplitHostPort(indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// assume indexName is of the form `host` without the port and go on.
 | 
			
		||||
		host = indexName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addrs, err := lookupIP(host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ip := net.ParseIP(host)
 | 
			
		||||
		if ip != nil {
 | 
			
		||||
			addrs = []net.IP{ip}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// if ip == nil, then `host` is neither an IP nor it could be looked up,
 | 
			
		||||
		// either because the index is unreachable, or because the index is behind an HTTP proxy.
 | 
			
		||||
		// So, len(addrs) == 0 and we're not aborting.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
 | 
			
		||||
	for _, addr := range addrs {
 | 
			
		||||
		for _, ipnet := range config.InsecureRegistryCIDRs {
 | 
			
		||||
			// check if the addr falls in the subnet
 | 
			
		||||
			if (*net.IPNet)(ipnet).Contains(addr) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										190
									
								
								docs/registry.go
								
								
								
								
							
							
						
						
									
										190
									
								
								docs/registry.go
								
								
								
								
							| 
						 | 
				
			
			@ -10,7 +10,6 @@ import (
 | 
			
		|||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,13 +18,9 @@ import (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrAlreadyExists         = errors.New("Image already exists")
 | 
			
		||||
	ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
 | 
			
		||||
	ErrDoesNotExist          = errors.New("Image does not exist")
 | 
			
		||||
	errLoginRequired         = errors.New("Authentication is required.")
 | 
			
		||||
	validNamespaceChars      = regexp.MustCompile(`^([a-z0-9-_]*)$`)
 | 
			
		||||
	validRepo                = regexp.MustCompile(`^([a-z0-9-_.]+)$`)
 | 
			
		||||
	emptyServiceConfig       = NewServiceConfig(nil)
 | 
			
		||||
	ErrAlreadyExists = errors.New("Image already exists")
 | 
			
		||||
	ErrDoesNotExist  = errors.New("Image does not exist")
 | 
			
		||||
	errLoginRequired = errors.New("Authentication is required.")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type TimeoutType uint32
 | 
			
		||||
| 
						 | 
				
			
			@ -161,185 +156,6 @@ func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secur
 | 
			
		|||
	return res, client, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRemoteName(remoteName string) error {
 | 
			
		||||
	var (
 | 
			
		||||
		namespace string
 | 
			
		||||
		name      string
 | 
			
		||||
	)
 | 
			
		||||
	nameParts := strings.SplitN(remoteName, "/", 2)
 | 
			
		||||
	if len(nameParts) < 2 {
 | 
			
		||||
		namespace = "library"
 | 
			
		||||
		name = nameParts[0]
 | 
			
		||||
 | 
			
		||||
		// the repository name must not be a valid image ID
 | 
			
		||||
		if err := utils.ValidateID(name); err == nil {
 | 
			
		||||
			return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		namespace = nameParts[0]
 | 
			
		||||
		name = nameParts[1]
 | 
			
		||||
	}
 | 
			
		||||
	if !validNamespaceChars.MatchString(namespace) {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Only [a-z0-9-_] are allowed.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if len(namespace) < 4 || len(namespace) > 30 {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Cannot be fewer than 4 or more than 30 characters.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if strings.HasPrefix(namespace, "-") || strings.HasSuffix(namespace, "-") {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Cannot begin or end with a hyphen.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if strings.Contains(namespace, "--") {
 | 
			
		||||
		return fmt.Errorf("Invalid namespace name (%s). Cannot contain consecutive hyphens.", namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if !validRepo.MatchString(name) {
 | 
			
		||||
		return fmt.Errorf("Invalid repository name (%s), only [a-z0-9-_.] are allowed", name)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewIndexInfo returns IndexInfo configuration from indexName
 | 
			
		||||
func NewIndexInfo(config *ServiceConfig, indexName string) (*IndexInfo, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	indexName, err = ValidateIndexName(indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Return any configured index info, first.
 | 
			
		||||
	if index, ok := config.IndexConfigs[indexName]; ok {
 | 
			
		||||
		return index, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Construct a non-configured index info.
 | 
			
		||||
	index := &IndexInfo{
 | 
			
		||||
		Name:     indexName,
 | 
			
		||||
		Mirrors:  make([]string, 0),
 | 
			
		||||
		Official: false,
 | 
			
		||||
	}
 | 
			
		||||
	index.Secure = config.isSecureIndex(indexName)
 | 
			
		||||
	return index, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateNoSchema(reposName string) error {
 | 
			
		||||
	if strings.Contains(reposName, "://") {
 | 
			
		||||
		// It cannot contain a scheme!
 | 
			
		||||
		return ErrInvalidRepositoryName
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
		// 'docker.io'
 | 
			
		||||
		indexName = IndexServerName()
 | 
			
		||||
		remoteName = reposName
 | 
			
		||||
	} else {
 | 
			
		||||
		indexName = nameParts[0]
 | 
			
		||||
		remoteName = nameParts[1]
 | 
			
		||||
	}
 | 
			
		||||
	return indexName, remoteName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
 | 
			
		||||
func NewRepositoryInfo(config *ServiceConfig, reposName string) (*RepositoryInfo, error) {
 | 
			
		||||
	if err := validateNoSchema(reposName); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	indexName, remoteName := splitReposName(reposName)
 | 
			
		||||
	if err := validateRemoteName(remoteName); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repoInfo := &RepositoryInfo{
 | 
			
		||||
		RemoteName: remoteName,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	repoInfo.Index, err = NewIndexInfo(config, indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if repoInfo.Index.Official {
 | 
			
		||||
		normalizedName := repoInfo.RemoteName
 | 
			
		||||
		if strings.HasPrefix(normalizedName, "library/") {
 | 
			
		||||
			// If pull "library/foo", it's stored locally under "foo"
 | 
			
		||||
			normalizedName = strings.SplitN(normalizedName, "/", 2)[1]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		repoInfo.LocalName = normalizedName
 | 
			
		||||
		repoInfo.RemoteName = normalizedName
 | 
			
		||||
		// If the normalized name does not contain a '/' (e.g. "foo")
 | 
			
		||||
		// then it is an official repo.
 | 
			
		||||
		if strings.IndexRune(normalizedName, '/') == -1 {
 | 
			
		||||
			repoInfo.Official = true
 | 
			
		||||
			// Fix up remote name for official repos.
 | 
			
		||||
			repoInfo.RemoteName = "library/" + normalizedName
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// *TODO: Prefix this with 'docker.io/'.
 | 
			
		||||
		repoInfo.CanonicalName = repoInfo.LocalName
 | 
			
		||||
	} else {
 | 
			
		||||
		// *TODO: Decouple index name from hostname (via registry configuration?)
 | 
			
		||||
		repoInfo.LocalName = repoInfo.Index.Name + "/" + repoInfo.RemoteName
 | 
			
		||||
		repoInfo.CanonicalName = repoInfo.LocalName
 | 
			
		||||
	}
 | 
			
		||||
	return repoInfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateRepositoryName validates a repository name
 | 
			
		||||
func ValidateRepositoryName(reposName string) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	if err = validateNoSchema(reposName); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	indexName, remoteName := splitReposName(reposName)
 | 
			
		||||
	if _, err = ValidateIndexName(indexName); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return validateRemoteName(remoteName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
 | 
			
		||||
// lacks registry configuration.
 | 
			
		||||
func ParseRepositoryInfo(reposName string) (*RepositoryInfo, error) {
 | 
			
		||||
	return NewRepositoryInfo(emptyServiceConfig, reposName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NormalizeLocalName transforms a repository name into a normalize LocalName
 | 
			
		||||
// Passes through the name without transformation on error (image id, etc)
 | 
			
		||||
func NormalizeLocalName(name string) string {
 | 
			
		||||
	repoInfo, err := ParseRepositoryInfo(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
	return repoInfo.LocalName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAuthConfigKey special-cases using the full index address of the official
 | 
			
		||||
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
 | 
			
		||||
func (index *IndexInfo) GetAuthConfigKey() string {
 | 
			
		||||
	if index.Official {
 | 
			
		||||
		return IndexServerAddress()
 | 
			
		||||
	}
 | 
			
		||||
	return index.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSearchTerm special-cases using local name for official index, and
 | 
			
		||||
// remote name for private indexes.
 | 
			
		||||
func (repoInfo *RepositoryInfo) GetSearchTerm() string {
 | 
			
		||||
	if repoInfo.Index.Official {
 | 
			
		||||
		return repoInfo.LocalName
 | 
			
		||||
	}
 | 
			
		||||
	return repoInfo.RemoteName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func trustedLocation(req *http.Request) bool {
 | 
			
		||||
	var (
 | 
			
		||||
		trusteds = []string{"docker.com", "docker.io"}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -561,7 +561,7 @@ func TestParseRepositoryInfo(t *testing.T) {
 | 
			
		|||
func TestNewIndexInfo(t *testing.T) {
 | 
			
		||||
	testIndexInfo := func(config *ServiceConfig, expectedIndexInfos map[string]*IndexInfo) {
 | 
			
		||||
		for indexName, expectedIndexInfo := range expectedIndexInfos {
 | 
			
		||||
			index, err := NewIndexInfo(config, indexName)
 | 
			
		||||
			index, err := config.NewIndexInfo(indexName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			} else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ func (s *Service) ResolveRepository(job *engine.Job) engine.Status {
 | 
			
		|||
		reposName = job.Args[0]
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	repoInfo, err := NewRepositoryInfo(s.Config, reposName)
 | 
			
		||||
	repoInfo, err := s.Config.NewRepositoryInfo(reposName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return job.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +168,7 @@ func (s *Service) ResolveIndex(job *engine.Job) engine.Status {
 | 
			
		|||
		indexName = job.Args[0]
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	index, err := NewIndexInfo(s.Config, indexName)
 | 
			
		||||
	index, err := s.Config.NewIndexInfo(indexName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return job.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue