Skip to content

Commit 9549389

Browse files
committed
Add ability to rewrite cached paths in the database
This is accomplished by a new flag, `--rewrite-config`, which instructs the database to replace cached paths. Before this, the only real way to change paths like tmpdir, runroot, graphroot, etc was to do a `podman system reset` then change the config files before running another Podman command. Now, changing the config files and running any Podman command with `--rewrite-config` should be sufficient to pick up the new paths. Please note that this can only be done with no containers, pods, and volumes present. Otherwise, we risk the breakages that caching paths was supposed to prevent in the first place. This is SQLite only, given the deprecation and impending removal of BoltDB. Signed-off-by: Matt Heon <[email protected]>
1 parent 098f1eb commit 9549389

File tree

12 files changed

+186
-7
lines changed

12 files changed

+186
-7
lines changed

cmd/podman/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,8 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
595595
_ = cmd.RegisterFlagCompletionFunc(networkBackendFlagName, common.AutocompleteNetworkBackend)
596596
_ = pFlags.MarkHidden(networkBackendFlagName)
597597

598+
pFlags.BoolVar(&podmanConfig.IsRewrite, "rewrite-config", false, "Rewrite cached database configuration")
599+
598600
rootFlagName := "root"
599601
pFlags.StringVar(&podmanConfig.GraphRoot, rootFlagName, "", "Path to the graph root directory where images, containers, etc. are stored")
600602
_ = cmd.RegisterFlagCompletionFunc(rootFlagName, completion.AutocompleteDefault)

docs/source/markdown/podman.1.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ When true, access to the Podman service is remote. Defaults to false.
124124
Settings can be modified in the containers.conf file. If the CONTAINER_HOST
125125
environment variable is set, the **--remote** option defaults to true.
126126

127+
#### **--rewrite-config**
128+
When true, cached configuration values in the database will be rewritten.
129+
Normally, changes to certain configuration values - graphDriver, graphRoot, and runRoot in storage.conf, as well as static_dir, tmp_dir, and volume_path in containers.conf - will be ignored until a `podman system reset`, as old values cached in the database will be used.
130+
This is done to ensure that configuration changes do not break existing pods, containers, and volumes present in the database.
131+
This option rewrites the cached values in the database, replacing them with the current configuration.
132+
This can only be done if no containers, pods, and volumes are present, to prevent the breakage described earlier.
133+
If any containers, pods, or volumes are present, an error will be returned.
134+
127135
#### **--root**=*value*
128136

129137
Storage root dir in which data, including images, is stored (default: "/var/lib/containers/storage" for UID 0, "$HOME/.local/share/containers/storage" for other users).

libpod/boltdb_state.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,11 +493,15 @@ func (s *BoltState) GetDBConfig() (*DBConfig, error) {
493493
}
494494

