Skip to content

Commit

Permalink
set digest-only for each mirror
Browse files Browse the repository at this point in the history
Close: #1407
Add the `digest-only` field to each mirror table, so it will
be configured for each mirror instead of for primary registry.
For the same primary registry, it can support both digest only pull
and tag allowed pull.

The `mirror-by-digest-only` for primary is still allowed configuring,
and it is honored for compatibility

Signed-off-by: Qi Wang <qiwan@redhat.com>
  • Loading branch information
QiWang19 committed Nov 19, 2021
1 parent b55fb86 commit f1b55f8
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 9 deletions.
13 changes: 11 additions & 2 deletions docs/containers-registries.conf.5.md
Expand Up @@ -108,16 +108,25 @@ Each TOML table in the `mirror` array can contain the following fields, with the
as if specified in the `[[registry]]` TOML table directly:
- `location`
- `insecure`
- `digest-only`

`mirror-by-digest-only`
: `true` or `false`.
If `true`, mirrors will only be used during pulling if the image reference includes a digest.
Note that if this is `true`, images referenced by a tag will only use the primary
registry, failing if that registry is not accessible.

`digest-only`: `true` or `false`.
: for each mirror in the `mirror` array, the mirror has `digest-only` set to `true` will only be used during pulling if the image reference includes a digest.
If `mirror-by-digest-only` for primary registry is `true`,
it will be honored and apply to all mirrors, regardless of whether the `digest-only` is set for each mirror.
Note that if all mirrors in the `mirror` array have `digest-only = true`, images referenced by a tag will only use the primary
registry, failing if that registry is not accessible.

Referencing an image by digest ensures that the same is always used
(whereas referencing an image by a tag may cause different registries to return
different images if the tag mapping is out of sync).

Note that if this is `true`, images referenced by a tag will only use the primary
registry, failing if that registry is not accessible.

*Note*: Redirection and mirrors are currently processed only when reading images, not when pushing
to a registry; that may change in the future.
Expand Down
26 changes: 23 additions & 3 deletions pkg/sysregistriesv2/system_registries_v2.go
Expand Up @@ -53,6 +53,12 @@ type Endpoint struct {
// If true, certs verification will be skipped and HTTP (non-TLS)
// connections will be allowed.
Insecure bool `toml:"insecure,omitempty"`
// If true, the mirror will only be used for digest pulls. Pulling images by
// tag can potentially yield different images, depending on which endpoint
// we pull from. Forcing digest-pulls for mirrors avoids that issue.
// This only applies to the mirror of the primary registry.
// If mirror-by-digest-only is set for the primary registry, mirror-by-digest-only is honored.
DigestOnly bool `toml:"digest-only,omitempty"`
}

