Enable configuration of index dependency validation

Enable configuration options that can selectively disable validation
that dependencies exist within the registry before the image index
is uploaded.

This enables sparse indexes, where a registry holds a manifest index that
could be signed (so the digest must not change) but does not hold every
referenced image in the index. The use case for this is when a registry
mirror does not need to mirror all platforms, but does need to maintain
the digests of all manifests either because they are signed or because
they are pulled by digest.

The registry administrator can also select specific image architectures
that must exist in the registry, enabling a registry operator to select
only the platforms they care about and ensure all image indexes uploaded
to the registry are valid for those platforms.

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
This commit is contained in:
James Hewitt 2023-08-15 14:37:43 +01:00
parent 72dc264241
commit db80ebed2d
No known key found for this signature in database
GPG key ID: EA6C3C654B6193E4
10 changed files with 971 additions and 76 deletions

View file

@ -237,25 +237,7 @@ type Configuration struct {
} `yaml:"compatibility,omitempty"`
// Validation configures validation options for the registry.
Validation struct {
// Enabled enables the other options in this section. This field is
// deprecated in favor of Disabled.
Enabled bool `yaml:"enabled,omitempty"`
// Disabled disables the other options in this section.
Disabled bool `yaml:"disabled,omitempty"`
// Manifests configures manifest validation.
Manifests struct {
// URLs configures validation for URLs in pushed manifests.
URLs struct {
// Allow specifies regular expressions (https://godoc.org/regexp/syntax)
// that URLs in pushed manifests must match.
Allow []string `yaml:"allow,omitempty"`
// Deny specifies regular expressions (https://godoc.org/regexp/syntax)
// that URLs in pushed manifests must not match.
Deny []string `yaml:"deny,omitempty"`
} `yaml:"urls,omitempty"`
} `yaml:"manifests,omitempty"`
} `yaml:"validation,omitempty"`
Validation Validation `yaml:"validation,omitempty"`
// Policy configures registry policy options.
Policy struct {
@ -384,6 +366,13 @@ type Health struct {
} `yaml:"storagedriver,omitempty"`
}
type Platform struct {
// Architecture is the architecture for this platform
Architecture string `yaml:"architecture,omitempty"`
// OS is the operating system for this platform
OS string `yaml:"os,omitempty"`
}
// v0_1Configuration is a Version 0.1 Configuration struct
// This is currently aliased to Configuration, as it is the current version
type v0_1Configuration Configuration
@ -683,6 +672,40 @@ type Proxy struct {
TTL *time.Duration `yaml:"ttl,omitempty"`
}
type Validation struct {
// Enabled enables the other options in this section. This field is
// deprecated in favor of Disabled.
Enabled bool `yaml:"enabled,omitempty"`
// Disabled disables the other options in this section.
Disabled bool `yaml:"disabled,omitempty"`
// Manifests configures manifest validation.
Manifests ValidationManifests `yaml:"manifests,omitempty"`
}
type ValidationManifests struct {
// URLs configures validation for URLs in pushed manifests.
URLs struct {
// Allow specifies regular expressions (https://godoc.org/regexp/syntax)
// that URLs in pushed manifests must match.
Allow []string `yaml:"allow,omitempty"`
// Deny specifies regular expressions (https://godoc.org/regexp/syntax)
// that URLs in pushed manifests must not match.
Deny []string `yaml:"deny,omitempty"`
} `yaml:"urls,omitempty"`
// ImageIndex configures validation of image indexes
ImageIndex ValidationManifestsImageIndex `yaml:"imageindex,omitempty"`
}
type ValidationManifestsImageIndex struct {
Images struct {
// Exist validates that images referenced by indexes exist in the registry
// before the image index is accepted.
Exist *bool `yaml:"exist"`
// Platforms filters the set of platforms to validate for image existence.
Platforms []Platform `yaml:"platforms,omitempty"`
} `yaml:"images,omitempty"`
}
// Parse parses an input configuration yaml document into a Configuration struct
// This should generally be capable of handling old configuration format versions
//

View file

@ -16,6 +16,9 @@ import (
// Hook up gocheck into the "go test" runner
func Test(t *testing.T) { check.TestingT(t) }
// falseaddr is a boolean false to reference
var falseaddr = false
// configStruct is a canonical example configuration, which should map to configYamlV0_1
var configStruct = Configuration{
Version: "0.1",
@ -165,6 +168,18 @@ var configStruct = Configuration{
ReadTimeout: time.Millisecond * 10,
WriteTimeout: time.Millisecond * 10,
},
Validation: Validation{
Manifests: ValidationManifests{
ImageIndex: ValidationManifestsImageIndex{
Images: struct {
Exist *bool `yaml:"exist"`
Platforms []Platform `yaml:"platforms,omitempty"`
}{
Exist: &falseaddr,
},
},
},
},
}
// configYamlV0_1 is a Version 0.1 yaml document representing configStruct
@ -221,6 +236,11 @@ redis:
dialtimeout: 10ms
readtimeout: 10ms
writetimeout: 10ms
validation:
manifests:
imageindex:
images:
exist: false
`
// inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory
@ -250,6 +270,11 @@ notifications:
http:
headers:
X-Content-Type-Options: [nosniff]
validation:
manifests:
imageindex:
images:
exist: false
`
type ConfigSuite struct {
@ -342,6 +367,7 @@ func (suite *ConfigSuite) TestParseIncomplete(c *check.C) {
IdleTimeout time.Duration `yaml:"idletimeout,omitempty"`
} `yaml:"pool,omitempty"`
}{}
suite.expectedConfig.Validation.Manifests.ImageIndex.Images.Exist = nil
// Note: this also tests that REGISTRY_STORAGE and
// REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY can be used together
@ -636,6 +662,12 @@ func copyConfig(config Configuration) *Configuration {
}
configCopy.Redis = config.Redis
configCopy.Validation = Validation{
Enabled: config.Validation.Enabled,
Disabled: config.Validation.Disabled,
Manifests: config.Validation.Manifests,
}
return configCopy
}

570
coverage.txt Normal file
View file