495495
// ValidateDBConfig validates paths in the given runtime against the database
496-
func (s *BoltState) ValidateDBConfig(runtime *Runtime) error {
496+
func (s *BoltState) ValidateDBConfig(runtime *Runtime, performRewrite bool) error {
497497
if !s.valid {
498498
return define.ErrDBClosed
499499
}
500500

501+
if performRewrite {
502+
return fmt.Errorf("rewriting database config is not allowed with the boltdb database backend: %w", define.ErrNotSupported)
503+
}
504+
501505
db, err := s.getDBCon()
502506
if err != nil {
503507
return err

libpod/define/errors.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ var (
148148
// yet present
149149
ErrNotImplemented = errors.New("not yet implemented")
150150

151+
// ErrNotSupported indicates this function is not supported.
152+
ErrNotSupported = errors.New("not supported")
151153
// ErrOSNotSupported indicates the function is not available on the particular
152154
// OS.
153155
ErrOSNotSupported = errors.New("no support for this OS yet")

libpod/options.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,23 @@ func WithRenumber() RuntimeOption {
430430
}
431431
}
432432

433+
// WithRewrite tells Libpod that the runtime should rewrite cached configuration
434+
// paths in the database.
435+
// This must be used to make configuration changes to certain paths take effect.
436+
// This option can only be used if no containers, pods, and volumes exist.
437+
// The runtime is fully usable after being returned.
438+
func WithRewrite() RuntimeOption {
439+
return func(rt *Runtime) error {
440+
if rt.valid {
441+
return define.ErrRuntimeFinalized
442+
}
443+
444+
rt.doRewrite = true
445+
446+
return nil
447+
}
448+
}
449+
433450
// WithEventsLogger sets the events backend to use.
434451
// Currently supported values are "file" for file backend and "journald" for
435452
// journald backend.

libpod/runtime.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ type Runtime struct {
110110
// errors related to lock initialization so a renumber can be performed
111111
// if something has gone wrong.
112112
doRenumber bool
113+
// doRewrite indicates that the runtime will overwrite cached values in
114+
// the database for a number of paths in the configuration (e.g.
115+
// graphroot, runroot, tmpdir). These paths will otherwise be overridden
116+
// by cached versions when changed by the user, requiring a wipe of the
117+
// database to change.
118+
// If doRewrite is set, the returned runtime is fully usable.
119+
// If doRewrite is set and any containers, pods, or volumes are present
120+
// in the database, an error will be returned during runtime init.
121+
doRewrite bool
113122

114123
// valid indicates whether the runtime is ready to use.
115124
// valid is set to true when a runtime is returned from GetRuntime(),
@@ -394,7 +403,11 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
394403
return fmt.Errorf("retrieving runtime configuration from database: %w", err)
395404
}
396405

397-
runtime.mergeDBConfig(dbConfig)
406+
if !runtime.doRewrite {
407+
runtime.mergeDBConfig(dbConfig)
408+
} else {
409+
logrus.Debugf("Going to rewrite cached paths in database. Values below will be used for new cached configuration.")
410+
}
398411

399412
checkCgroups2UnifiedMode(runtime)
400413

@@ -408,7 +421,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
408421

409422
// Validate our config against the database, now that we've set our
410423
// final storage configuration
411-
if err := runtime.state.ValidateDBConfig(runtime); err != nil {
424+
if err := runtime.state.ValidateDBConfig(runtime, runtime.doRewrite); err != nil {
412425
// If we are performing a storage reset: continue on with a
413426
// warning. Otherwise we can't `system reset` after a change to
414427
// the core paths.

libpod/sqlite_state.go

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ func (s *SQLiteState) GetDBConfig() (*DBConfig, error) {
306306
}
307307

308308
// ValidateDBConfig validates paths in the given runtime against the database
309-
func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
309+
func (s *SQLiteState) ValidateDBConfig(runtime *Runtime, performRewrite bool) (defErr error) {
310310
if !s.valid {
311311
return define.ErrDBClosed
312312
}
@@ -322,6 +322,20 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
322322
?, ?, ?,
323323
?, ?, ?
324324
);`
325+
const getNumObject = `
326+
SELECT
327+
(SELECT COUNT(*) FROM ContainerConfig) AS container_count,
328+
(SELECT COUNT(*) FROM PodConfig) AS pod_count,
329+
(SELECT COUNT(*) FROM VolumeConfig) AS volume_count;`
330+
const updateRow = `
331+
UPDATE DBConfig SET
332+
OS=?,
333+
StaticDir=?,
334+
TmpDir=?,
335+
GraphRoot=?,
336+
RunRoot=?,
337+
GraphDriver=?,
338+
VolumeDir=?;`
325339

326340
var (
327341
dbOS, staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumePath string
@@ -335,7 +349,9 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
335349
)
336350

337351
// Some fields may be empty, indicating they are set to the default.
338-
// If so, grab the default from c/storage for them.
352+
// If so, grab the values from c/storage for them.
353+
// TODO: Validate the c/storage ones are not empty string,
354+
// grab default values if they are.
339355
if runtimeGraphRoot == "" {
340356
runtimeGraphRoot = storeOpts.GraphRoot
341357
}
@@ -386,6 +402,37 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
386402
return fmt.Errorf("retrieving DB config: %w", err)
387403
}
388404

405+
if performRewrite {
406+
// If a rewrite of database configuration is requested:
407+
// First ensure no containers, pods, volumes are present.
408+
// If clear to proceed, update the row and return. Do not
409+
// perform any checks; use current configuration without
410+
// question, as we would on DB init.
411+
var numCtrs, numPods, numVols int
412+
countRow := tx.QueryRow(getNumObject)
413+
if err := countRow.Scan(&numCtrs, &numPods, &numVols); err != nil {
414+
return fmt.Errorf("querying number of objects in database: %w", err)
415+
}
416+
if numCtrs+numPods+numVols != 0 {
417+
return fmt.Errorf("refusing to rewrite database cached configuration as containers, pods, or volumes are present: %w", define.ErrInternal)
418+
}
419+
result, err := tx.Exec(updateRow, runtimeOS, runtimeStaticDir, runtimeTmpDir, runtimeGraphRoot, runtimeRunRoot, runtimeGraphDriver, runtimeVolumePath)
420+
if err != nil {
421+
return fmt.Errorf("updating database cached configuration: %w", err)
422+
}
423+
rows, err := result.RowsAffected()
424+
if err != nil {
425+
return fmt.Errorf("counting rows affected by DB configuration update: %w", err)
426+
}
427+
if rows != 1 {
428+
return fmt.Errorf("updated %d rows when changing DB configuration, expected 1: %w", rows, define.ErrInternal)
429+
}
430+
if err := tx.Commit(); err != nil {
431+
return fmt.Errorf("committing write of database validation row: %w", err)
432+
}
433+
return nil
434+
}
435+
389436
// Sometimes, for as-yet unclear reasons, the database value ends up set
390437
// to the empty string. If it does, this evaluation is always going to
391438
// fail, and libpod will be unusable.

libpod/state.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ type State interface { //nolint:interfacebloat
3939
// This is not implemented by the in-memory state, as it has no need to
4040
// validate runtime configuration that may change over multiple runs of
4141
// the program.
42-
ValidateDBConfig(runtime *Runtime) error
42+
// If performRewrite is set, the current configuration values will be
43+
// overwritten by the values given in the current runtime struct.
44+
// This occurs if and only if no containers, pods, and volumes are
45+
// present.
46+
ValidateDBConfig(runtime *Runtime, performRewrite bool) error
4347

4448
// Resolve an ID to a Container Name.
4549
GetContainerName(id string) (string, error)
@@ -164,7 +168,7 @@ type State interface { //nolint:interfacebloat
164168
// There are a lot of capital letters and conditions here, but the short
165169
// answer is this: use this only very sparingly, and only if you really
166170
// know what you're doing.
167-
// TODO: Once BoltDB is removed, RewriteContainerConfig and
171+
// TODO 6.0: Once BoltDB is removed, RewriteContainerConfig and
168172
// SafeRewriteContainerConfig can be merged.
169173
RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error
170174
// This is a more limited version of RewriteContainerConfig, though it

pkg/domain/entities/engine.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type PodmanConfig struct {
3939
Identity string // ssh identity for connecting to server
4040
IsRenumber bool // Is this a system renumber command? If so, a number of checks will be relaxed
4141
IsReset bool // Is this a system reset command? If so, a number of checks will be skipped/omitted
42+
IsRewrite bool // Rewrite cached database configuration.
4243
MaxWorks int // maximum number of parallel threads
4344
MemoryProfile string // Hidden: Should memory profile be taken
4445
RegistriesConf string // allows for specifying a custom registries.conf

pkg/domain/infra/runtime_libpod.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
134134
if opts.renumber {
135135
options = append(options, libpod.WithRenumber())
136136
}
137+
if opts.config.IsRewrite {
138+
options = append(options, libpod.WithRewrite())
139+
}
137140

138141
if len(cfg.RuntimeFlags) > 0 {
139142
runtimeFlags := []string{}

0 commit comments

Comments
 (0)