mirror of
https://github.com/containers/podman.git
synced 2025-07-18 01:57:24 +08:00
add --module flag
Support a new concept in containers.conf called "modules". A "module" is a containers.conf file located at a specific directory. More than one module can be loaded in the specified order, following existing override semantics. There are three directories to load modules from: - $CONFIG_HOME/containers/containers.conf.modules - /etc/containers/containers.conf.modules - /usr/share/containers/containers.conf.modules With CONFIG_HOME pointing to $HOME/.config or, if set, $XDG_CONFIG_HOME. Absolute paths will be loaded as is, relative paths will be resolved relative to the three directories above allowing for admin configs (/etc/) to override system configs (/usr/share/) and user configs ($CONFIG_HOME) to override admin configs. Pulls in containers/common/pull/1599. Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
This commit is contained in:
200
vendor/github.com/containers/common/pkg/config/config.go
generated
vendored
200
vendor/github.com/containers/common/pkg/config/config.go
generated
vendored
@ -3,14 +3,11 @@ package config
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/containers/common/libnetwork/types"
|
||||
@ -81,6 +78,8 @@ type Config struct {
|
||||
ConfigMaps ConfigMapConfig `toml:"configmaps"`
|
||||
// Farms defines configurations for the buildfarm farms
|
||||
Farms FarmConfig `toml:"farms"`
|
||||
|
||||
loadedModules []string // only used at runtime to store which modules were loaded
|
||||
}
|
||||
|
||||
// ContainersConfig represents the "containers" TOML config table
|
||||
@ -708,166 +707,6 @@ func (c *EngineConfig) ImagePlatformToRuntime(os string, arch string) string {
|
||||
return c.OCIRuntime
|
||||
}
|
||||
|
||||
// NewConfig creates a new Config. It starts with an empty config and, if
|
||||
// specified, merges the config at `userConfigPath` path. Depending if we're
|
||||
// running as root or rootless, we then merge the system configuration followed
|
||||
// by merging the default config (hard-coded default in memory).
|
||||
// Note that the OCI runtime is hard-set to `crun` if we're running on a system
|
||||
// with cgroupv2v2. Other OCI runtimes are not yet supporting cgroupv2v2. This
|
||||
// might change in the future.
|
||||
func NewConfig(userConfigPath string) (*Config, error) {
|
||||
// Generate the default config for the system
|
||||
config, err := DefaultConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now, gather the system configs and merge them as needed.
|
||||
configs, err := systemConfigs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("finding config on system: %w", err)
|
||||
}
|
||||
for _, path := range configs {
|
||||
// Merge changes in later configs with the previous configs.
|
||||
// Each config file that specified fields, will override the
|
||||
// previous fields.
|
||||
if err = readConfigFromFile(path, config); err != nil {
|
||||
return nil, fmt.Errorf("reading system config %q: %w", path, err)
|
||||
}
|
||||
logrus.Debugf("Merged system config %q", path)
|
||||
logrus.Tracef("%+v", config)
|
||||
}
|
||||
|
||||
// If the caller specified a config path to use, then we read it to
|
||||
// override the system defaults.
|
||||
if userConfigPath != "" {
|
||||
var err error
|
||||
// readConfigFromFile reads in container config in the specified
|
||||
// file and then merge changes with the current default.
|
||||
if err = readConfigFromFile(userConfigPath, config); err != nil {
|
||||
return nil, fmt.Errorf("reading user config %q: %w", userConfigPath, err)
|
||||
}
|
||||
logrus.Debugf("Merged user config %q", userConfigPath)
|
||||
logrus.Tracef("%+v", config)
|
||||
}
|
||||
config.addCAPPrefix()
|
||||
|
||||
if err := config.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := config.setupEnv(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// readConfigFromFile reads the specified config file at `path` and attempts to
|
||||
// unmarshal its content into a Config. The config param specifies the previous
|
||||
// default config. If the path, only specifies a few fields in the Toml file
|
||||
// the defaults from the config parameter will be used for all other fields.
|
||||
func readConfigFromFile(path string, config *Config) error {
|
||||
logrus.Tracef("Reading configuration file %q", path)
|
||||
meta, err := toml.DecodeFile(path, config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode configuration %v: %w", path, err)
|
||||
}
|
||||
keys := meta.Undecoded()
|
||||
if len(keys) > 0 {
|
||||
logrus.Debugf("Failed to decode the keys %q from %q.", keys, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addConfigs will search one level in the config dirPath for config files
|
||||
// If the dirPath does not exist, addConfigs will return nil
|
||||
func addConfigs(dirPath string, configs []string) ([]string, error) {
|
||||
newConfigs := []string{}
|
||||
|
||||
err := filepath.WalkDir(dirPath,
|
||||
// WalkFunc to read additional configs
|
||||
func(path string, d fs.DirEntry, err error) error {
|
||||
switch {
|
||||
case err != nil:
|
||||
// return error (could be a permission problem)
|
||||
return err
|
||||
case d.IsDir():
|
||||
if path != dirPath {
|
||||
// make sure to not recurse into sub-directories
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// ignore directories
|
||||
return nil
|
||||
default:
|
||||
// only add *.conf files
|
||||
if strings.HasSuffix(path, ".conf") {
|
||||
newConfigs = append(newConfigs, path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
},
|
||||
)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
}
|
||||
sort.Strings(newConfigs)
|
||||
return append(configs, newConfigs...), err
|
||||
}
|
||||
|
||||
// Returns the list of configuration files, if they exist in order of hierarchy.
|
||||
// The files are read in order and each new file can/will override previous
|
||||
// file settings.
|
||||
func systemConfigs() (configs []string, finalErr error) {
|
||||
if path := os.Getenv("CONTAINERS_CONF_OVERRIDE"); path != "" {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return nil, fmt.Errorf("CONTAINERS_CONF_OVERRIDE file: %w", err)
|
||||
}
|
||||
// Add the override config last to make sure it can override any
|
||||
// previous settings.
|
||||
defer func() {
|
||||
if finalErr == nil {
|
||||
configs = append(configs, path)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if path := os.Getenv("CONTAINERS_CONF"); path != "" {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return nil, fmt.Errorf("CONTAINERS_CONF file: %w", err)
|
||||
}
|
||||
return append(configs, path), nil
|
||||
}
|
||||
if _, err := os.Stat(DefaultContainersConfig); err == nil {
|
||||
configs = append(configs, DefaultContainersConfig)
|
||||
}
|
||||
if _, err := os.Stat(OverrideContainersConfig); err == nil {
|
||||
configs = append(configs, OverrideContainersConfig)
|
||||
}
|
||||
|
||||
var err error
|
||||
configs, err = addConfigs(OverrideContainersConfig+".d", configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path, err := ifRootlessConfigPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if path != "" {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
configs = append(configs, path)
|
||||
}
|
||||
configs, err = addConfigs(path+".d", configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// CheckCgroupsAndAdjustConfig checks if we're running rootless with the systemd
|
||||
// cgroup manager. In case the user session isn't available, we're switching the
|
||||
// cgroup manager to cgroupfs. Note, this only applies to rootless.
|
||||
@ -1190,37 +1029,6 @@ func rootlessConfigPath() (string, error) {
|
||||
return filepath.Join(home, UserOverrideContainersConfig), nil
|
||||
}
|
||||
|
||||
var (
|
||||
configErr error
|
||||
configMutex sync.Mutex
|
||||
config *Config
|
||||
)
|
||||
|
||||
// Default returns the default container config.
|
||||
// Configuration files will be read in the following files:
|
||||
// * /usr/share/containers/containers.conf
|
||||
// * /etc/containers/containers.conf
|
||||
// * $HOME/.config/containers/containers.conf # When run in rootless mode
|
||||
// Fields in latter files override defaults set in previous files and the
|
||||
// default config.
|
||||
// None of these files are required, and not all fields need to be specified
|
||||
// in each file, only the fields you want to override.
|
||||
// The system defaults container config files can be overwritten using the
|
||||
// CONTAINERS_CONF environment variable. This is usually done for testing.
|
||||
func Default() (*Config, error) {
|
||||
configMutex.Lock()
|
||||
defer configMutex.Unlock()
|
||||
if config != nil || configErr != nil {
|
||||
return config, configErr
|
||||
}
|
||||
return defConfig()
|
||||
}
|
||||
|
||||
func defConfig() (*Config, error) {
|
||||
config, configErr = NewConfig("")
|
||||
return config, configErr
|
||||
}
|
||||
|
||||
func Path() string {
|
||||
if path := os.Getenv("CONTAINERS_CONF"); path != "" {
|
||||
return path
|
||||
@ -1289,9 +1097,7 @@ func (c *Config) Write() error {
|
||||
// This function is meant to be used for long-running processes that need to reload potential changes made to
|
||||
// the cached containers.conf files.
|
||||
func Reload() (*Config, error) {
|
||||
configMutex.Lock()
|
||||
defer configMutex.Unlock()
|
||||
return defConfig()
|
||||
return New(&Options{SetDefault: true})
|
||||
}
|
||||
|
||||
func (c *Config) ActiveDestination() (uri, identity string, machine bool, err error) {
|
||||
|
25
vendor/github.com/containers/common/pkg/config/default.go
generated
vendored
25
vendor/github.com/containers/common/pkg/config/default.go
generated
vendored
@ -157,9 +157,11 @@ const (
|
||||
DefaultVolumePluginTimeout = 5
|
||||
)
|
||||
|
||||
// DefaultConfig defines the default values from containers.conf.
|
||||
func DefaultConfig() (*Config, error) {
|
||||
defaultEngineConfig, err := defaultConfigFromMemory()
|
||||
// defaultConfig returns Config with builtin defaults and minimal adjustments
|
||||
// to the current host only. It does not read any config files from the host or
|
||||
// the environment.
|
||||
func defaultConfig() (*Config, error) {
|
||||
defaultEngineConfig, err := defaultEngineConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -266,9 +268,9 @@ func defaultFarmConfig() FarmConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// defaultConfigFromMemory returns a default engine configuration. Note that the
|
||||
// defaultEngineConfig eturns a default engine configuration. Note that the
|
||||
// config is different for root and rootless. It also parses the storage.conf.
|
||||
func defaultConfigFromMemory() (*EngineConfig, error) {
|
||||
func defaultEngineConfig() (*EngineConfig, error) {
|
||||
c := new(EngineConfig)
|
||||
tmp, err := defaultTmpDir()
|
||||
if err != nil {
|
||||
@ -653,3 +655,16 @@ func useUserConfigLocations() bool {
|
||||
// GetRootlessUID == -1 on Windows, so exclude negative range
|
||||
return unshare.GetRootlessUID() > 0
|
||||
}
|
||||
|
||||
// getDefaultImage returns the default machine image stream
|
||||
// On Windows this refers to the Fedora major release number
|
||||
func getDefaultMachineImage() string {
|
||||
return "testing"
|
||||
}
|
||||
|
||||
// getDefaultMachineUser returns the user to use for rootless podman
|
||||
// This is only for the apple, hyperv, and qemu implementations.
|
||||
// WSL's user will be hardcoded in podman to "user"
|
||||
func getDefaultMachineUser() string {
|
||||
return "core"
|
||||
}
|
||||
|
11
vendor/github.com/containers/common/pkg/config/default_linux.go
generated
vendored
11
vendor/github.com/containers/common/pkg/config/default_linux.go
generated
vendored
@ -17,17 +17,6 @@ func getDefaultCgroupsMode() string {
|
||||
return "enabled"
|
||||
}
|
||||
|
||||
// getDefaultMachineImage returns the default machine image stream
|
||||
// On Linux/Mac, this returns the FCOS stream
|
||||
func getDefaultMachineImage() string {
|
||||
return "testing"
|
||||
}
|
||||
|
||||
// getDefaultMachineUser returns the user to use for rootless podman
|
||||
func getDefaultMachineUser() string {
|
||||
return "core"
|
||||
}
|
||||
|
||||
// getDefaultProcessLimits returns the nproc for the current process in ulimits format
|
||||
// Note that nfile sometimes cannot be set to unlimited, and the limit is hardcoded
|
||||
// to (oldMaxSize) 1048576 (2^20), see: http://stackoverflow.com/a/1213069/1811501
|
||||
|
11
vendor/github.com/containers/common/pkg/config/default_unsupported.go
generated
vendored
11
vendor/github.com/containers/common/pkg/config/default_unsupported.go
generated
vendored
@ -5,17 +5,6 @@ package config
|
||||
|
||||
import "os"
|
||||
|
||||
// getDefaultMachineImage returns the default machine image stream
|
||||
// On Linux/Mac, this returns the FCOS stream
|
||||
func getDefaultMachineImage() string {
|
||||
return "testing"
|
||||
}
|
||||
|
||||
// getDefaultMachineUser returns the user to use for rootless podman
|
||||
func getDefaultMachineUser() string {
|
||||
return "core"
|
||||
}
|
||||
|
||||
// isCgroup2UnifiedMode returns whether we are running in cgroup2 mode.
|
||||
func isCgroup2UnifiedMode() (isUnified bool, isUnifiedErr error) {
|
||||
return false, nil
|
||||
|
13
vendor/github.com/containers/common/pkg/config/default_windows.go
generated
vendored
13
vendor/github.com/containers/common/pkg/config/default_windows.go
generated
vendored
@ -2,19 +2,6 @@ package config
|
||||
|
||||
import "os"
|
||||
|
||||
// getDefaultImage returns the default machine image stream
|
||||
// On Windows this refers to the Fedora major release number
|
||||
func getDefaultMachineImage() string {
|
||||
return "testing"
|
||||
}
|
||||
|
||||
// getDefaultMachineUser returns the user to use for rootless podman
|
||||
// This is only for the hyperv and qemu implementations. WSL's user
|
||||
// will be hardcoded in podman to "user"
|
||||
func getDefaultMachineUser() string {
|
||||
return "core"
|
||||
}
|
||||
|
||||
// isCgroup2UnifiedMode returns whether we are running in cgroup2 mode.
|
||||
func isCgroup2UnifiedMode() (isUnified bool, isUnifiedErr error) {
|
||||
return false, nil
|
||||
|
95
vendor/github.com/containers/common/pkg/config/modules.go
generated
vendored
Normal file
95
vendor/github.com/containers/common/pkg/config/modules.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
// The subdirectory for looking up containers.conf modules.
|
||||
const moduleSubdir = "containers/containers.conf.modules"
|
||||
|
||||
// Moving the base paths into variables allows for overriding them in units
|
||||
// tests.
|
||||
var (
|
||||
moduleBaseEtc = "/etc/"
|
||||
moduleBaseUsr = "/usr/share"
|
||||
)
|
||||
|
||||
// LoadedModules returns absolute paths to loaded containers.conf modules.
|
||||
func (c *Config) LoadedModules() []string {
|
||||
// Required for conmon's callback to Podman's cleanup.
|
||||
// Absolute paths make loading the modules a bit faster.
|
||||
return c.loadedModules
|
||||
}
|
||||
|
||||
// Find the specified modules in the options. Return an error if a specific
|
||||
// module cannot be located on the host.
|
||||
func (o *Options) modules() ([]string, error) {
|
||||
if len(o.Modules) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
dirs, err := ModuleDirectories()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configs := make([]string, 0, len(o.Modules))
|
||||
for _, path := range o.Modules {
|
||||
resolved, err := resolveModule(path, dirs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not resolve module %q: %w", path, err)
|
||||
}
|
||||
configs = append(configs, resolved)
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// ModuleDirectories return the directories to load modules from:
|
||||
// 1) XDG_CONFIG_HOME/HOME if rootless
|
||||
// 2) /etc/
|
||||
// 3) /usr/share
|
||||
func ModuleDirectories() ([]string, error) { // Public API for shell completions in Podman
|
||||
modules := []string{
|
||||
filepath.Join(moduleBaseEtc, moduleSubdir),
|
||||
filepath.Join(moduleBaseUsr, moduleSubdir),
|
||||
}
|
||||
|
||||
if !unshare.IsRootless() {
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
// Prepend the user modules dir.
|
||||
configHome, err := homedir.GetConfigHome()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append([]string{filepath.Join(configHome, moduleSubdir)}, modules...), nil
|
||||
}
|
||||
|
||||
// Resolve the specified path to a module.
|
||||
func resolveModule(path string, dirs []string) (string, error) {
|
||||
if filepath.IsAbs(path) {
|
||||
_, err := os.Stat(path)
|
||||
return path, err
|
||||
}
|
||||
|
||||
// Collect all errors to avoid suppressing important errors (e.g.,
|
||||
// permission errors).
|
||||
var multiErr error
|
||||
for _, d := range dirs {
|
||||
candidate := filepath.Join(d, path)
|
||||
_, err := os.Stat(candidate)
|
||||
if err == nil {
|
||||
return candidate, nil
|
||||
}
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
return "", multiErr
|
||||
}
|
240
vendor/github.com/containers/common/pkg/config/new.go
generated
vendored
Normal file
240
vendor/github.com/containers/common/pkg/config/new.go
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
cachedConfigError error
|
||||
cachedConfigMutex sync.Mutex
|
||||
cachedConfig *Config
|
||||
)
|
||||
|
||||
const (
|
||||
// FIXME: update code base and tests to use the two constants below.
|
||||
containersConfEnv = "CONTAINERS_CONF"
|
||||
containersConfOverrideEnv = containersConfEnv + "_OVERRIDE"
|
||||
)
|
||||
|
||||
// Options to use when loading a Config via New().
|
||||
type Options struct {
|
||||
// Attempt to load the following config modules.
|
||||
Modules []string
|
||||
|
||||
// Set the loaded config as the default one which can later on be
|
||||
// accessed via Default().
|
||||
SetDefault bool
|
||||
|
||||
// Additional configs to load. An internal only field to make the
|
||||
// behavior observable and testable in unit tests.
|
||||
additionalConfigs []string
|
||||
}
|
||||
|
||||
// New returns a Config as described in the containers.conf(5) man page.
|
||||
func New(options *Options) (*Config, error) {
|
||||
if options == nil {
|
||||
options = &Options{}
|
||||
} else if options.SetDefault {
|
||||
cachedConfigMutex.Lock()
|
||||
defer cachedConfigMutex.Unlock()
|
||||
}
|
||||
return newLocked(options)
|
||||
}
|
||||
|
||||
// Default returns the default container config. If no default config has been
|
||||
// set yet, a new config will be loaded by New() and set as the default one.
|
||||
// All callers are expected to use the returned Config read only. Changing
|
||||
// data may impact other call sites.
|
||||
func Default() (*Config, error) {
|
||||
cachedConfigMutex.Lock()
|
||||
defer cachedConfigMutex.Unlock()
|
||||
if cachedConfig != nil || cachedConfigError != nil {
|
||||
return cachedConfig, cachedConfigError
|
||||
}
|
||||
cachedConfig, cachedConfigError = newLocked(&Options{SetDefault: true})
|
||||
return cachedConfig, cachedConfigError
|
||||
}
|
||||
|
||||
// A helper function for New() expecting the caller to hold the
|
||||
// cachedConfigMutex if options.SetDefault is set..
|
||||
func newLocked(options *Options) (*Config, error) {
|
||||
// Start with the built-in defaults
|
||||
config, err := defaultConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now, gather the system configs and merge them as needed.
|
||||
configs, err := systemConfigs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("finding config on system: %w", err)
|
||||
}
|
||||
for _, path := range configs {
|
||||
// Merge changes in later configs with the previous configs.
|
||||
// Each config file that specified fields, will override the
|
||||
// previous fields.
|
||||
if err = readConfigFromFile(path, config); err != nil {
|
||||
return nil, fmt.Errorf("reading system config %q: %w", path, err)
|
||||
}
|
||||
logrus.Debugf("Merged system config %q", path)
|
||||
logrus.Tracef("%+v", config)
|
||||
}
|
||||
|
||||
modules, err := options.modules()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.loadedModules = modules
|
||||
|
||||
options.additionalConfigs = append(options.additionalConfigs, modules...)
|
||||
|
||||
// The _OVERRIDE variable _must_ always win. That's a contract we need
|
||||
// to honor (for the Podman CI).
|
||||
if path := os.Getenv(containersConfOverrideEnv); path != "" {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return nil, fmt.Errorf("%s file: %w", containersConfOverrideEnv, err)
|
||||
}
|
||||
options.additionalConfigs = append(options.additionalConfigs, path)
|
||||
}
|
||||
|
||||
// If the caller specified a config path to use, then we read it to
|
||||
// override the system defaults.
|
||||
for _, add := range options.additionalConfigs {
|
||||
if add == "" {
|
||||
continue
|
||||
}
|
||||
// readConfigFromFile reads in container config in the specified
|
||||
// file and then merge changes with the current default.
|
||||
if err := readConfigFromFile(add, config); err != nil {
|
||||
return nil, fmt.Errorf("reading additional config %q: %w", add, err)
|
||||
}
|
||||
logrus.Debugf("Merged additional config %q", add)
|
||||
logrus.Tracef("%+v", config)
|
||||
}
|
||||
config.addCAPPrefix()
|
||||
|
||||
if err := config.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := config.setupEnv(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.SetDefault {
|
||||
cachedConfig = config
|
||||
cachedConfigError = nil
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// NewConfig creates a new Config. It starts with an empty config and, if
|
||||
// specified, merges the config at `userConfigPath` path.
|
||||
//
|
||||
// Deprecated: use new instead.
|
||||
func NewConfig(userConfigPath string) (*Config, error) {
|
||||
return New(&Options{additionalConfigs: []string{userConfigPath}})
|
||||
}
|
||||
|
||||
// Returns the list of configuration files, if they exist in order of hierarchy.
|
||||
// The files are read in order and each new file can/will override previous
|
||||
// file settings.
|
||||
func systemConfigs() (configs []string, finalErr error) {
|
||||
if path := os.Getenv(containersConfEnv); path != "" {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return nil, fmt.Errorf("%s file: %w", containersConfEnv, err)
|
||||
}
|
||||
return append(configs, path), nil
|
||||
}
|
||||
if _, err := os.Stat(DefaultContainersConfig); err == nil {
|
||||
configs = append(configs, DefaultContainersConfig)
|
||||
}
|
||||
if _, err := os.Stat(OverrideContainersConfig); err == nil {
|
||||
configs = append(configs, OverrideContainersConfig)
|
||||
}
|
||||
|
||||
var err error
|
||||
configs, err = addConfigs(OverrideContainersConfig+".d", configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path, err := ifRootlessConfigPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if path != "" {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
configs = append(configs, path)
|
||||
}
|
||||
configs, err = addConfigs(path+".d", configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// addConfigs will search one level in the config dirPath for config files
|
||||
// If the dirPath does not exist, addConfigs will return nil
|
||||
func addConfigs(dirPath string, configs []string) ([]string, error) {
|
||||
newConfigs := []string{}
|
||||
|
||||
err := filepath.WalkDir(dirPath,
|
||||
// WalkFunc to read additional configs
|
||||
func(path string, d fs.DirEntry, err error) error {
|
||||
switch {
|
||||
case err != nil:
|
||||
// return error (could be a permission problem)
|
||||
return err
|
||||
case d.IsDir():
|
||||
if path != dirPath {
|
||||
// make sure to not recurse into sub-directories
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// ignore directories
|
||||
return nil
|
||||
default:
|
||||
// only add *.conf files
|
||||
if strings.HasSuffix(path, ".conf") {
|
||||
newConfigs = append(newConfigs, path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
},
|
||||
)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
}
|
||||
sort.Strings(newConfigs)
|
||||
return append(configs, newConfigs...), err
|
||||
}
|
||||
|
||||
// readConfigFromFile reads the specified config file at `path` and attempts to
|
||||
// unmarshal its content into a Config. The config param specifies the previous
|
||||
// default config. If the path, only specifies a few fields in the Toml file
|
||||
// the defaults from the config parameter will be used for all other fields.
|
||||
func readConfigFromFile(path string, config *Config) error {
|
||||
logrus.Tracef("Reading configuration file %q", path)
|
||||
meta, err := toml.DecodeFile(path, config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode configuration %v: %w", path, err)
|
||||
}
|
||||
keys := meta.Undecoded()
|
||||
if len(keys) > 0 {
|
||||
logrus.Debugf("Failed to decode the keys %q from %q.", keys, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user