Switch config format to be easier to understand

Use a multiple choice instead of trying to keep it to booleans

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
This commit is contained in:
James Hewitt 2022-04-10 18:04:31 +01:00
parent db80ebed2d
commit b6cc51d369
No known key found for this signature in database
GPG key ID: EA6C3C654B6193E4
4 changed files with 101 additions and 60 deletions

View file

@ -680,6 +680,8 @@ type Validation struct {
Disabled bool `yaml:"disabled,omitempty"` Disabled bool `yaml:"disabled,omitempty"`
// Manifests configures manifest validation. // Manifests configures manifest validation.
Manifests ValidationManifests `yaml:"manifests,omitempty"` Manifests ValidationManifests `yaml:"manifests,omitempty"`
// ImageIndexes configures validation of image indexes
ImageIndexes ValidationImageIndexes `yaml:"imageindexes,omitempty"`
} }
type ValidationManifests struct { type ValidationManifests struct {
@ -692,18 +694,38 @@ type ValidationManifests struct {
// that URLs in pushed manifests must not match. // that URLs in pushed manifests must not match.
Deny []string `yaml:"deny,omitempty"` Deny []string `yaml:"deny,omitempty"`
} `yaml:"urls,omitempty"` } `yaml:"urls,omitempty"`
// ImageIndex configures validation of image indexes
ImageIndex ValidationManifestsImageIndex `yaml:"imageindex,omitempty"`
} }
type ValidationManifestsImageIndex struct { type ValidationImageIndexes struct {
Images struct { // PlatformsExist configures the validation applies to the platform images included in an image index
// Exist validates that images referenced by indexes exist in the registry PlatformsExist PlatformsExist `yaml:"platformsexist"`
// before the image index is accepted. // SelectedPlatforms filters the set of platforms to validate for image existence.
Exist *bool `yaml:"exist"` SelectedPlatforms []Platform `yaml:"selectedplatforms,omitempty"`
// Platforms filters the set of platforms to validate for image existence. }
Platforms []Platform `yaml:"platforms,omitempty"`
} `yaml:"images,omitempty"` // PlatformsExist configures the validation applies to the platform images included in an image index
// This can be all, none, or selected
type PlatformsExist string
// UnmarshalYAML implements the yaml.Umarshaler interface
// Unmarshals a string into a PlatformsExist option, lowercasing the string and validating that it represents a
// valid option
func (platformsexist *PlatformsExist) UnmarshalYAML(unmarshal func(interface{}) error) error {
var platformsExistString string
err := unmarshal(&platformsExistString)
if err != nil {
return err
}
platformsExistString = strings.ToLower(platformsExistString)
switch platformsExistString {
case "all", "none", "selected":
default:
return fmt.Errorf("invalid platformsexist option %s Must be one of [all, none, selected]", platformsExistString)
}
*platformsexist = PlatformsExist(platformsExistString)
return nil
} }
// Parse parses an input configuration yaml document into a Configuration struct // Parse parses an input configuration yaml document into a Configuration struct

View file

@ -16,9 +16,6 @@ import (
// Hook up gocheck into the "go test" runner // Hook up gocheck into the "go test" runner
func Test(t *testing.T) { check.TestingT(t) } 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 // configStruct is a canonical example configuration, which should map to configYamlV0_1
var configStruct = Configuration{ var configStruct = Configuration{
Version: "0.1", Version: "0.1",
@ -169,15 +166,8 @@ var configStruct = Configuration{
WriteTimeout: time.Millisecond * 10, WriteTimeout: time.Millisecond * 10,
}, },
Validation: Validation{ Validation: Validation{
Manifests: ValidationManifests{ ImageIndexes: ValidationImageIndexes{
ImageIndex: ValidationManifestsImageIndex{ PlatformsExist: "none",
Images: struct {
Exist *bool `yaml:"exist"`
Platforms []Platform `yaml:"platforms,omitempty"`
}{
Exist: &falseaddr,
},
},
}, },
}, },
} }
@ -237,10 +227,8 @@ redis:
readtimeout: 10ms readtimeout: 10ms
writetimeout: 10ms writetimeout: 10ms
validation: validation:
manifests: imageindexes:
imageindex: platformsexist: none
images:
exist: false
` `
// inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory // inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory
@ -271,10 +259,8 @@ http:
headers: headers:
X-Content-Type-Options: [nosniff] X-Content-Type-Options: [nosniff]
validation: validation:
manifests: imageindexes:
imageindex: platformsexist: none
images:
exist: false
` `
type ConfigSuite struct { type ConfigSuite struct {
@ -367,7 +353,7 @@ func (suite *ConfigSuite) TestParseIncomplete(c *check.C) {
IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` IdleTimeout time.Duration `yaml:"idletimeout,omitempty"`
} `yaml:"pool,omitempty"` } `yaml:"pool,omitempty"`
}{} }{}
suite.expectedConfig.Validation.Manifests.ImageIndex.Images.Exist = nil suite.expectedConfig.Validation.ImageIndexes.PlatformsExist = ""
// Note: this also tests that REGISTRY_STORAGE and // Note: this also tests that REGISTRY_STORAGE and
// REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY can be used together // REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY can be used together
@ -662,11 +648,12 @@ func copyConfig(config Configuration) *Configuration {
} }
configCopy.Redis = config.Redis configCopy.Redis = config.Redis
configCopy.Validation = Validation{ configCopy.Validation = Validation{
Enabled: config.Validation.Enabled, Enabled: config.Validation.Enabled,
Disabled: config.Validation.Disabled, Disabled: config.Validation.Disabled,
Manifests: config.Validation.Manifests, Manifests: config.Validation.Manifests,
ImageIndexes: config.Validation.ImageIndexes,
} }
return configCopy return configCopy

View file

@ -1247,16 +1247,16 @@ features. Each subsection defines such a feature with configurable behavior.
## `validation` ## `validation`
```none ```yaml
validation: validation:
manifests: disabled: false
urls:
allow:
- ^https?://([^/]+\.)*example\.com/
deny:
- ^https?://www\.example\.com/
``` ```
Use these settings to configure what validation the registry performs on content.
Validation is performed when content is uploaded to the registry. Changing these
settings will not validate content that has already been accepting into the registry.
### `disabled` ### `disabled`
The `disabled` flag disables the other options in the `validation` The `disabled` flag disables the other options in the `validation`
@ -1269,6 +1269,16 @@ Use the `manifests` subsection to configure validation of manifests. If
#### `urls` #### `urls`
```yaml
validation:
manifests:
urls:
allow:
- ^https?://([^/]+\.)*example\.com/
deny:
- ^https?://www\.example\.com/
```
The `allow` and `deny` options are each a list of The `allow` and `deny` options are each a list of
[regular expressions](https://pkg.go.dev/regexp/syntax) that restrict the URLs in [regular expressions](https://pkg.go.dev/regexp/syntax) that restrict the URLs in
pushed manifests. pushed manifests.
@ -1282,27 +1292,47 @@ 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 2. `deny` is set but no URLs within the manifest match any of the `deny` regular
expressions. expressions.
#### `imageindex` ### `imageindexes`
##### `images` validation:
imageindexes:
platformsexist: [All|None|Selected]
selectedplatforms:
- os: linux
architecture: amd64
Set `exist` to false to disable validation that images exist when an image Use these settings to configure what validation the registry performs on image
index manifest is uploaded. index manifests uploaded to the registry.
Set `platforms` to configure the set of platforms to validate the existence of. #### `platformsexist`
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, Set `platformexist` to `all` (the default) to validate all platform images exist.
if an image index is missing one or more platform images from this array it may The registry will validate that the images referenced by the index exist in the
still be accepted by the registry. registry before accepting the image index.
Set `platformsexist` to `none` to disable all validation that images exist when an
image index manifest is uploaded. This allows image lists to be uploaded to the
registry without their associated images.
Set `platformsexist` to `selected` to selectively validate the existence of platforms
within image index manifests.
#### `selectedplatforms`
When `platformsexist` is set to `selected`, set `selectedplatforms` to an array of
platforms to validate. If a platform is included in this the array and in the images
contained within an index, the registry will validate that the platform specific image
exists in the registry before accepting the index. The registry will not validate the
existence of platform specific images in the index that do not appear in the
`selectedplatforms` array.
This parameter does not validate that the configured platforms are included in every
index. If an image index does not include one of the platform specific images configured
in the `selectedplatforms` array, it may still be accepted by the registry.
Each platform is a map with two keys, `os` and `architecture`, as defined in the 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). [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 ## Example: Development configuration
You can use this simple example for local development: You can use this simple example for local development:

View file

@ -258,13 +258,15 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
options = append(options, storage.ManifestURLsDenyRegexp(re)) options = append(options, storage.ManifestURLsDenyRegexp(re))
} }
} }
// If unset, default to true // Any option except "none" enables the validation
if config.Validation.Manifests.ImageIndex.Images.Exist == nil || if config.Validation.ImageIndexes.PlatformsExist != "none" {
*config.Validation.Manifests.ImageIndex.Images.Exist {
options = append(options, storage.EnableValidateImageIndexImagesExist) options = append(options, storage.EnableValidateImageIndexImagesExist)
} }
for _, platform := range config.Validation.Manifests.ImageIndex.Images.Platforms { // If we are verifying selected image platforms, add those platforms to the options
options = append(options, storage.AddValidateImageIndexImagesExistPlatform(platform.Architecture, platform.OS)) if config.Validation.ImageIndexes.PlatformsExist == "selected" {
for _, platform := range config.Validation.ImageIndexes.SelectedPlatforms {
options = append(options, storage.AddValidateImageIndexImagesExistPlatform(platform.Architecture, platform.OS))
}
} }
} }