From 165cb39770ccbf8ff231c71f91b6c43884ecc317 Mon Sep 17 00:00:00 2001 From: Qi Wang Date: Thu, 24 Mar 2022 00:28:26 -0400 Subject: [PATCH] Add pull-from-mirror Add pull-from-mirror: all, digest-only, tag-only for adding per-mirror level restrictions to image pull through mirrors. Signed-off-by: Qi Wang --- docs/containers-registries.conf.5.md | 9 ++-- pkg/sysregistriesv2/system_registries_v2.go | 42 ++++++++++++---- .../system_registries_v2_test.go | 24 ++++++++-- .../pull-sources-mirror-reference.conf | 48 +++++++++++++++++-- 4 files changed, 105 insertions(+), 18 deletions(-) diff --git a/docs/containers-registries.conf.5.md b/docs/containers-registries.conf.5.md index 0f135874ce..36c174b70c 100644 --- a/docs/containers-registries.conf.5.md +++ b/docs/containers-registries.conf.5.md @@ -109,15 +109,16 @@ Each TOML table in the `mirror` array can contain the following fields: as specified in the `[[registry]]` TOML table - `insecure`: same semantics as specified in the `[[registry]]` TOML table -- `digest-only`: `true` or `false`. If `true` an individual mirror will only be used during pulling if the image reference includes a digest. -Note that digest-only will be overridden if mirror-by-digest-only is set for the entire registry. +- `pull-from-mirror`: `all`, `digest-only` or `tag-only`. If "digest-only", mirrors will only be used for digest pulls. Pulling images by tag can potentially yield different images, depending on which endpoint we pull from. Restricting mirrors to pulls by digest avoids that issue. If "tag-only", mirrors will only be used for tag pulls. For a more up-to-date and expensive mirror that it is less likely to be out of sync if tags move, it should not be unnecessarily used for digest references. Default is "all", mirrors will be used for both digest pulls and tag pulls unless the mirror-by-digest-only is set for the primary registry. +Note that pull-from-mirror can only be set in a registry's Mirror field, not in the registry's primary Endpoint. This per-mirror setting is allowed only when mirror-by-digest-only is not configured for the primary registry. `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 all mirrors are configured to be digest-only, images referenced by a tag will only use the primary -registry, failing if that registry is not accessible. +registry. +If all mirrors are configured to be tag-only, images referenced by a digest will only use the primary +registry. 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 diff --git a/pkg/sysregistriesv2/system_registries_v2.go b/pkg/sysregistriesv2/system_registries_v2.go index 9828c0d31c..02b85b53c5 100644 --- a/pkg/sysregistriesv2/system_registries_v2.go +++ b/pkg/sysregistriesv2/system_registries_v2.go @@ -43,6 +43,14 @@ const builtinRegistriesConfDirPath = "/etc/containers/registries.conf.d" // helper. const AuthenticationFileHelper = "containers-auth.json" +type PullFromMirror string + +const ( + MirrorAll PullFromMirror = "all" + MirrorByDigestOnly PullFromMirror = "digest-only" + MirrorByTagOnly PullFromMirror = "tag-only" +) + // Endpoint describes a remote location of a registry. type Endpoint struct { // The endpoint's remote location. Can be empty iff Prefix contains @@ -53,12 +61,18 @@ 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 + // PullFromMirror is used for adding restrictions to image pull through the mirror. + // Set to "all", "digest-only", or "tag-only". + // If "digest-only", mirrors will only be used for digest pulls. Pulling images by // tag can potentially yield different images, depending on which endpoint // we pull from. Restricting mirrors to pulls by digest avoids that issue. + // If "tag-only", mirrors will only be used for tag pulls. For a more up-to-date and expensive mirror + // that it is less likely to be out of sync if tags move, it should not be unnecessarily + // used for digest references. + // Default is "all", mirrors will be used for both digest pulls and tag pulls unless the mirror-by-digest-only is set for the primary registry . // This can only be set in a registry's Mirror field, not in the registry's primary Endpoint. - // If mirror-by-digest-only is set to true for the primary registry, this per-mirror setting is ignored. - DigestOnly bool `toml:"digest-only,omitempty"` + // This per-mirror setting is allowed only when mirror-by-digest-only is not configured for the primary registry. + PullFromMirror PullFromMirror `toml:"pull-from-mirror,omitempty"` } // userRegistriesFile is the path to the per user registry configuration file. @@ -143,11 +157,15 @@ func (r *Registry) PullSourcesFromReference(ref reference.Named) ([]PullSource, endpoints = append(endpoints, r.Mirrors...) } } else { - // 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 || isDigested { - endpoints = append(endpoints, mirror) + // skip the mirror if per mirror setting exists but reference does not match the restriction + if mirror.PullFromMirror == MirrorByDigestOnly && !isDigested { + continue + } + if mirror.PullFromMirror == MirrorByTagOnly && isDigested { + continue } + endpoints = append(endpoints, mirror) } } endpoints = append(endpoints, r.Endpoint) @@ -385,8 +403,8 @@ func (config *V2RegistriesConf) postProcessRegistries() error { } // validate the mirror usage settings does not apply to primary registry - if reg.DigestOnly { - return fmt.Errorf("digest-only must not be set for a non-mirror registry %s", reg.Prefix) + if reg.PullFromMirror != "" { + return fmt.Errorf("pull-from-mirror must not be set for a non-mirror registry %s", reg.Prefix) } // make sure mirrors are valid for _, mir := range reg.Mirrors { @@ -401,6 +419,14 @@ func (config *V2RegistriesConf) postProcessRegistries() error { if mir.Location == "" { return &InvalidRegistries{s: "invalid condition: mirror location is unset"} } + + if reg.MirrorByDigestOnly && mir.PullFromMirror != "" { + return &InvalidRegistries{s: "cannot set mirror usage mirror-by-digest-only for the registry and pull-from-mirror for per-mirror at the same time."} + } + if mir.PullFromMirror != "" && mir.PullFromMirror != MirrorAll && + mir.PullFromMirror != MirrorByDigestOnly && mir.PullFromMirror != MirrorByTagOnly { + return &InvalidRegistries{s: fmt.Sprintf("unsupported pull-from-mirror value %q for mirror %q", mir.PullFromMirror, mir.Location)} + } } if reg.Location == "" { regMap[reg.Prefix] = append(regMap[reg.Prefix], reg) diff --git a/pkg/sysregistriesv2/system_registries_v2_test.go b/pkg/sysregistriesv2/system_registries_v2_test.go index 856b9a39f9..df08335db6 100644 --- a/pkg/sysregistriesv2/system_registries_v2_test.go +++ b/pkg/sysregistriesv2/system_registries_v2_test.go @@ -678,7 +678,7 @@ func TestPullSourcesMirrorFromReference(t *testing.T) { } registries, err := GetRegistries(sys) require.NoError(t, err) - assert.Equal(t, 4, len(registries)) + assert.Equal(t, 7, len(registries)) digest := "@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" tag := ":aaa" @@ -705,13 +705,31 @@ func TestPullSourcesMirrorFromReference(t *testing.T) { []string{"mirror-1.registry-c.com", "mirror-2.registry-c.com", "registry-c.com/bar"}, []string{"mirror-1.registry-c.com", "registry-c.com/bar"}, }, - // Registry D set mirror-by-digest-only, honor this configuration for registry - // Regsitry D has no digest-only set for mirrors table + // Registry D set digest-only for registry level, allows only digest pulls + // Registry D has no digest-only set for mirrors table { "registry-d.com/foo/image", []string{"mirror-1.registry-d.com", "mirror-2.registry-d.com", "registry-d.com/bar"}, []string{"registry-d.com/bar"}, }, + // Registry E has mirrors only allows tag pull + { + "registry-e.com/foo/image", + []string{"registry-e.com/bar"}, + []string{"mirror-1.registry-e.com", "mirror-2.registry-e.com", "registry-e.com/bar"}, + }, + // Registry F has one tag only mirror does not allow digest pull + { + "registry-f.com/foo/image", + []string{"mirror-1.registry-f.com", "registry-f.com/bar"}, + []string{"mirror-1.registry-f.com", "mirror-2.registry-f.com", "registry-f.com/bar"}, + }, + // Registry G has one digest-only pull and one tag only pull + { + "registry-g.com/foo/image", + []string{"mirror-1.registry-g.com", "mirror-3.registry-g.com", "mirror-4.registry-g.com", "registry-g.com/bar"}, + []string{"mirror-2.registry-g.com", "mirror-3.registry-g.com", "mirror-4.registry-g.com", "registry-g.com/bar"}, + }, } { // Digest digestedRef := toNamedRef(t, tc.registry+digest) diff --git a/pkg/sysregistriesv2/testdata/pull-sources-mirror-reference.conf b/pkg/sysregistriesv2/testdata/pull-sources-mirror-reference.conf index 3682cb2921..50d51c2892 100644 --- a/pkg/sysregistriesv2/testdata/pull-sources-mirror-reference.conf +++ b/pkg/sysregistriesv2/testdata/pull-sources-mirror-reference.conf @@ -14,11 +14,11 @@ prefix = "registry-b.com/foo" location = "registry-b.com/bar" [[registry.mirror]] -digest-only = true +pull-from-mirror = "digest-only" location = "mirror-1.registry-b.com" [[registry.mirror]] -digest-only = true +pull-from-mirror = "digest-only" location = "mirror-2.registry-b.com" [[registry]] @@ -29,7 +29,7 @@ location = "registry-c.com/bar" location = "mirror-1.registry-c.com" [[registry.mirror]] -digest-only = true +pull-from-mirror = "digest-only" location = "mirror-2.registry-c.com" [[registry]] @@ -42,3 +42,45 @@ location = "mirror-1.registry-d.com" [[registry.mirror]] location = "mirror-2.registry-d.com" + +[[registry]] +prefix = "registry-e.com/foo" +location = "registry-e.com/bar" + +[[registry.mirror]] +pull-from-mirror = "tag-only" +location = "mirror-1.registry-e.com" + +[[registry.mirror]] +pull-from-mirror = "tag-only" +location = "mirror-2.registry-e.com" + +[[registry]] +prefix = "registry-f.com/foo" +location = "registry-f.com/bar" + +[[registry.mirror]] +location = "mirror-1.registry-f.com" + +[[registry.mirror]] +pull-from-mirror = "tag-only" +location = "mirror-2.registry-f.com" + +[[registry]] +prefix = "registry-g.com/foo" +location = "registry-g.com/bar" + +[[registry.mirror]] +pull-from-mirror = "digest-only" +location = "mirror-1.registry-g.com" + +[[registry.mirror]] +pull-from-mirror = "tag-only" +location = "mirror-2.registry-g.com" + +[[registry.mirror]] +location = "mirror-3.registry-g.com" + +[[registry.mirror]] +pull-from-mirror = "all" +location = "mirror-4.registry-g.com"