@ -0,0 +1,570 @@
mode: atomic
mode: atomic
mode: atomic
mode: atomic
github.com/distribution/distribution/v3/configuration/configuration.go:350.80,353.16 3 46
github.com/distribution/distribution/v3/configuration/configuration.go:357.2,358.46 2 46
github.com/distribution/distribution/v3/configuration/configuration.go:362.2,362.46 1 46
github.com/distribution/distribution/v3/configuration/configuration.go:366.2,367.12 2 46
github.com/distribution/distribution/v3/configuration/configuration.go:353.16,355.3 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:358.46,360.3 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:362.46,364.3 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:380.82,383.16 3 23
github.com/distribution/distribution/v3/configuration/configuration.go:387.2,388.24 2 23
github.com/distribution/distribution/v3/configuration/configuration.go:394.2,395.12 2 21
github.com/distribution/distribution/v3/configuration/configuration.go:383.16,385.3 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:389.40,389.40 0 21
github.com/distribution/distribution/v3/configuration/configuration.go:390.10,391.101 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:405.38,409.25 2 241
github.com/distribution/distribution/v3/configuration/configuration.go:423.2,423.26 1 241
github.com/distribution/distribution/v3/configuration/configuration.go:426.2,426.27 1 241
github.com/distribution/distribution/v3/configuration/configuration.go:429.2,429.11 1 1
github.com/distribution/distribution/v3/configuration/configuration.go:409.25,410.12 1 241
github.com/distribution/distribution/v3/configuration/configuration.go:411.22,411.22 0 1
github.com/distribution/distribution/v3/configuration/configuration.go:413.16,413.16 0 0
github.com/distribution/distribution/v3/configuration/configuration.go:415.17,415.17 0 0
github.com/distribution/distribution/v3/configuration/configuration.go:417.19,417.19 0 0
github.com/distribution/distribution/v3/configuration/configuration.go:419.11,420.40 1 240
github.com/distribution/distribution/v3/configuration/configuration.go:423.26,424.114 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:426.27,428.3 1 240
github.com/distribution/distribution/v3/configuration/configuration.go:433.48,435.2 1 22
github.com/distribution/distribution/v3/configuration/configuration.go:438.68,440.2 1 184
github.com/distribution/distribution/v3/configuration/configuration.go:444.80,447.16 3 22
github.com/distribution/distribution/v3/configuration/configuration.go:473.2,475.16 3 6
github.com/distribution/distribution/v3/configuration/configuration.go:480.2,480.12 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:447.16,448.26 1 16
github.com/distribution/distribution/v3/configuration/configuration.go:469.3,470.13 2 16
github.com/distribution/distribution/v3/configuration/configuration.go:448.26,450.30 2 0
github.com/distribution/distribution/v3/configuration/configuration.go:465.4,465.22 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:450.30,451.14 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:452.24,452.24 0 0
github.com/distribution/distribution/v3/configuration/configuration.go:454.18,454.18 0 0
github.com/distribution/distribution/v3/configuration/configuration.go:456.19,456.19 0 0
github.com/distribution/distribution/v3/configuration/configuration.go:458.21,458.21 0 0
github.com/distribution/distribution/v3/configuration/configuration.go:460.13,461.30 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:465.22,467.5 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:475.16,478.3 2 6
github.com/distribution/distribution/v3/configuration/configuration.go:484.59,485.33 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:488.2,488.44 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:485.33,487.3 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:495.32,497.22 1 82
github.com/distribution/distribution/v3/configuration/configuration.go:500.2,500.11 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:497.22,499.3 1 82
github.com/distribution/distribution/v3/configuration/configuration.go:504.42,506.2 1 22
github.com/distribution/distribution/v3/configuration/configuration.go:509.62,511.2 1 40
github.com/distribution/distribution/v3/configuration/configuration.go:515.74,518.16 3 18
github.com/distribution/distribution/v3/configuration/configuration.go:534.2,536.16 3 1
github.com/distribution/distribution/v3/configuration/configuration.go:541.2,541.12 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:518.16,519.17 1 17
github.com/distribution/distribution/v3/configuration/configuration.go:530.3,531.13 2 17
github.com/distribution/distribution/v3/configuration/configuration.go:519.17,521.21 2 0
github.com/distribution/distribution/v3/configuration/configuration.go:527.4,527.75 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:521.21,523.5 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:536.16,539.3 2 1
github.com/distribution/distribution/v3/configuration/configuration.go:545.53,546.30 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:549.2,549.41 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:546.30,548.3 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:679.50,681.16 2 21
github.com/distribution/distribution/v3/configuration/configuration.go:685.2,689.61 1 21
github.com/distribution/distribution/v3/configuration/configuration.go:711.2,713.16 3 21
github.com/distribution/distribution/v3/configuration/configuration.go:717.2,717.20 1 14
github.com/distribution/distribution/v3/configuration/configuration.go:681.16,683.3 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:689.61,690.47 1 15
github.com/distribution/distribution/v3/configuration/configuration.go:706.5,706.75 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:690.47,691.40 1 15
github.com/distribution/distribution/v3/configuration/configuration.go:698.6,698.39 1 15
github.com/distribution/distribution/v3/configuration/configuration.go:701.6,701.35 1 15
github.com/distribution/distribution/v3/configuration/configuration.go:704.6,704.40 1 14
github.com/distribution/distribution/v3/configuration/configuration.go:691.40,692.40 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:692.40,694.8 1 0
github.com/distribution/distribution/v3/configuration/configuration.go:694.13,696.8 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:698.39,700.7 1 2
github.com/distribution/distribution/v3/configuration/configuration.go:701.35,703.7 1 1
github.com/distribution/distribution/v3/configuration/configuration.go:713.16,715.3 1 7
github.com/distribution/distribution/v3/configuration/parser.go:21.51,23.2 1 43
github.com/distribution/distribution/v3/configuration/parser.go:25.46,29.2 3 67
github.com/distribution/distribution/v3/configuration/parser.go:32.37,35.2 2 21
github.com/distribution/distribution/v3/configuration/parser.go:37.46,41.2 3 67
github.com/distribution/distribution/v3/configuration/parser.go:44.37,47.2 2 21
github.com/distribution/distribution/v3/configuration/parser.go:70.38,70.55 1 23
github.com/distribution/distribution/v3/configuration/parser.go:71.38,71.65 1 30
github.com/distribution/distribution/v3/configuration/parser.go:72.38,72.70 1 48
github.com/distribution/distribution/v3/configuration/parser.go:84.72,87.39 2 23
github.com/distribution/distribution/v3/configuration/parser.go:91.2,91.35 1 23
github.com/distribution/distribution/v3/configuration/parser.go:102.2,104.11 2 23
github.com/distribution/distribution/v3/configuration/parser.go:87.39,89.3 1 23
github.com/distribution/distribution/v3/configuration/parser.go:91.35,94.3 2 39
github.com/distribution/distribution/v3/configuration/parser.go:114.56,119.61 2 23
github.com/distribution/distribution/v3/configuration/parser.go:123.2,124.9 2 23
github.com/distribution/distribution/v3/configuration/parser.go:128.2,130.16 3 22
github.com/distribution/distribution/v3/configuration/parser.go:134.2,134.31 1 21
github.com/distribution/distribution/v3/configuration/parser.go:146.2,147.16 2 17
github.com/distribution/distribution/v3/configuration/parser.go:150.2,151.12 2 16
github.com/distribution/distribution/v3/configuration/parser.go:119.61,121.3 1 0
github.com/distribution/distribution/v3/configuration/parser.go:124.9,126.3 1 1
github.com/distribution/distribution/v3/configuration/parser.go:130.16,132.3 1 1
github.com/distribution/distribution/v3/configuration/parser.go:134.31,136.64 2 39
github.com/distribution/distribution/v3/configuration/parser.go:136.64,140.18 3 37
github.com/distribution/distribution/v3/configuration/parser.go:140.18,142.5 1 4
github.com/distribution/distribution/v3/configuration/parser.go:147.16,149.3 1 1
github.com/distribution/distribution/v3/configuration/parser.go:157.105,158.30 1 78
github.com/distribution/distribution/v3/configuration/parser.go:164.2,164.18 1 78
github.com/distribution/distribution/v3/configuration/parser.go:181.2,181.12 1 0
github.com/distribution/distribution/v3/configuration/parser.go:158.30,159.16 1 40
github.com/distribution/distribution/v3/configuration/parser.go:162.3,162.26 1 40
github.com/distribution/distribution/v3/configuration/parser.go:159.16,160.84 1 0
github.com/distribution/distribution/v3/configuration/parser.go:165.22,166.55 1 56
github.com/distribution/distribution/v3/configuration/parser.go:167.19,168.52 1 21
github.com/distribution/distribution/v3/configuration/parser.go:169.25,170.25 1 1
github.com/distribution/distribution/v3/configuration/parser.go:170.25,171.18 1 1
github.com/distribution/distribution/v3/configuration/parser.go:175.4,178.60 4 1
github.com/distribution/distribution/v3/configuration/parser.go:171.18,173.5 1 0
github.com/distribution/distribution/v3/configuration/parser.go:184.105,187.36 2 56
github.com/distribution/distribution/v3/configuration/parser.go:196.2,197.14 2 56
github.com/distribution/distribution/v3/configuration/parser.go:201.2,204.20 3 54
github.com/distribution/distribution/v3/configuration/parser.go:217.2,217.24 1 31
github.com/distribution/distribution/v3/configuration/parser.go:228.2,229.16 2 31
github.com/distribution/distribution/v3/configuration/parser.go:233.2,233.12 1 28
github.com/distribution/distribution/v3/configuration/parser.go:187.36,190.48 3 588
github.com/distribution/distribution/v3/configuration/parser.go:193.3,193.25 1 588
github.com/distribution/distribution/v3/configuration/parser.go:190.48,191.83 1 0
github.com/distribution/distribution/v3/configuration/parser.go:197.14,200.3 2 2
github.com/distribution/distribution/v3/configuration/parser.go:204.20,208.17 3 23
github.com/distribution/distribution/v3/configuration/parser.go:212.3,213.13 2 21
github.com/distribution/distribution/v3/configuration/parser.go:208.17,211.4 2 2
github.com/distribution/distribution/v3/configuration/parser.go:218.19,219.20 1 12
github.com/distribution/distribution/v3/configuration/parser.go:222.19,223.20 1 2
github.com/distribution/distribution/v3/configuration/parser.go:219.20,221.4 1 0
github.com/distribution/distribution/v3/configuration/parser.go:223.20,225.4 1 1
github.com/distribution/distribution/v3/configuration/parser.go:229.16,231.3 1 3
github.com/distribution/distribution/v3/configuration/parser.go:236.102,237.45 1 22
github.com/distribution/distribution/v3/configuration/parser.go:243.2,243.19 1 22
github.com/distribution/distribution/v3/configuration/parser.go:263.2,264.43 2 14
github.com/distribution/distribution/v3/configuration/parser.go:269.2,269.19 1 14
github.com/distribution/distribution/v3/configuration/parser.go:281.2,283.12 2 12
github.com/distribution/distribution/v3/configuration/parser.go:237.45,241.3 2 0
github.com/distribution/distribution/v3/configuration/parser.go:243.19,246.33 1 10
github.com/distribution/distribution/v3/configuration/parser.go:246.33,247.46 1 10
github.com/distribution/distribution/v3/configuration/parser.go:247.46,254.23 2 8
github.com/distribution/distribution/v3/configuration/parser.go:257.5,257.68 1 8
github.com/distribution/distribution/v3/configuration/parser.go:254.23,255.11 1 0
github.com/distribution/distribution/v3/configuration/parser.go:264.43,266.3 1 4
github.com/distribution/distribution/v3/configuration/parser.go:266.8,268.3 1 10
github.com/distribution/distribution/v3/configuration/parser.go:269.19,271.17 2 2
github.com/distribution/distribution/v3/configuration/parser.go:271.17,273.4 1 0
github.com/distribution/distribution/v3/configuration/parser.go:274.8,276.17 2 12
github.com/distribution/distribution/v3/configuration/parser.go:276.17,278.4 1 2
mode: atomic
github.com/distribution/distribution/v3/context/util.go:11.64,12.53 1 0
github.com/distribution/distribution/v3/context/util.go:15.2,15.10 1 0
github.com/distribution/distribution/v3/context/util.go:12.53,14.3 1 0
github.com/distribution/distribution/v3/context/util.go:20.74,21.47 1 4
github.com/distribution/distribution/v3/context/util.go:24.2,24.14 1 4
github.com/distribution/distribution/v3/context/util.go:21.47,23.3 1 2
github.com/distribution/distribution/v3/context/version.go:7.35,7.55 1 1
github.com/distribution/distribution/v3/context/version.go:12.71,16.2 2 1
github.com/distribution/distribution/v3/context/version.go:20.45,22.2 1 2
github.com/distribution/distribution/v3/context/context.go:18.63,19.26 1 9
github.com/distribution/distribution/v3/context/context.go:30.2,30.30 1 6
github.com/distribution/distribution/v3/context/context.go:19.26,20.21 1 3
github.com/distribution/distribution/v3/context/context.go:27.3,27.15 1 3
github.com/distribution/distribution/v3/context/context.go:20.21,26.4 1 1
github.com/distribution/distribution/v3/context/context.go:40.35,42.2 1 5
github.com/distribution/distribution/v3/context/context.go:53.80,55.22 2 0
github.com/distribution/distribution/v3/context/context.go:59.2,62.3 1 0
github.com/distribution/distribution/v3/context/context.go:55.22,57.3 1 0
github.com/distribution/distribution/v3/context/context.go:65.64,66.32 1 0
github.com/distribution/distribution/v3/context/context.go:72.2,72.31 1 0
github.com/distribution/distribution/v3/context/context.go:66.32,67.29 1 0
github.com/distribution/distribution/v3/context/context.go:67.29,69.4 1 0
github.com/distribution/distribution/v3/context/http.go:23.35,25.15 2 4
github.com/distribution/distribution/v3/context/http.go:28.2,28.11 1 4
github.com/distribution/distribution/v3/context/http.go:25.15,27.3 1 1
github.com/distribution/distribution/v3/context/http.go:33.41,34.59 1 4
github.com/distribution/distribution/v3/context/http.go:45.2,45.55 1 3
github.com/distribution/distribution/v3/context/http.go:51.2,51.21 1 1
github.com/distribution/distribution/v3/context/http.go:34.59,36.23 2 2
github.com/distribution/distribution/v3/context/http.go:36.23,38.34 2 2
github.com/distribution/distribution/v3/context/http.go:38.34,40.5 1 1
github.com/distribution/distribution/v3/context/http.go:45.55,46.29 1 2
github.com/distribution/distribution/v3/context/http.go:46.29,48.4 1 2
github.com/distribution/distribution/v3/context/http.go:56.39,60.55 2 0
github.com/distribution/distribution/v3/context/http.go:64.2,64.13 1 0
github.com/distribution/distribution/v3/context/http.go:60.55,62.3 1 0
github.com/distribution/distribution/v3/context/http.go:72.72,73.38 1 1
github.com/distribution/distribution/v3/context/http.go:80.2,85.3 1 1
github.com/distribution/distribution/v3/context/http.go:73.38,77.40 1 0
github.com/distribution/distribution/v3/context/http.go:91.61,92.72 1 0
github.com/distribution/distribution/v3/context/http.go:95.2,95.33 1 0
github.com/distribution/distribution/v3/context/http.go:92.72,94.3 1 0
github.com/distribution/distribution/v3/context/http.go:100.47,102.2 1 0
github.com/distribution/distribution/v3/context/http.go:106.108,112.2 2 1
github.com/distribution/distribution/v3/context/http.go:117.74,121.22 3 1
github.com/distribution/distribution/v3/context/http.go:125.2,125.16 1 1
github.com/distribution/distribution/v3/context/http.go:121.22,123.3 1 0
github.com/distribution/distribution/v3/context/http.go:137.69,142.2 1 1
github.com/distribution/distribution/v3/context/http.go:147.51,157.2 1 0
github.com/distribution/distribution/v3/context/http.go:163.52,171.18 3 0
github.com/distribution/distribution/v3/context/http.go:175.2,175.10 1 0
github.com/distribution/distribution/v3/context/http.go:171.18,173.3 1 0
github.com/distribution/distribution/v3/context/http.go:190.67,191.36 1 9
github.com/distribution/distribution/v3/context/http.go:235.2,235.31 1 0
github.com/distribution/distribution/v3/context/http.go:191.36,192.31 1 9
github.com/distribution/distribution/v3/context/http.go:196.3,196.50 1 8
github.com/distribution/distribution/v3/context/http.go:200.3,202.22 2 8
github.com/distribution/distribution/v3/context/http.go:206.3,206.19 1 8
github.com/distribution/distribution/v3/context/http.go:192.31,194.4 1 1
github.com/distribution/distribution/v3/context/http.go:196.50,197.17 1 0
github.com/distribution/distribution/v3/context/http.go:202.22,203.17 1 0
github.com/distribution/distribution/v3/context/http.go:207.14,208.27 1 1
github.com/distribution/distribution/v3/context/http.go:209.21,210.28 1 1
github.com/distribution/distribution/v3/context/http.go:211.17,212.23 1 1
github.com/distribution/distribution/v3/context/http.go:213.15,214.21 1 1
github.com/distribution/distribution/v3/context/http.go:215.18,217.21 2 1
github.com/distribution/distribution/v3/context/http.go:220.20,221.28 1 1
github.com/distribution/distribution/v3/context/http.go:222.13,223.17 1 1
github.com/distribution/distribution/v3/context/http.go:224.20,225.24 1 1
github.com/distribution/distribution/v3/context/http.go:226.22,228.16 2 0
github.com/distribution/distribution/v3/context/http.go:217.21,219.5 1 1
github.com/distribution/distribution/v3/context/http.go:228.16,230.5 1 0
github.com/distribution/distribution/v3/context/http.go:243.63,244.36 1 3
github.com/distribution/distribution/v3/context/http.go:256.2,256.31 1 0
github.com/distribution/distribution/v3/context/http.go:244.36,245.23 1 3
github.com/distribution/distribution/v3/context/http.go:249.3,251.36 2 2
github.com/distribution/distribution/v3/context/http.go:245.23,247.4 1 1
github.com/distribution/distribution/v3/context/http.go:251.36,253.4 1 2
github.com/distribution/distribution/v3/context/http.go:271.75,278.21 4 1
github.com/distribution/distribution/v3/context/http.go:282.2,284.8 2 1
github.com/distribution/distribution/v3/context/http.go:278.21,280.3 1 1
github.com/distribution/distribution/v3/context/http.go:287.64,293.2 4 1
github.com/distribution/distribution/v3/context/http.go:295.48,296.58 1 1
github.com/distribution/distribution/v3/context/http.go:296.58,298.3 1 1
github.com/distribution/distribution/v3/context/http.go:301.75,302.36 1 6
github.com/distribution/distribution/v3/context/http.go:334.2,334.31 1 0
github.com/distribution/distribution/v3/context/http.go:302.36,303.32 1 6
github.com/distribution/distribution/v3/context/http.go:307.3,307.51 1 4
github.com/distribution/distribution/v3/context/http.go:311.3,313.22 2 4
github.com/distribution/distribution/v3/context/http.go:317.3,320.19 3 4
github.com/distribution/distribution/v3/context/http.go:303.32,305.4 1 2
github.com/distribution/distribution/v3/context/http.go:307.51,308.17 1 0
github.com/distribution/distribution/v3/context/http.go:313.22,314.17 1 0
github.com/distribution/distribution/v3/context/http.go:321.18,322.22 1 1
github.com/distribution/distribution/v3/context/http.go:323.17,324.21 1 3
github.com/distribution/distribution/v3/context/http.go:325.22,327.25 2 0
github.com/distribution/distribution/v3/context/http.go:327.25,329.5 1 0
github.com/distribution/distribution/v3/context/logger.go:55.69,57.2 1 1
github.com/distribution/distribution/v3/context/logger.go:62.98,64.2 1 0
github.com/distribution/distribution/v3/context/logger.go:69.111,72.33 2 0
github.com/distribution/distribution/v3/context/logger.go:76.2,76.58 1 0
github.com/distribution/distribution/v3/context/logger.go:72.33,74.3 1 0
github.com/distribution/distribution/v3/context/logger.go:85.65,87.2 1 3
github.com/distribution/distribution/v3/context/logger.go:90.38,92.9 2 0
github.com/distribution/distribution/v3/context/logger.go:96.2,98.26 3 0
github.com/distribution/distribution/v3/context/logger.go:92.9,94.3 1 0
github.com/distribution/distribution/v3/context/logger.go:105.78,110.28 3 3
github.com/distribution/distribution/v3/context/logger.go:116.2,116.19 1 3
github.com/distribution/distribution/v3/context/logger.go:130.2,131.27 2 3
github.com/distribution/distribution/v3/context/logger.go:138.2,138.34 1 3
github.com/distribution/distribution/v3/context/logger.go:110.28,111.53 1 0
github.com/distribution/distribution/v3/context/logger.go:111.53,113.4 1 0
github.com/distribution/distribution/v3/context/logger.go:116.19,121.24 3 3
github.com/distribution/distribution/v3/context/logger.go:125.3,127.28 3 3
github.com/distribution/distribution/v3/context/logger.go:121.24,123.4 1 3
github.com/distribution/distribution/v3/context/logger.go:131.27,133.15 2 13
github.com/distribution/distribution/v3/context/logger.go:133.15,135.4 1 12
github.com/distribution/distribution/v3/context/trace.go:40.94,41.16 1 2
github.com/distribution/distribution/v3/context/trace.go:45.2,57.52 4 2
github.com/distribution/distribution/v3/context/trace.go:41.16,43.3 1 0
github.com/distribution/distribution/v3/context/trace.go:57.52,66.3 1 2
github.com/distribution/distribution/v3/context/trace.go:82.54,83.13 1 31
github.com/distribution/distribution/v3/context/trace.go:104.2,104.30 1 6
github.com/distribution/distribution/v3/context/trace.go:84.21,85.18 1 2
github.com/distribution/distribution/v3/context/trace.go:86.24,87.30 1 2
github.com/distribution/distribution/v3/context/trace.go:88.18,89.15 1 6
github.com/distribution/distribution/v3/context/trace.go:90.25,91.22 1 3
github.com/distribution/distribution/v3/context/trace.go:95.3,95.19 1 2
github.com/distribution/distribution/v3/context/trace.go:96.20,97.19 1 4
github.com/distribution/distribution/v3/context/trace.go:98.20,99.17 1 4
github.com/distribution/distribution/v3/context/trace.go:100.20,101.17 1 4
github.com/distribution/distribution/v3/context/trace.go:91.22,93.4 1 1
mode: atomic
github.com/distribution/distribution/v3/contrib/token-server/main.go:27.13,57.11 12 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:61.2,61.18 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:75.2,75.17 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:79.2,83.16 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:88.2,102.16 7 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:110.2,110.16 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:57.11,59.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:61.18,63.17 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:66.3,66.83 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:63.17,65.4 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:67.8,69.17 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:72.3,72.76 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:69.17,71.4 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:75.17,77.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:83.16,85.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:102.16,104.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:104.8,104.26 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:104.26,106.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:106.8,108.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:110.16,112.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:118.126,119.71 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:119.71,125.3 4 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:128.73,131.60 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:136.2,136.59 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:131.60,134.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:143.31,146.19 3 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:155.2,155.18 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:146.19,149.17 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:153.3,153.44 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:149.17,152.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:177.107,178.36 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:181.2,182.45 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:210.2,210.26 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:178.36,180.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:182.45,183.34 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:208.3,208.56 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:183.34,184.46 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:188.4,188.24 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:184.46,186.13 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:188.24,189.59 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:189.59,190.31 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:190.31,192.15 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:194.11,194.56 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:194.56,196.6 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:198.9,198.39 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:198.39,199.32 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:199.32,201.13 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:204.9,206.12 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:215.36,215.60 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:219.40,219.68 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:223.38,223.64 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:227.94,234.65 6 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:243.2,246.16 3 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:263.2,280.16 12 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:285.2,292.13 3 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:300.2,305.60 4 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:234.65,237.17 3 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:237.17,240.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:246.16,248.10 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:254.3,261.9 5 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:248.10,251.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:280.16,283.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:292.13,298.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:318.95,320.21 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:325.2,326.19 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:331.2,332.20 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:337.2,338.40 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:347.2,351.19 4 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:389.2,402.16 10 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:407.2,416.13 3 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:424.2,424.18 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:428.2,433.61 4 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:320.21,323.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:326.19,329.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:332.20,335.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:339.20,339.20 0 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:340.17,341.17 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:342.10,344.9 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:352.23,354.19 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:358.3,359.35 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:363.3,363.23 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:364.18,366.10 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:370.3,371.20 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:375.3,376.21 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:380.3,380.64 1 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:384.10,386.9 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:354.19,357.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:359.35,362.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:366.10,369.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:371.20,374.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:376.21,379.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:380.64,383.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:402.16,405.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:416.13,422.3 2 0
github.com/distribution/distribution/v3/contrib/token-server/main.go:424.18,426.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:23.85,26.44 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:58.2,59.50 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:63.2,63.28 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:26.44,30.22 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:35.3,38.25 3 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:43.3,43.54 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:30.22,32.12 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:38.25,39.12 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:43.54,55.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:59.50,61.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:68.52,70.22 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:73.2,73.46 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:76.2,76.54 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:70.22,72.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:73.46,75.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:81.76,84.2 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:86.40,87.19 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:90.2,90.58 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:87.19,89.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:95.47,97.27 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:100.2,100.29 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:97.27,99.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:112.120,115.43 2 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:124.2,125.54 2 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:139.2,141.16 3 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:144.2,150.37 5 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:159.2,164.66 2 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:175.2,176.14 2 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:180.2,197.65 3 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:200.2,200.61 1 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:204.2,209.113 5 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:213.2,215.61 2 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:115.43,117.14 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:121.3,121.40 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:117.14,120.4 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:125.54,127.33 2 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:131.3,136.5 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:127.33,129.4 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:141.16,143.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:151.13,152.16 1 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:153.12,154.16 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:155.10,156.84 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:164.66,166.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:166.8,169.17 3 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:172.3,172.34 1 1
github.com/distribution/distribution/v3/contrib/token-server/token.go:169.17,171.4 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:176.14,178.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:197.65,199.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:200.61,202.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:209.113,211.3 1 0
github.com/distribution/distribution/v3/contrib/token-server/token.go:218.43,220.2 1 3
mode: atomic
github.com/distribution/distribution/v3/digestset/set.go:39.20,43.2 1 5
github.com/distribution/distribution/v3/digestset/set.go:49.81,50.31 1 35
github.com/distribution/distribution/v3/digestset/set.go:62.2,62.13 1 19
github.com/distribution/distribution/v3/digestset/set.go:50.31,51.22 1 2
github.com/distribution/distribution/v3/digestset/set.go:54.3,54.51 1 1
github.com/distribution/distribution/v3/digestset/set.go:51.22,53.4 1 1
github.com/distribution/distribution/v3/digestset/set.go:54.51,56.4 1 0
github.com/distribution/distribution/v3/digestset/set.go:57.8,57.46 1 33
github.com/distribution/distribution/v3/digestset/set.go:57.46,59.3 1 15
github.com/distribution/distribution/v3/digestset/set.go:59.8,59.57 1 18
github.com/distribution/distribution/v3/digestset/set.go:59.57,61.3 1 0
github.com/distribution/distribution/v3/digestset/set.go:69.57,72.27 3 10
github.com/distribution/distribution/v3/digestset/set.go:75.2,81.42 3 10
github.com/distribution/distribution/v3/digestset/set.go:96.2,97.111 2 10
github.com/distribution/distribution/v3/digestset/set.go:100.2,100.64 1 8
github.com/distribution/distribution/v3/digestset/set.go:103.2,103.115 1 7
github.com/distribution/distribution/v3/digestset/set.go:107.2,107.37 1 5
github.com/distribution/distribution/v3/digestset/set.go:72.27,74.3 1 0
github.com/distribution/distribution/v3/digestset/set.go:81.42,83.33 2 5
github.com/distribution/distribution/v3/digestset/set.go:83.33,85.4 1 17
github.com/distribution/distribution/v3/digestset/set.go:86.8,89.33 3 5
github.com/distribution/distribution/v3/digestset/set.go:89.33,90.33 1 18
github.com/distribution/distribution/v3/digestset/set.go:93.4,93.36 1 17
github.com/distribution/distribution/v3/digestset/set.go:90.33,92.5 1 1
github.com/distribution/distribution/v3/digestset/set.go:97.111,99.3 1 2
github.com/distribution/distribution/v3/digestset/set.go:100.64,102.3 1 1
github.com/distribution/distribution/v3/digestset/set.go:103.115,105.3 1 2
github.com/distribution/distribution/v3/digestset/set.go:113.44,114.37 1 136
github.com/distribution/distribution/v3/digestset/set.go:117.2,120.33 4 136
github.com/distribution/distribution/v3/digestset/set.go:126.2,127.29 2 136
github.com/distribution/distribution/v3/digestset/set.go:134.2,138.12 5 110
github.com/distribution/distribution/v3/digestset/set.go:114.37,116.3 1 0
github.com/distribution/distribution/v3/digestset/set.go:120.33,121.38 1 605
github.com/distribution/distribution/v3/digestset/set.go:124.3,124.41 1 604
github.com/distribution/distribution/v3/digestset/set.go:121.38,123.4 1 1
github.com/distribution/distribution/v3/digestset/set.go:127.29,130.3 2 25
github.com/distribution/distribution/v3/digestset/set.go:130.8,130.41 1 111
github.com/distribution/distribution/v3/digestset/set.go:130.41,132.3 1 1
github.com/distribution/distribution/v3/digestset/set.go:144.47,145.37 1 1
github.com/distribution/distribution/v3/digestset/set.go:148.2,151.33 4 1
github.com/distribution/distribution/v3/digestset/set.go:157.2,159.61 2 1
github.com/distribution/distribution/v3/digestset/set.go:163.2,168.12 5 1
github.com/distribution/distribution/v3/digestset/set.go:145.37,147.3 1 0
github.com/distribution/distribution/v3/digestset/set.go:151.33,152.38 1 3
github.com/distribution/distribution/v3/digestset/set.go:155.3,155.41 1 2
github.com/distribution/distribution/v3/digestset/set.go:152.38,154.4 1 1
github.com/distribution/distribution/v3/digestset/set.go:159.61,161.3 1 0
github.com/distribution/distribution/v3/digestset/set.go:172.39,176.29 4 1
github.com/distribution/distribution/v3/digestset/set.go:180.2,180.18 1 1
github.com/distribution/distribution/v3/digestset/set.go:176.29,178.3 1 100
github.com/distribution/distribution/v3/digestset/set.go:188.68,194.40 6 1
github.com/distribution/distribution/v3/digestset/set.go:223.2,223.10 1 1
github.com/distribution/distribution/v3/digestset/set.go:194.40,197.16 3 8
github.com/distribution/distribution/v3/digestset/set.go:218.3,219.20 2 8
github.com/distribution/distribution/v3/digestset/set.go:197.16,199.36 2 13
github.com/distribution/distribution/v3/digestset/set.go:199.36,201.5 1 0
github.com/distribution/distribution/v3/digestset/set.go:201.10,203.47 2 13
github.com/distribution/distribution/v3/digestset/set.go:213.5,213.17 1 13
github.com/distribution/distribution/v3/digestset/set.go:203.47,204.76 1 19
github.com/distribution/distribution/v3/digestset/set.go:204.76,205.23 1 9
github.com/distribution/distribution/v3/digestset/set.go:208.7,208.22 1 9
github.com/distribution/distribution/v3/digestset/set.go:205.23,207.8 1 4
github.com/distribution/distribution/v3/digestset/set.go:209.12,210.12 1 10
github.com/distribution/distribution/v3/digestset/set.go:213.17,215.6 1 5
github.com/distribution/distribution/v3/digestset/set.go:219.20,221.4 1 4
github.com/distribution/distribution/v3/digestset/set.go:234.34,236.2 1 0
github.com/distribution/distribution/v3/digestset/set.go:238.44,239.26 1 0
github.com/distribution/distribution/v3/digestset/set.go:242.2,242.28 1 0
github.com/distribution/distribution/v3/digestset/set.go:239.26,241.3 1 0
github.com/distribution/distribution/v3/digestset/set.go:245.39,247.2 1 0
mode: atomic
github.com/distribution/distribution/v3/health/health.go:25.30,29.2 1 2
github.com/distribution/distribution/v3/health/health.go:47.35,49.2 1 1
github.com/distribution/distribution/v3/health/health.go:69.33,74.2 3 3
github.com/distribution/distribution/v3/health/health.go:78.40,83.2 3 2
github.com/distribution/distribution/v3/health/health.go:86.33,88.2 1 1
github.com/distribution/distribution/v3/health/health.go:102.43,106.30 3 0
github.com/distribution/distribution/v3/health/health.go:110.2,110.12 1 0
github.com/distribution/distribution/v3/health/health.go:106.30,108.3 1 0
github.com/distribution/distribution/v3/health/health.go:115.50,119.19 3 0
github.com/distribution/distribution/v3/health/health.go:125.2,125.20 1 0
github.com/distribution/distribution/v3/health/health.go:119.19,121.3 1 0
github.com/distribution/distribution/v3/health/health.go:121.8,121.36 1 0
github.com/distribution/distribution/v3/health/health.go:121.36,123.3 1 0
github.com/distribution/distribution/v3/health/health.go:129.47,131.2 1 0
github.com/distribution/distribution/v3/health/health.go:134.67,136.12 2 0
github.com/distribution/distribution/v3/health/health.go:145.2,145.10 1 0
github.com/distribution/distribution/v3/health/health.go:136.12,139.7 3 0
github.com/distribution/distribution/v3/health/health.go:139.7,142.4 2 0
github.com/distribution/distribution/v3/health/health.go:150.91,152.12 2 0
github.com/distribution/distribution/v3/health/health.go:161.2,161.11 1 0
github.com/distribution/distribution/v3/health/health.go:152.12,155.7 3 0
github.com/distribution/distribution/v3/health/health.go:155.7,158.4 2 0
github.com/distribution/distribution/v3/health/health.go:165.59,169.46 4 5
github.com/distribution/distribution/v3/health/health.go:176.2,176.19 1 5
github.com/distribution/distribution/v3/health/health.go:169.46,171.17 2 4
github.com/distribution/distribution/v3/health/health.go:171.17,173.4 1 2
github.com/distribution/distribution/v3/health/health.go:181.38,183.2 1 5
github.com/distribution/distribution/v3/health/health.go:186.64,187.21 1 2
github.com/distribution/distribution/v3/health/health.go:190.2,193.8 4 2
github.com/distribution/distribution/v3/health/health.go:196.2,196.41 1 2
github.com/distribution/distribution/v3/health/health.go:187.21,189.3 1 0
github.com/distribution/distribution/v3/health/health.go:193.8,194.41 1 0
github.com/distribution/distribution/v3/health/health.go:201.43,203.2 1 2
github.com/distribution/distribution/v3/health/health.go:207.73,209.2 1 0
github.com/distribution/distribution/v3/health/health.go:213.52,215.2 1 0
github.com/distribution/distribution/v3/health/health.go:219.100,221.2 1 0
github.com/distribution/distribution/v3/health/health.go:225.79,227.2 1 0
github.com/distribution/distribution/v3/health/health.go:231.124,233.2 1 0
github.com/distribution/distribution/v3/health/health.go:237.103,239.2 1 0
github.com/distribution/distribution/v3/health/health.go:244.60,245.23 1 2
github.com/distribution/distribution/v3/health/health.go:245.23,250.23 3 2
github.com/distribution/distribution/v3/health/health.go:254.3,254.39 1 2
github.com/distribution/distribution/v3/health/health.go:250.23,252.4 1 1
github.com/distribution/distribution/v3/health/health.go:255.8,257.3 1 0
github.com/distribution/distribution/v3/health/health.go:264.49,265.71 1 1
github.com/distribution/distribution/v3/health/health.go:265.71,267.23 2 3
github.com/distribution/distribution/v3/health/health.go:273.3,273.26 1 2
github.com/distribution/distribution/v3/health/health.go:267.23,271.4 2 1
github.com/distribution/distribution/v3/health/health.go:279.99,281.16 2 2
github.com/distribution/distribution/v3/health/health.go:296.2,299.38 4 2
github.com/distribution/distribution/v3/health/health.go:281.16,290.17 4 0
github.com/distribution/distribution/v3/health/health.go:290.17,293.4 2 0
github.com/distribution/distribution/v3/health/health.go:299.38,301.3 1 0
github.com/distribution/distribution/v3/health/health.go:305.13,308.2 2 1
mode: atomic
github.com/distribution/distribution/v3/health/api/api.go:15.58,16.24 1 3
github.com/distribution/distribution/v3/health/api/api.go:16.24,18.3 1 1
github.com/distribution/distribution/v3/health/api/api.go:18.8,20.3 1 2
github.com/distribution/distribution/v3/health/api/api.go:24.56,25.24 1 1
github.com/distribution/distribution/v3/health/api/api.go:25.24,27.3 1 1
github.com/distribution/distribution/v3/health/api/api.go:27.8,29.3 1 0
github.com/distribution/distribution/v3/health/api/api.go:33.13,37.2 3 1

