Merge pull request #49568 from thaJeztah/dismantle_ParseRepositoryInfo

registry: refactor to allow splitting out logic for CLI
This commit is contained in:
Sebastiaan van Stijn
2025-03-03 15:53:49 +01:00
committed by GitHub
4 changed files with 70 additions and 42 deletions

View File

@@ -56,8 +56,38 @@ var (
Host: DefaultRegistryHost,
}
emptyServiceConfig, _ = newServiceConfig(ServiceOptions{})
validHostPortRegex = lazyregexp.New(`^` + reference.DomainRegexp.String() + `$`)
// ipv6Loopback is the CIDR for the IPv6 loopback address ("::1"); "::1/128"
ipv6Loopback = &net.IPNet{
IP: net.IPv6loopback,
Mask: net.CIDRMask(128, 128),
}
// ipv4Loopback is the CIDR for IPv4 loopback addresses ("127.0.0.0/8")
ipv4Loopback = &net.IPNet{
IP: net.IPv4(127, 0, 0, 0),
Mask: net.CIDRMask(8, 32),
}
// emptyServiceConfig is a default service-config for situations where
// no config-file is available (e.g. when used in the CLI). If won't
// have mirrors configured, but does have the default insecure registry
// CIDRs for loopback interfaces configured.
emptyServiceConfig = &serviceConfig{
IndexConfigs: map[string]*registry.IndexInfo{
IndexName: {
Name: IndexName,
Mirrors: make([]string, 0),
Secure: true,
Official: true,
},
},
InsecureRegistryCIDRs: []*registry.NetIPNet{
(*registry.NetIPNet)(ipv6Loopback),
(*registry.NetIPNet)(ipv4Loopback),
},
}
validHostPortRegex = lazyregexp.New(`^` + reference.DomainRegexp.String() + `$`)
// certsDir is used to override defaultCertsDir.
certsDir string
@@ -288,16 +318,22 @@ func ValidateMirror(val string) (string, error) {
// ValidateIndexName validates an index name. It is used by the daemon to
// validate the daemon configuration.
func ValidateIndexName(val string) (string, error) {
// TODO: upstream this to check to reference package
if val == "index.docker.io" {
val = "docker.io"
}
val = normalizeIndexName(val)
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
return "", invalidParamf("invalid index name (%s). Cannot begin or end with a hyphen", val)
}
return val, nil
}
func normalizeIndexName(val string) string {
// TODO(thaJeztah): consider normalizing other known options, such as "(https://)registry-1.docker.io", "https://index.docker.io/v1/".
// TODO: upstream this to check to reference package
if val == "index.docker.io" {
return "docker.io"
}
return val
}
func hasScheme(reposName string) bool {
return strings.Contains(reposName, "://")
}
@@ -327,25 +363,20 @@ func validateHostPort(s string) error {
}
// newIndexInfo returns IndexInfo configuration from indexName
func newIndexInfo(config *serviceConfig, indexName string) (*registry.IndexInfo, error) {
var err error
indexName, err = ValidateIndexName(indexName)
if err != nil {
return nil, err
}
func newIndexInfo(config *serviceConfig, indexName string) *registry.IndexInfo {
indexName = normalizeIndexName(indexName)
// Return any configured index info, first.
if index, ok := config.IndexConfigs[indexName]; ok {
return index, nil
return index
}
// Construct a non-configured index info.
return &registry.IndexInfo{
Name: indexName,
Mirrors: make([]string, 0),
Secure: config.isSecureIndex(indexName),
Official: false,
}, nil
Name: indexName,
Mirrors: make([]string, 0),
Secure: config.isSecureIndex(indexName),
}
}
// GetAuthConfigKey special-cases using the full index address of the official
@@ -358,18 +389,22 @@ func GetAuthConfigKey(index *registry.IndexInfo) string {
}
// newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
index, err := newIndexInfo(config, reference.Domain(name))
if err != nil {
return nil, err
func newRepositoryInfo(config *serviceConfig, name reference.Named) *RepositoryInfo {
index := newIndexInfo(config, reference.Domain(name))
var officialRepo bool
if index.Official {
// RepositoryInfo.Official indicates whether the image repository
// is an official (docker library official images) repository.
//
// We only need to check this if the image-repository is on Docker Hub.
officialRepo = !strings.ContainsRune(reference.FamiliarName(name), '/')
}
official := !strings.ContainsRune(reference.FamiliarName(name), '/')
return &RepositoryInfo{
Name: reference.TrimNamed(name),
Index: index,
Official: official,
}, nil
Official: officialRepo,
}
}
// ParseRepositoryInfo performs the breakdown of a repository name into a
@@ -377,5 +412,5 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository
//
// It is used by the Docker cli to interact with registry-related endpoints.
func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
return newRepositoryInfo(emptyServiceConfig, reposName)
return newRepositoryInfo(emptyServiceConfig, reposName), nil
}

View File

@@ -248,15 +248,11 @@ func TestNewIndexInfo(t *testing.T) {
overrideLookupIP(t)
testIndexInfo := func(config *serviceConfig, expectedIndexInfos map[string]*registry.IndexInfo) {
for indexName, expectedIndexInfo := range expectedIndexInfos {
index, err := newIndexInfo(config, indexName)
if err != nil {
t.Fatal(err)
} else {
assert.Check(t, is.Equal(index.Name, expectedIndexInfo.Name), indexName+" name")
assert.Check(t, is.Equal(index.Official, expectedIndexInfo.Official), indexName+" is official")
assert.Check(t, is.Equal(index.Secure, expectedIndexInfo.Secure), indexName+" is secure")
assert.Check(t, is.Equal(len(index.Mirrors), len(expectedIndexInfo.Mirrors)), indexName+" mirrors")
}
index := newIndexInfo(config, indexName)
assert.Check(t, is.Equal(index.Name, expectedIndexInfo.Name), indexName+" name")
assert.Check(t, is.Equal(index.Official, expectedIndexInfo.Official), indexName+" is official")
assert.Check(t, is.Equal(index.Secure, expectedIndexInfo.Secure), indexName+" is secure")
assert.Check(t, is.Equal(len(index.Mirrors), len(expectedIndexInfo.Mirrors)), indexName+" mirrors")
}
}

View File

@@ -93,12 +93,8 @@ func (s *Service) searchUnfiltered(ctx context.Context, term string, limit int,
// Search is a long-running operation, just lock s.config to avoid block others.
s.mu.RLock()
index, err := newIndexInfo(s.config, indexName)
index := newIndexInfo(s.config, indexName)
s.mu.RUnlock()
if err != nil {
return nil, err
}
if index.Official {
// If pull "library/foo", it's stored locally under "foo"
remoteName = strings.TrimPrefix(remoteName, "library/")
@@ -158,5 +154,5 @@ func splitReposSearchTerm(reposName string) (string, string) {
// for that.
func ParseSearchIndexInfo(reposName string) (*registry.IndexInfo, error) {
indexName, _ := splitReposSearchTerm(reposName)
return newIndexInfo(emptyServiceConfig, indexName)
return newIndexInfo(emptyServiceConfig, indexName), nil
}

View File

@@ -97,7 +97,8 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use
func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
s.mu.RLock()
defer s.mu.RUnlock()
return newRepositoryInfo(s.config, name)
// TODO(thaJeztah): remove error return as it's no longer used.
return newRepositoryInfo(s.config, name), nil
}
// APIEndpoint represents a remote API endpoint