Skip to content

Commit 25f1558

Browse files
committed
Add support for drop-in config
Signed-off-by: Harshal Patil <[email protected]>
1 parent c051e2a commit 25f1558

8 files changed

+124
-30
lines changed

store.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -3710,7 +3710,7 @@ func DefaultConfigFile() (string, error) {
37103710
// the configuration in storeOptions.
37113711
// Deprecated: Use types.ReloadConfigurationFile, which can return an error.
37123712
func ReloadConfigurationFile(configFile string, storeOptions *types.StoreOptions) {
3713-
_ = types.ReloadConfigurationFile(configFile, storeOptions)
3713+
_ = types.ReloadConfigurationFile(configFile, storeOptions, true)
37143714
}
37153715

37163716
// GetDefaultMountOptions returns the default mountoptions defined in container/storage

types/options.go

+64-8
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ var (
4949
defaultConfigFile = SystemConfigFile
5050
// DefaultStoreOptions is a reasonable default set of options.
5151
defaultStoreOptions StoreOptions
52+
53+
// defaultOverrideConfigFile path to override the default system wide storage.conf file
54+
defaultOverrideConfigFile = "/etc/containers/storage.conf"
55+
56+
// defaultDropInConfigDir path to the folder containing drop in config files
57+
defaultDropInConfigDir = defaultOverrideConfigFile + ".d"
5258
)
5359

5460
func loadDefaultStoreOptions() {
@@ -114,11 +120,53 @@ func loadDefaultStoreOptions() {
114120

115121
// loadStoreOptions returns the default storage ops for containers
116122
func loadStoreOptions() (StoreOptions, error) {
117-
storageConf, err := DefaultConfigFile()
123+
baseConf, err := DefaultConfigFile()
124+
if err != nil {
125+
return defaultStoreOptions, err
126+
}
127+
128+
// Load the base config file
129+
baseOptions, err := loadStoreOptionsFromConfFile(baseConf)
118130
if err != nil {
119131
return defaultStoreOptions, err
120132
}
121-
return loadStoreOptionsFromConfFile(storageConf)
133+
134+
if _, err := os.Stat(defaultDropInConfigDir); err == nil {
135+
// The directory exists, so merge the configuration from this directory
136+
err = mergeConfigFromDirectory(&baseOptions, defaultDropInConfigDir)
137+
if err != nil {
138+
return defaultStoreOptions, err
139+
}
140+
} else if !os.IsNotExist(err) {
141+
// There was an error other than the directory not existing
142+
return defaultStoreOptions, err
143+
}
144+
145+
return baseOptions, nil
146+
}
147+
148+
func mergeConfigFromDirectory(baseOptions *StoreOptions, configDir string) error {
149+
return filepath.Walk(configDir, func(path string, info os.FileInfo, err error) error {
150+
if err != nil {
151+
return err
152+
}
153+
if info.IsDir() {
154+
return nil
155+
}
156+
157+
// Only consider files with .conf extension
158+
if filepath.Ext(path) != ".conf" {
159+
return nil
160+
}
161+
162+
// Load drop-in options from the current file
163+
err = ReloadConfigurationFile(path, baseOptions, false)
164+
if err != nil {
165+
return err
166+
}
167+
168+
return nil
169+
})
122170
}
123171

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

402-
if err := ReloadConfigurationFile(configFile, storeOptions); err != nil {
450+
if err := ReloadConfigurationFile(configFile, storeOptions, true); err != nil {
403451
return err
404452
}
405453

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

413461
// ReloadConfigurationFile parses the specified configuration file and overrides
414462
// the configuration in storeOptions.
415-
func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) error {
463+
func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions, initializeOptions bool) error {
416464
config := new(TomlConfig)
417465

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

431-
// Clear storeOptions of previous settings
432-
*storeOptions = StoreOptions{}
479+
if initializeOptions {
480+
// Clear storeOptions of previous settings
481+
*storeOptions = StoreOptions{}
482+
}
483+
433484
if config.Storage.Driver != "" {
434485
storeOptions.GraphDriverName = config.Storage.Driver
435486
}
@@ -519,8 +570,13 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro
519570
storeOptions.PullOptions = config.Storage.Options.PullOptions
520571
}
521572

522-
storeOptions.DisableVolatile = config.Storage.Options.DisableVolatile
523-
storeOptions.TransientStore = config.Storage.TransientStore
573+
if config.Storage.Options.DisableVolatile {
574+
storeOptions.DisableVolatile = config.Storage.Options.DisableVolatile
575+
}
576+
577+
if config.Storage.TransientStore {
578+
storeOptions.TransientStore = config.Storage.TransientStore
579+
}
524580

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

types/options_darwin.go

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ const (
88
SystemConfigFile = "/usr/share/containers/storage.conf"
99
)
1010

11-
var defaultOverrideConfigFile = "/etc/containers/storage.conf"
12-
1311
// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
1412
func canUseRootlessOverlay(home, runhome string) bool {
1513
return false

types/options_freebsd.go

-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ const (
88
SystemConfigFile = "/usr/local/share/containers/storage.conf"
99
)
1010

11-
// defaultConfigFile path to the system wide storage.conf file
12-
var (
13-
defaultOverrideConfigFile = "/usr/local/etc/containers/storage.conf"
14-
)
15-
1611
// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
1712
func canUseRootlessOverlay(home, runhome string) bool {
1813
return false

types/options_linux.go

-5
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@ const (
1616
SystemConfigFile = "/usr/share/containers/storage.conf"
1717
)
1818

19-
// defaultConfigFile path to the system wide storage.conf file
20-
var (
21-
defaultOverrideConfigFile = "/etc/containers/storage.conf"
22-
)
23-
2419
// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
2520
func canUseRootlessOverlay(home, runhome string) bool {
2621
// we check first for fuse-overlayfs since it is cheaper.

types/options_test.go

+58-3
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func TestSetRemapUIDsGIDsOpts(t *testing.T) {
143143
},
144144
}
145145

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

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

207207
assert.Equal(t, strings.Contains(content.String(), "Failed to decode the keys [\\\"foo\\\" \\\"storage.options.graphroot\\\"] from \\\"./storage_broken.conf\\\"\""), true)
208208
}
209+
210+
func TestMergeConfigFromDirectory(t *testing.T) {
211+
tempDir, err := os.MkdirTemp("", "testConfigDir")
212+
if err != nil {
213+
t.Fatalf("Failed to create temp dir: %v", err)
214+
}
215+
defer os.RemoveAll(tempDir)
216+
217+
// Creating a mix of files with .txt and .conf extensions
218+
fileNames := []string{"config1.conf", "config2.conf", "ignore.txt", "config3.conf", "config4.txt"}
219+
contents := []string{
220+
`[storage]
221+
runroot = 'temp/run1'
222+
graphroot = 'temp/graph1'`,
223+
`[storage]
224+
runroot = 'temp/run2'
225+
graphroot = 'temp/graph2'`,
226+
`[storage]
227+
runroot = 'should/ignore'
228+
graphroot = 'should/ignore'`,
229+
`[storage]
230+
runroot = 'temp/run3'`,
231+
`[storage]
232+
runroot = 'temp/run4'
233+
graphroot = 'temp/graph4'`,
234+
}
235+
for i, fileName := range fileNames {
236+
filePath := filepath.Join(tempDir, fileName)
237+
if err := os.WriteFile(filePath, []byte(contents[i]), 0o666); err != nil {
238+
t.Fatalf("Failed to write to temp file: %v", err)
239+
}
240+
}
241+
242+
// Set base options
243+
baseOptions := StoreOptions{
244+
RunRoot: "initial/run",
245+
GraphRoot: "initial/graph",
246+
TransientStore: true,
247+
}
248+
249+
// Expected results after merging configurations from only .conf files
250+
expectedOptions := StoreOptions{
251+
RunRoot: "temp/run3", // Last .conf file (config3.conf) read overrides earlier values
252+
GraphRoot: "temp/graph2",
253+
TransientStore: true,
254+
}
255+
256+
// Run the merging function
257+
err = mergeConfigFromDirectory(&baseOptions, tempDir)
258+
if err != nil {
259+
t.Fatalf("Error merging config from directory: %v", err)
260+
}
261+
262+
assert.DeepEqual(t, expectedOptions, baseOptions)
263+
}

types/options_windows.go

-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ const (
88
SystemConfigFile = "/usr/share/containers/storage.conf"
99
)
1010

11-
// defaultConfigFile path to the system wide storage.conf file
12-
var (
13-
defaultOverrideConfigFile = "/etc/containers/storage.conf"
14-
)
15-
1611
// canUseRootlessOverlay returns true if the overlay driver can be used for rootless containers
1712
func canUseRootlessOverlay(home, runhome string) bool {
1813
return false

types/utils.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func reloadConfigurationFileIfNeeded(configFile string, storeOptions *StoreOptio
6666
return
6767
}
6868

69-
ReloadConfigurationFile(configFile, storeOptions)
69+
ReloadConfigurationFile(configFile, storeOptions, true)
7070

7171
prevReloadConfig.storeOptions = storeOptions
7272
prevReloadConfig.mod = mtime

0 commit comments

Comments
 (0)