View file

@ -320,6 +320,12 @@ validation:
- ^https?://([^/]+\.)*example\.com/
deny:
- ^https?://www\.example\.com/
imageindex:
images:
exist: true
platforms:
- architecture: amd64
os: linux
```
In some instances a configuration option is **optional** but it contains child
@ -1276,6 +1282,27 @@ one of the `allow` regular expressions **and** one of the following holds:
2. `deny` is set but no URLs within the manifest match any of the `deny` regular
expressions.
#### `imageindex`
##### `images`
Set `exist` to false to disable validation that images exist when an image
index manifest is uploaded.
Set `platforms` to configure the set of platforms to validate the existence of.
If a platform is included in this list and in the images contained within an index,
the registry will validate that it exists in the registry. The registry will not
validate the existence of other platform images in the index.
This parameter does not validate that the platforms are included in the index,
if an image index is missing one or more platform images from this array it may
still be accepted by the registry.
Each platform is a map with two keys, `os` and `architecture`, as defined in the
[OCI Image Index specification](https://github.com/opencontainers/image-spec/blob/main/image-index.md#image-index-property-descriptions).
If `platforms` is an empty array, all images in the index must exist.
## Example: Development configuration
You can use this simple example for local development:

View file

@ -47,7 +47,7 @@ type ManifestBuilder interface {
AppendReference(dependency Describable) error
}
// ManifestService describes operations on image manifests.
// ManifestService describes operations on manifests.
type ManifestService interface {
// Exists returns true if the manifest exists.
Exists(ctx context.Context, dgst digest.Digest) (bool, error)

View file

@ -258,6 +258,14 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
options = append(options, storage.ManifestURLsDenyRegexp(re))
}
}
// If unset, default to true
if config.Validation.Manifests.ImageIndex.Images.Exist == nil ||
*config.Validation.Manifests.ImageIndex.Images.Exist {
options = append(options, storage.EnableValidateImageIndexImagesExist)
}
for _, platform := range config.Validation.Manifests.ImageIndex.Images.Platforms {
options = append(options, storage.AddValidateImageIndexImagesExistPlatform(platform.Architecture, platform.OS))
}
}
// configure storage caches

View file

@ -13,9 +13,11 @@ import (
// manifestListHandler is a ManifestHandler that covers schema2 manifest lists.
type manifestListHandler struct {
repository distribution.Repository
blobStore distribution.BlobStore
ctx context.Context
repository distribution.Repository
blobStore distribution.BlobStore
ctx context.Context
validateImagesExist bool
validateImagesExistPlatforms []platform // [] = All platforms
}
var _ ManifestHandler = &manifestListHandler{}
@ -74,7 +76,7 @@ func (ms *manifestListHandler) Put(ctx context.Context, manifestList distributio
func (ms *manifestListHandler) verifyManifest(ctx context.Context, mnfst distribution.Manifest, skipDependencyVerification bool) error {
var errs distribution.ErrManifestVerification
if !skipDependencyVerification {
if ms.validateImagesExist && !skipDependencyVerification {
// This manifest service is different from the blob service
// returned by Blob. It uses a linked blob store to ensure that
// only manifests are accessible.
@ -85,6 +87,10 @@ func (ms *manifestListHandler) verifyManifest(ctx context.Context, mnfst distrib
}
for _, manifestDescriptor := range mnfst.References() {
if !ms.platformMustExist(manifestDescriptor) {
continue
}
exists, err := manifestService.Exists(ctx, manifestDescriptor.Digest)
if err != nil && err != distribution.ErrBlobUnknown {
errs = append(errs, err)
@ -101,3 +107,20 @@ func (ms *manifestListHandler) verifyManifest(ctx context.Context, mnfst distrib
return nil
}
func (ms *manifestListHandler) platformMustExist(descriptor distribution.Descriptor) bool {
if len(ms.validateImagesExistPlatforms) == 0 {
return true
}
imagePlatform := descriptor.Platform
for _, platform := range ms.validateImagesExistPlatforms {
if imagePlatform.Architecture == platform.architecture &&
imagePlatform.OS == platform.os {
return true
}
}
return false
}

View file

@ -59,7 +59,7 @@ func TestManifestStorage(t *testing.T) {
if err != nil {
t.Fatal(err)
}
testManifestStorage(t, true, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect, Schema1SigningKey(k), EnableSchema1)
testManifestStorage(t, true, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect, Schema1SigningKey(k), EnableSchema1, EnableValidateImageIndexImagesExist)
}
func TestManifestStorageV1Unsupported(t *testing.T) {
@ -67,7 +67,7 @@ func TestManifestStorageV1Unsupported(t *testing.T) {
if err != nil {
t.Fatal(err)
}
testManifestStorage(t, false, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect, Schema1SigningKey(k))
testManifestStorage(t, false, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), EnableDelete, EnableRedirect, Schema1SigningKey(k), EnableValidateImageIndexImagesExist)
}
func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryOption) {
@ -394,7 +394,7 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo
repoName, _ := reference.WithName("foo/bar")
env := newManifestStoreTestEnv(t, repoName, "thetag",
BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)),
EnableDelete, EnableRedirect)
EnableDelete, EnableRedirect, EnableValidateImageIndexImagesExist)
ctx := context.Background()
ms, err := env.repository.Manifests(ctx)
@ -402,44 +402,36 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo
t.Fatal(err)
}
// Build a manifest and store it and its layers in the registry
// Build a manifest and store its layers in the registry
blobStore := env.repository.Blobs(ctx)
builder := ocischema.NewManifestBuilder(blobStore, []byte{}, map[string]string{})
err = builder.(*ocischema.Builder).SetMediaType(imageMediaType)
mfst, err := createRandomImage(t, testname, imageMediaType, blobStore)
if err != nil {
t.Fatal(err)
t.Fatalf("%s: unexpected error generating random image: %v", testname, err)
}
// Add some layers
for i := 0; i < 2; i++ {
rs, dgst, err := testutil.CreateRandomTarFile()
if err != nil {
t.Fatalf("%s: unexpected error generating test layer file", testname)
}
// create an image index
wr, err := env.repository.Blobs(env.ctx).Create(env.ctx)
if err != nil {
t.Fatalf("%s: unexpected error creating test upload: %v", testname, err)
}
if _, err := io.Copy(wr, rs); err != nil {
t.Fatalf("%s: unexpected error copying to upload: %v", testname, err)
}
if _, err := wr.Commit(env.ctx, distribution.Descriptor{Digest: dgst}); err != nil {
t.Fatalf("%s: unexpected error finishing upload: %v", testname, err)
}
builder.AppendReference(distribution.Descriptor{Digest: dgst})
platformSpec := manifestlist.PlatformSpec{
Architecture: "atari2600",
OS: "CP/M",
}
mfst, err := builder.Build(ctx)
mfstDescriptors := []manifestlist.ManifestDescriptor{
createManifestDescriptor(t, testname, mfst, platformSpec),
}
imageIndex, err := manifestlist.FromDescriptorsWithMediaType(mfstDescriptors, indexMediaType)
if err != nil {
t.Fatalf("%s: unexpected error generating manifest: %v", testname, err)
t.Fatalf("%s: unexpected error creating image index: %v", testname, err)
}
// before putting the manifest test for proper handling of SchemaVersion
_, err = ms.Put(ctx, imageIndex)
if err == nil {
t.Fatalf("%s: expected error putting image index without child manifests in the registry: %v", testname, err)
}
// Test for proper handling of SchemaVersion for the image
if mfst.(*ocischema.DeserializedManifest).Manifest.SchemaVersion != 2 {
t.Fatalf("%s: unexpected error generating default version for oci manifest", testname)
@ -457,22 +449,7 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo
}
}
// Also create an image index that contains the manifest
descriptor, err := env.registry.BlobStatter().Stat(ctx, manifestDigest)
if err != nil {
t.Fatalf("%s: unexpected error getting manifest descriptor", testname)
}
descriptor.MediaType = v1.MediaTypeImageManifest
descriptor.Platform = &v1.Platform{
Architecture: "atari2600",
OS: "CP/M",
}
imageIndex, err := ociIndexFromDesriptorsWithMediaType([]distribution.Descriptor{descriptor}, indexMediaType)
if err != nil {
t.Fatalf("%s: unexpected error creating image index: %v", testname, err)
}
// We can now push the index
var indexDigest digest.Digest
if indexDigest, err = ms.Put(ctx, imageIndex); err != nil {
@ -534,6 +511,203 @@ func testOCIManifestStorage(t *testing.T, testname string, includeMediaTypes boo
}
}
func TestIndexManifestStorageWithoutImageCheck(t *testing.T) {
imageMediaType := v1.MediaTypeImageManifest
indexMediaType := v1.MediaTypeImageIndex
repoName, _ := reference.WithName("foo/bar")
env := newManifestStoreTestEnv(t, repoName, "thetag",
BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)),
EnableDelete, EnableRedirect)
ctx := context.Background()
ms, err := env.repository.Manifests(ctx)
if err != nil {
t.Fatal(err)
}
// Build a manifest and store its layers in the registry
blobStore := env.repository.Blobs(ctx)
manifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore)
if err != nil {
t.Fatalf("unexpected error generating random image: %v", err)
}
// create an image index
platformSpec := manifestlist.PlatformSpec{
Architecture: "atari2600",
OS: "CP/M",
}
manifestDescriptors := []manifestlist.ManifestDescriptor{
createManifestDescriptor(t, t.Name(), manifest, platformSpec),
}
imageIndex, err := manifestlist.FromDescriptorsWithMediaType(manifestDescriptors, indexMediaType)
if err != nil {
t.Fatalf("unexpected error creating image index: %v", err)
}
// We should be able to put the index without having put the image
_, err = ms.Put(ctx, imageIndex)
if err != nil {
t.Fatalf("unexpected error putting sparse image index: %v", err)
}
}
func TestIndexManifestStorageWithSelectivePlatforms(t *testing.T) {
imageMediaType := v1.MediaTypeImageManifest
indexMediaType := v1.MediaTypeImageIndex
repoName, _ := reference.WithName("foo/bar")
env := newManifestStoreTestEnv(t, repoName, "thetag",
BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)),
EnableDelete, EnableRedirect, EnableValidateImageIndexImagesExist,
AddValidateImageIndexImagesExistPlatform("amd64", "linux"))
ctx := context.Background()
ms, err := env.repository.Manifests(ctx)
if err != nil {
t.Fatal(err)
}
// Build a manifests their layers in the registry
blobStore := env.repository.Blobs(ctx)
amdManifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore)
if err != nil {
t.Fatalf("%s: unexpected error generating random image: %v", t.Name(), err)
}
armManifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore)
if err != nil {
t.Fatalf("%s: unexpected error generating random image: %v", t.Name(), err)
}
atariManifest, err := createRandomImage(t, t.Name(), imageMediaType, blobStore)
if err != nil {
t.Fatalf("%s: unexpected error generating random image: %v", t.Name(), err)
}
// create an image index
amdPlatformSpec := manifestlist.PlatformSpec{
Architecture: "amd64",
OS: "linux",
}
armPlatformSpec := manifestlist.PlatformSpec{
Architecture: "arm",
OS: "plan9",
}
atariPlatformSpec := manifestlist.PlatformSpec{
Architecture: "atari2600",
OS: "CP/M",
}
manifestDescriptors := []manifestlist.ManifestDescriptor{
createManifestDescriptor(t, t.Name(), amdManifest, amdPlatformSpec),
createManifestDescriptor(t, t.Name(), armManifest, armPlatformSpec),
createManifestDescriptor(t, t.Name(), atariManifest, atariPlatformSpec),
}
imageIndex, err := manifestlist.FromDescriptorsWithMediaType(manifestDescriptors, indexMediaType)
if err != nil {
t.Fatalf("unexpected error creating image index: %v", err)
}
// Test we can't push with no image manifests existing in the registry
_, err = ms.Put(ctx, imageIndex)
if err == nil {
t.Fatalf("expected error putting image index without existing images: %v", err)
}
// Test we can't push with a manifest but not the right one
_, err = ms.Put(ctx, atariManifest)
if err != nil {
t.Fatalf("unexpected error putting manifest: %v", err)
}
_, err = ms.Put(ctx, imageIndex)
if err == nil {
t.Fatalf("expected error putting image index without correct existing images: %v", err)
}
// Test we can push with the right manifest
_, err = ms.Put(ctx, amdManifest)
if err != nil {
t.Fatalf("unexpected error putting manifest: %v", err)
}
_, err = ms.Put(ctx, imageIndex)
if err != nil {
t.Fatalf("unexpected error putting image index: %v", err)
}
}
// createRandomImage builds an image manifest and store it and its layers in the registry
func createRandomImage(t *testing.T, testname string, imageMediaType string, blobStore distribution.BlobStore) (distribution.Manifest, error) {
builder := ocischema.NewManifestBuilder(blobStore, []byte{}, map[string]string{})
err := builder.(*ocischema.Builder).SetMediaType(imageMediaType)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
// Add some layers
for i := 0; i < 2; i++ {
rs, dgst, err := testutil.CreateRandomTarFile()
if err != nil {
t.Fatalf("%s: unexpected error generating test layer file", testname)
}
wr, err := blobStore.Create(ctx)
if err != nil {
t.Fatalf("%s: unexpected error creating test upload: %v", testname, err)
}
if _, err := io.Copy(wr, rs); err != nil {
t.Fatalf("%s: unexpected error copying to upload: %v", testname, err)
}
if _, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst}); err != nil {
t.Fatalf("%s: unexpected error finishing upload: %v", testname, err)
}
builder.AppendReference(distribution.Descriptor{Digest: dgst})
}
return builder.Build(ctx)
}
// createManifestDescriptor builds a manifest descriptor from a manifest and a platform descriptor
func createManifestDescriptor(t *testing.T, testname string, manifest distribution.Manifest, platformSpec manifestlist.PlatformSpec) manifestlist.ManifestDescriptor {
manifestMediaType, manifestPayload, err := manifest.Payload()
if err != nil {
t.Fatalf("%s: unexpected error getting manifest payload: %v", testname, err)
}
manifestDigest := digest.FromBytes(manifestPayload)
descriptor := distribution.Descriptor{
Digest: manifestDigest,
Size: int64(len(manifestPayload)),
MediaType: manifestMediaType,
Platform: &v1.Platform{
Architecture: platformSpec.Architecture,
OS: platformSpec.OS,
},
}
return manifestlist.ManifestDescriptor{
Descriptor: descriptor,
Platform: platformSpec,
}
}
// TestLinkPathFuncs ensures that the link path functions behavior are locked
// down and implemented as expected.
func TestLinkPathFuncs(t *testing.T) {

View file

@ -23,8 +23,12 @@ type registry struct {
resumableDigestEnabled bool
schema1SigningKey libtrust.PrivateKey
blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory
manifestURLs manifestURLs
driver storagedriver.StorageDriver
// Validation
manifestURLs manifestURLs
validateImageIndexImagesExist bool
validateImageIndexImagesExistPlatforms []platform // [] = All platforms
}
// manifestURLs holds regular expressions for controlling manifest URL whitelisting
@ -33,6 +37,12 @@ type manifestURLs struct {
deny *regexp.Regexp
}
// platform represents a platform to validate exists in the
type platform struct {
architecture string
os string
}
// RegistryOption is the type used for functional options for NewRegistry.
type RegistryOption func(*registry) error
@ -80,6 +90,28 @@ func ManifestURLsDenyRegexp(r *regexp.Regexp) RegistryOption {
}
}
// EnableValidateImageIndexImagesExist is a functional option for NewRegistry. It enables
// validation that references exist before an image index is accepted.
func EnableValidateImageIndexImagesExist(registry *registry) error {
registry.validateImageIndexImagesExist = true
return nil
}
// AddValidateImageIndexImagesExistPlatform returns a functional option for NewRegistry.
// It adds a platform to check for existence before an image index is accepted.
func AddValidateImageIndexImagesExistPlatform(architecture string, os string) RegistryOption {
return func(registry *registry) error {
registry.validateImageIndexImagesExistPlatforms = append(
registry.validateImageIndexImagesExistPlatforms,
platform{
architecture: architecture,
os: os,
},
)
return nil
}
}
// Schema1SigningKey returns a functional option for NewRegistry. It sets the
// key for signing all schema1 manifests.
func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption {
@ -260,9 +292,11 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M
}
manifestListHandler := &manifestListHandler{
ctx: ctx,
repository: repo,
blobStore: blobStore,
ctx: ctx,
repository: repo,
blobStore: blobStore,
validateImagesExist: repo.validateImageIndexImagesExist,
validateImagesExistPlatforms: repo.validateImageIndexImagesExistPlatforms,
}
ms := &manifestStore{

View file

@ -18,7 +18,9 @@ func TestVerifyManifestForeignLayer(t *testing.T) {
inmemoryDriver := inmemory.New()
registry := createRegistry(t, inmemoryDriver,
ManifestURLsAllowRegexp(regexp.MustCompile("^https?://foo")),
ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope")))
ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope")),
EnableValidateImageIndexImagesExist,
)
repo := makeRepository(t, registry, "test")
manifestService := makeManifestService(t, repo)
@ -156,7 +158,9 @@ func TestVerifyManifestBlobLayerAndConfig(t *testing.T) {
inmemoryDriver := inmemory.New()
registry := createRegistry(t, inmemoryDriver,
ManifestURLsAllowRegexp(regexp.MustCompile("^https?://foo")),
ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope")))
ManifestURLsDenyRegexp(regexp.MustCompile("^https?://foo/nope")),
EnableValidateImageIndexImagesExist,
)
repo := makeRepository(t, registry, strings.ToLower(t.Name()))
manifestService := makeManifestService(t, repo)