Skip to content

Commit

Permalink
Add support for drop-in config
Browse files Browse the repository at this point in the history
Signed-off-by: Harshal Patil <harpatil@redhat.com>
  • Loading branch information
harche committed Apr 26, 2024
1 parent c051e2a commit 25f1558
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 30 deletions.
2 changes: 1 addition & 1 deletion store.go
Expand Up @@ -3710,7 +3710,7 @@ func DefaultConfigFile() (string, error) {
// the configuration in storeOptions.
// Deprecated: Use types.ReloadConfigurationFile, which can return an error.
func ReloadConfigurationFile(configFile string, storeOptions *types.StoreOptions) {
_ = types.ReloadConfigurationFile(configFile, storeOptions)
_ = types.ReloadConfigurationFile(configFile, storeOptions, true)
}

// GetDefaultMountOptions returns the default mountoptions defined in container/storage
Expand Down
72 changes: 64 additions & 8 deletions types/options.go
Expand Up @@ -49,6 +49,12 @@ var (
defaultConfigFile = SystemConfigFile
// DefaultStoreOptions is a reasonable default set of options.
defaultStoreOptions StoreOptions

// defaultOverrideConfigFile path to override the default system wide storage.conf file
defaultOverrideConfigFile = "/etc/containers/storage.conf"

// defaultDropInConfigDir path to the folder containing drop in config files
defaultDropInConfigDir = defaultOverrideConfigFile + ".d"
)

func loadDefaultStoreOptions() {
Expand Down Expand Up @@ -114,11 +120,53 @@ func loadDefaultStoreOptions() {

// loadStoreOptions returns the default storage ops for containers
func loadStoreOptions() (StoreOptions, error) {
storageConf, err := DefaultConfigFile()
baseConf, err := DefaultConfigFile()
if err != nil {
return defaultStoreOptions, err
}

// Load the base config file
baseOptions, err := loadStoreOptionsFromConfFile(baseConf)
if err != nil {
return defaultStoreOptions, err
}
return loadStoreOptionsFromConfFile(storageConf)

if _, err := os.Stat(defaultDropInConfigDir); err == nil {
// The directory exists, so merge the configuration from this directory
err = mergeConfigFromDirectory(&baseOptions, defaultDropInConfigDir)
if err != nil {
return defaultStoreOptions, err
}
} else if !os.IsNotExist(err) {
// There was an error other than the directory not existing
return defaultStoreOptions, err
}

return baseOptions, nil
}

func mergeConfigFromDirectory(baseOptions *StoreOptions, configDir string) error {
return filepath.Walk(configDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}

// Only consider files with .conf extension
if filepath.Ext(path) != ".conf" {
return nil
}

// Load drop-in options from the current file
err = ReloadConfigurationFile(path, baseOptions, false)
if err != nil {
return err
}

return nil
})
}

// usePerUserStorage returns whether the user private storage must be used.
Expand Down Expand Up @@ -399,7 +447,7 @@ func ReloadConfigurationFileIfNeeded(configFile string, storeOptions *StoreOptio
return nil
}

if err := ReloadConfigurationFile(configFile, storeOptions); err != nil {
if err := ReloadConfigurationFile(configFile, storeOptions, true); err != nil {
return err
}

Expand All @@ -412,7 +460,7 @@ func ReloadConfigurationFileIfNeeded(configFile string, storeOptions *StoreOptio

// ReloadConfigurationFile parses the specified configuration file and overrides
// the configuration in storeOptions.
func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) error {
func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions, initializeOptions bool) error {
config := new(TomlConfig)

meta, err := toml.DecodeFile(configFile, &config)
Expand All @@ -428,8 +476,11 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro
}
}

// Clear storeOptions of previous settings
*storeOptions = StoreOptions{}
if initializeOptions {
// Clear storeOptions of previous settings
*storeOptions = StoreOptions{}
}

if config.Storage.Driver != "" {
storeOptions.GraphDriverName = config.Storage.Driver
}
Expand Down Expand Up @@ -519,8 +570,13 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro
storeOptions.PullOptions = config.Storage.Options.PullOptions
}

storeOptions.DisableVolatile = config.Storage.Options.DisableVolatile
storeOptions.TransientStore = config.Storage.TransientStore
if config.Storage.Options.DisableVolatile {
storeOptions.DisableVolatile = config.Storage.Options.DisableVolatile
}

if config.Storage.TransientStore {
storeOptions.TransientStore = config.Storage.TransientStore
}

storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, cfg.GetGraphDriverOptions(storeOptions.GraphDriverName, config.Storage.Options)...)

Expand Down
2 changes: 0 additions & 2 deletions types/options_darwin.go
Expand Up @@ -8,8 +8,6 @@ const (
SystemConfigFile = "/usr/share/containers/storage.conf"
)

var defaultOverrideConfigFile = "/etc/containers/storage.conf"

// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
func canUseRootlessOverlay(home, runhome string) bool {
return false
Expand Down
5 changes: 0 additions & 5 deletions types/options_freebsd.go
Expand Up @@ -8,11 +8,6 @@ const (
SystemConfigFile = "/usr/local/share/containers/storage.conf"
)

// defaultConfigFile path to the system wide storage.conf file
var (
defaultOverrideConfigFile = "/usr/local/etc/containers/storage.conf"
)

// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
func canUseRootlessOverlay(home, runhome string) bool {
return false
Expand Down
5 changes: 0 additions & 5 deletions types/options_linux.go
Expand Up @@ -16,11 +16,6 @@ const (
SystemConfigFile = "/usr/share/containers/storage.conf"
)

// defaultConfigFile path to the system wide storage.conf file
var (
defaultOverrideConfigFile = "/etc/containers/storage.conf"
)

// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
func canUseRootlessOverlay(home, runhome string) bool {
// we check first for fuse-overlayfs since it is cheaper.
Expand Down
61 changes: 58 additions & 3 deletions types/options_test.go
Expand Up @@ -143,7 +143,7 @@ func TestSetRemapUIDsGIDsOpts(t *testing.T) {
},
}

err := ReloadConfigurationFile("./storage_test.conf", &remapOpts)
err := ReloadConfigurationFile("./storage_test.conf", &remapOpts, true)
require.NoError(t, err)
if !reflect.DeepEqual(uidmap, remapOpts.UIDMap) {
t.Errorf("Failed to set UIDMap: Expected %v Actual %v", uidmap, remapOpts.UIDMap)
Expand Down Expand Up @@ -185,7 +185,7 @@ remap-group = "%s"

mappings, err := idtools.NewIDMappings(user, user)
require.NoError(t, err)
err = ReloadConfigurationFile(configPath, &remapOpts)
err = ReloadConfigurationFile(configPath, &remapOpts, true)
require.NoError(t, err)
if !reflect.DeepEqual(mappings.UIDs(), remapOpts.UIDMap) {
t.Errorf("Failed to set UIDMap: Expected %v Actual %v", mappings.UIDs(), remapOpts.UIDMap)
Expand All @@ -199,10 +199,65 @@ func TestReloadConfigurationFile(t *testing.T) {
content := bytes.NewBufferString("")
logrus.SetOutput(content)
var storageOpts StoreOptions
err := ReloadConfigurationFile("./storage_broken.conf", &storageOpts)
err := ReloadConfigurationFile("./storage_broken.conf", &storageOpts, true)
require.NoError(t, err)
assert.Equal(t, storageOpts.RunRoot, "/run/containers/test")
logrus.SetOutput(os.Stderr)

assert.Equal(t, strings.Contains(content.String(), "Failed to decode the keys [\\\"foo\\\" \\\"storage.options.graphroot\\\"] from \\\"./storage_broken.conf\\\"\""), true)
}

func TestMergeConfigFromDirectory(t *testing.T) {
tempDir, err := os.MkdirTemp("", "testConfigDir")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)

// Creating a mix of files with .txt and .conf extensions
fileNames := []string{"config1.conf", "config2.conf", "ignore.txt", "config3.conf", "config4.txt"}
contents := []string{
`[storage]
runroot = 'temp/run1'
graphroot = 'temp/graph1'`,
`[storage]
runroot = 'temp/run2'
graphroot = 'temp/graph2'`,
`[storage]
runroot = 'should/ignore'
graphroot = 'should/ignore'`,
`[storage]
runroot = 'temp/run3'`,
`[storage]
runroot = 'temp/run4'
graphroot = 'temp/graph4'`,
}
for i, fileName := range fileNames {
filePath := filepath.Join(tempDir, fileName)
if err := os.WriteFile(filePath, []byte(contents[i]), 0o666); err != nil {
t.Fatalf("Failed to write to temp file: %v", err)
}
}

// Set base options
baseOptions := StoreOptions{
RunRoot: "initial/run",
GraphRoot: "initial/graph",
TransientStore: true,
}

// Expected results after merging configurations from only .conf files
expectedOptions := StoreOptions{
RunRoot: "temp/run3", // Last .conf file (config3.conf) read overrides earlier values
GraphRoot: "temp/graph2",
TransientStore: true,
}

// Run the merging function
err = mergeConfigFromDirectory(&baseOptions, tempDir)
if err != nil {
t.Fatalf("Error merging config from directory: %v", err)
}

assert.DeepEqual(t, expectedOptions, baseOptions)
}
5 changes: 0 additions & 5 deletions types/options_windows.go
Expand Up @@ -8,11 +8,6 @@ const (
SystemConfigFile = "/usr/share/containers/storage.conf"
)

// defaultConfigFile path to the system wide storage.conf file
var (
defaultOverrideConfigFile = "/etc/containers/storage.conf"
)

// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
func canUseRootlessOverlay(home, runhome string) bool {
return false
Expand Down
2 changes: 1 addition & 1 deletion types/utils.go
Expand Up @@ -66,7 +66,7 @@ func reloadConfigurationFileIfNeeded(configFile string, storeOptions *StoreOptio
return
}

ReloadConfigurationFile(configFile, storeOptions)
ReloadConfigurationFile(configFile, storeOptions, true)

prevReloadConfig.storeOptions = storeOptions
prevReloadConfig.mod = mtime
Expand Down

0 comments on commit 25f1558

Please sign in to comment.