-
Notifications
You must be signed in to change notification settings - Fork 91
image/docker: use unified configfile for registries.d #753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b8b5734
6e253a3
ae6b09f
bc3e627
acc9008
63d0fc2
988bbf9
81f5682
4ad0836
b575526
ff4e0c0
470e8ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ package docker | |
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "io" | ||
| "io/fs" | ||
| "net/url" | ||
| "os" | ||
|
|
@@ -15,30 +16,19 @@ import ( | |
| "go.podman.io/image/v5/docker/reference" | ||
| "go.podman.io/image/v5/internal/rootless" | ||
| "go.podman.io/image/v5/types" | ||
| "go.podman.io/storage/pkg/fileutils" | ||
| "go.podman.io/storage/pkg/configfile" | ||
| "go.podman.io/storage/pkg/homedir" | ||
| "go.podman.io/storage/pkg/unshare" | ||
| "gopkg.in/yaml.v3" | ||
| ) | ||
|
|
||
| // systemRegistriesDirPath is the path to registries.d, used for locating lookaside Docker signature storage. | ||
| // You can override this at build time with | ||
| // -ldflags '-X go.podman.io/image/v5/docker.systemRegistriesDirPath=$your_path' | ||
| var systemRegistriesDirPath = builtinRegistriesDirPath | ||
|
|
||
| // builtinRegistriesDirPath is the path to registries.d. | ||
| // DO NOT change this, instead see systemRegistriesDirPath above. | ||
| const builtinRegistriesDirPath = etcDir + "/containers/registries.d" | ||
|
|
||
| // userRegistriesDirPath is the path to the per user registries.d. | ||
| var userRegistriesDir = filepath.FromSlash(".config/containers/registries.d") | ||
|
|
||
| // defaultUserDockerDir is the default lookaside directory for unprivileged user | ||
| var defaultUserDockerDir = filepath.FromSlash(".local/share/containers/sigstore") | ||
|
|
||
| // defaultDockerDir is the default lookaside directory for root | ||
| var defaultDockerDir = "/var/lib/containers/sigstore" | ||
|
|
||
| // registryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all. | ||
| // registryConfiguration is one of the registries signature storage YAML fragments, or the result of merging them all. | ||
| // NOTE: Keep this in sync with docs/registries.d.md! | ||
| type registryConfiguration struct { | ||
| DefaultDocker *registryNamespace `yaml:"default-docker"` | ||
|
|
@@ -78,31 +68,40 @@ func SignatureStorageBaseURL(sys *types.SystemContext, ref types.ImageReference, | |
|
|
||
| // loadRegistryConfiguration returns a registryConfiguration appropriate for sys. | ||
| func loadRegistryConfiguration(sys *types.SystemContext) (*registryConfiguration, error) { | ||
| dirPath := registriesDirPath(sys) | ||
| logrus.Debugf(`Using registries.d directory %s`, dirPath) | ||
| return loadAndMergeConfig(dirPath) | ||
| } | ||
|
|
||
| // registriesDirPath returns a path to registries.d | ||
| func registriesDirPath(sys *types.SystemContext) string { | ||
| return registriesDirPathWithHomeDir(sys, homedir.Get()) | ||
| } | ||
|
|
||
| // registriesDirPathWithHomeDir is an internal implementation detail of registriesDirPath, | ||
| // it exists only to allow testing it with an artificial home directory. | ||
| func registriesDirPathWithHomeDir(sys *types.SystemContext, homeDir string) string { | ||
| if sys != nil && sys.RegistriesDirPath != "" { | ||
| return sys.RegistriesDirPath | ||
| logrus.Debugf(`Using registries.d directory %s`, sys.RegistriesDirPath) | ||
| return loadAndMergeConfig(sys.RegistriesDirPath) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At a glance, this separate implementation is fairly unsatisfactory. One approach might be to extend |
||
| } | ||
| userRegistriesDirPath := filepath.Join(homeDir, userRegistriesDir) | ||
| if err := fileutils.Exists(userRegistriesDirPath); err == nil { | ||
| return userRegistriesDirPath | ||
| var rootForImplicitAbsPaths string | ||
| if sys != nil { | ||
| rootForImplicitAbsPaths = sys.RootForImplicitAbsolutePaths | ||
| } | ||
| if sys != nil && sys.RootForImplicitAbsolutePaths != "" { | ||
| return filepath.Join(sys.RootForImplicitAbsolutePaths, systemRegistriesDirPath) | ||
| registriesFiles := configfile.File{ | ||
| Name: "registries", | ||
| Extension: "yaml", | ||
| DoNotLoadMainFiles: true, | ||
| DoNotUseExtensionForConfigName: true, | ||
| RootForImplicitAbsolutePaths: rootForImplicitAbsPaths, | ||
| UserId: unshare.GetRootlessUID(), | ||
| ErrorIfNotFound: false, | ||
| } | ||
|
|
||
| return systemRegistriesDirPath | ||
| mergedConfig := registryConfiguration{Docker: map[string]registryNamespace{}} | ||
| dockerDefaultMergedFrom := "" | ||
| nsMergedFrom := map[string]string{} | ||
| for item, err := range configfile.Read(®istriesFiles) { | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| contents, err := io.ReadAll(item.Reader) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| logrus.Debugf(`Reading registries signature storage configuration from %q`, item.Name) | ||
| if err := mergeRegistriesYAMLFragment(&mergedConfig, item.Name, contents, &dockerDefaultMergedFrom, nsMergedFrom); err != nil { | ||
| return nil, err | ||
| } | ||
| } | ||
| return &mergedConfig, nil | ||
| } | ||
|
|
||
| // loadAndMergeConfig loads configuration files in dirPath | ||
|
|
@@ -119,6 +118,7 @@ func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) { | |
| } | ||
| return nil, err | ||
| } | ||
| defer dir.Close() | ||
| configNames, err := dir.Readdirnames(0) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -131,39 +131,44 @@ func loadAndMergeConfig(dirPath string) (*registryConfiguration, error) { | |
| configBytes, err := os.ReadFile(configPath) | ||
| if err != nil { | ||
| if errors.Is(err, fs.ErrNotExist) { | ||
| // file must have been removed between the directory listing | ||
| // and the open call, ignore that as it is a expected race | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is non-obvious enough to be worth preserving the comment. |
||
| continue | ||
| } | ||
| return nil, err | ||
| } | ||
|
|
||
| var config registryConfiguration | ||
| err = yaml.Unmarshal(configBytes, &config) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("parsing %s: %w", configPath, err) | ||
| if err := mergeRegistriesYAMLFragment(&mergedConfig, configPath, configBytes, &dockerDefaultMergedFrom, nsMergedFrom); err != nil { | ||
| return nil, err | ||
| } | ||
| } | ||
|
|
||
| if config.DefaultDocker != nil { | ||
| if mergedConfig.DefaultDocker != nil { | ||
| return nil, fmt.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in %q and %q`, | ||
| dockerDefaultMergedFrom, configPath) | ||
| } | ||
| mergedConfig.DefaultDocker = config.DefaultDocker | ||
| dockerDefaultMergedFrom = configPath | ||
| } | ||
| return &mergedConfig, nil | ||
| } | ||
|
|
||
| for nsName, nsConfig := range config.Docker { // includes config.Docker == nil | ||
| if _, ok := mergedConfig.Docker[nsName]; ok { | ||
| return nil, fmt.Errorf(`Error parsing signature storage configuration: "docker" namespace %q defined both in %q and %q`, | ||
| nsName, nsMergedFrom[nsName], configPath) | ||
| } | ||
| mergedConfig.Docker[nsName] = nsConfig | ||
| nsMergedFrom[nsName] = configPath | ||
| // mergeRegistriesYAMLFragment parses configBytes as a single registries.d YAML fragment and merges it into merged. | ||
| func mergeRegistriesYAMLFragment(merged *registryConfiguration, configPath string, configBytes []byte, dockerDefaultMergedFrom *string, nsMergedFrom map[string]string) error { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Non-blocking: This works, but I think defining an object to hold the merging state, with new / mergeFragment / mergedConfig methods would be cleaner. But see elsewhere, do we need this split at all?) |
||
| var config registryConfiguration | ||
| err := yaml.Unmarshal(configBytes, &config) | ||
| if err != nil { | ||
| return fmt.Errorf("parsing %s: %w", configPath, err) | ||
| } | ||
|
|
||
| if config.DefaultDocker != nil { | ||
| if merged.DefaultDocker != nil { | ||
| return fmt.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in %q and %q`, | ||
| *dockerDefaultMergedFrom, configPath) | ||
| } | ||
| merged.DefaultDocker = config.DefaultDocker | ||
| *dockerDefaultMergedFrom = configPath | ||
| } | ||
|
|
||
| return &mergedConfig, nil | ||
| for nsName, nsConfig := range config.Docker { | ||
| if _, ok := merged.Docker[nsName]; ok { | ||
| return fmt.Errorf(`Error parsing signature storage configuration: "docker" namespace %q defined both in %q and %q`, | ||
| nsName, nsMergedFrom[nsName], configPath) | ||
| } | ||
| merged.Docker[nsName] = nsConfig | ||
| nsMergedFrom[nsName] = configPath | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // lookasideStorageBaseURL returns an appropriate signature storage URL for ref, for write access if “write”. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The files are not documented to be purely for signature-related items.