// userRegistriesFile is the path to the per user registry configuration file.
Expand Down Expand Up @@ -129,17 +135,31 @@ type PullSource struct {
// PullSourcesFromReference returns a slice of PullSource's based on the passed
// reference.
func (r *Registry) PullSourcesFromReference(ref reference.Named) ([]PullSource, error) {
if r.DigestOnly {
return nil, fmt.Errorf("digest-only does not apply to primary registry")
}
var endpoints []Endpoint

_, isDigested := ref.(reference.Canonical)
if r.MirrorByDigestOnly {
// Only use mirrors when the reference is a digest one.
if _, isDigested := ref.(reference.Canonical); isDigested {
if isDigested {
endpoints = append(r.Mirrors, r.Endpoint)
} else {
endpoints = []Endpoint{r.Endpoint}
}
} else {
endpoints = append(r.Mirrors, r.Endpoint)
// check digest-only for each mirror if non mirror-by-digest-only set, since mirror-by-digest-only is honored
for _, mirror := range r.Mirrors {
if mirror.DigestOnly {
if isDigested {
// Only use the mirror when the reference is a digest one.
endpoints = append(endpoints, mirror)
}
} else {
endpoints = append(endpoints, mirror)
}
}
endpoints = append(endpoints, r.Endpoint)
}

sources := []PullSource{}
Expand Down
39 changes: 36 additions & 3 deletions pkg/sysregistriesv2/system_registries_v2_test.go
Expand Up @@ -637,9 +637,9 @@ func TestPullSourcesFromReference(t *testing.T) {
}
registries, err := GetRegistries(sys)
assert.Nil(t, err)
assert.Equal(t, 2, len(registries))
assert.Equal(t, 4, len(registries))

// Registry A allowing any kind of pull from mirrors
// Registry A has mirrors allow any kind of pull
registryA, err := FindRegistry(sys, "registry-a.com/foo/image:latest")
assert.Nil(t, err)
assert.NotNil(t, registryA)
Expand All @@ -657,7 +657,7 @@ func TestPullSourcesFromReference(t *testing.T) {
assert.Equal(t, 3, len(pullSources))
assert.Equal(t, "registry-a.com/bar/image:aaa", pullSources[2].Reference.String())

// Registry B allowing digests pull only from mirrors
// Registry B has mirrors allow digests pull only
registryB, err := FindRegistry(sys, "registry-b.com/foo/image:latest")
assert.Nil(t, err)
assert.NotNil(t, registryB)
Expand All @@ -673,6 +673,39 @@ func TestPullSourcesFromReference(t *testing.T) {
pullSources, err = registryB.PullSourcesFromReference(referenceBTag)
assert.Nil(t, err)
assert.Equal(t, 1, len(pullSources))

// Registry C has a mirror allows digest pull only and a mirror allows any kind of pull
registryC, err := FindRegistry(sys, "registry-c.com/foo/image:latest")
assert.Nil(t, err)
assert.NotNil(t, registryB)
// Digest
referenceCDigest := toNamedRef(t, "registry-c.com/foo/image@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
pullSources, err = registryC.PullSourcesFromReference(referenceCDigest)
assert.Nil(t, err)
assert.Equal(t, 3, len(pullSources))
assert.Equal(t, "registry-c.com/bar", pullSources[2].Endpoint.Location)
assert.Equal(t, "registry-c.com/bar/image@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", pullSources[2].Reference.String())
// Tag
referenceCTag := toNamedRef(t, "registry-c.com/foo/image:aaa")
pullSources, err = registryC.PullSourcesFromReference(referenceCTag)
assert.Nil(t, err)
assert.Equal(t, 2, len(pullSources))
// Registry D set mirror-by-digest-only, honor this configuration for registry
// Regsitry D has no digest-only set for mirrors table
registryD, err := FindRegistry(sys, "registry-d.com/foo/image:latest")
assert.Nil(t, err)
assert.NotNil(t, registryA)
// Digest
referenceDDigest := toNamedRef(t, "registry-d.com/foo/image@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
pullSources, err = registryD.PullSourcesFromReference(referenceDDigest)
assert.Nil(t, err)
assert.Equal(t, 3, len(pullSources))
assert.Equal(t, "mirror-1.registry-d.com/image@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", pullSources[0].Reference.String())
// Tag
referenceDTag := toNamedRef(t, "registry-d.com/foo/image:aaa")
pullSources, err = registryD.PullSourcesFromReference(referenceDTag)
assert.Nil(t, err)
assert.Equal(t, 1, len(pullSources))
}

func TestTryUpdatingCache(t *testing.T) {
Expand Down
25 changes: 24 additions & 1 deletion pkg/sysregistriesv2/testdata/pull-sources-from-reference.conf
Expand Up @@ -12,10 +12,33 @@ insecure = true
[[registry]]
prefix = "registry-b.com/foo"
location = "registry-b.com/bar"
mirror-by-digest-only = true

[[registry.mirror]]
digest-only = true
location = "mirror-1.registry-b.com"

[[registry.mirror]]
digest-only = true
location = "mirror-2.registry-b.com"

[[registry]]
prefix = "registry-c.com/foo"
location = "registry-c.com/bar"

[[registry.mirror]]
location = "mirror-1.registry-c.com"

[[registry.mirror]]
digest-only = true
location = "mirror-2.registry-c.com"

[[registry]]
prefix = "registry-d.com/foo"
location = "registry-d.com/bar"
mirror-by-digest-only = true

[[registry.mirror]]
location = "mirror-1.registry-d.com"

[[registry.mirror]]
location = "mirror-2.registry-d.com"

0 comments on commit f1b55f8

Please sign in to comment.