diff --git a/cmd/podman/registry/registry.go b/cmd/podman/registry/registry.go index 42419074d6..45d8e1d831 100644 --- a/cmd/podman/registry/registry.go +++ b/cmd/podman/registry/registry.go @@ -2,6 +2,7 @@ package registry import ( "context" + "errors" "path/filepath" "github.com/sirupsen/logrus" @@ -72,6 +73,16 @@ func NewContainerEngine(cmd *cobra.Command, _ []string) (entities.ContainerEngin logrus.Debugf("Performing system renumber, runtime validation checks will be relaxed") podmanOptions.IsRenumber = true } + if cmd.Name() == "migrate" && cmd.Parent().Name() == "system" { + isMigrateDB, err := cmd.Flags().GetBool("migrate-db") + if err != nil { + return nil, errors.New("system migrate command missing flag migrate-db") + } + if isMigrateDB { + podmanOptions.IsMigrateDB = true + logrus.Debugf("Performing database migration, BoltDB checks will be relaxed") + } + } engine, err := infra.NewContainerEngine(&podmanOptions) if err != nil { return nil, err diff --git a/cmd/podman/root.go b/cmd/podman/root.go index e093be435a..991fb24cdd 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -382,7 +382,10 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { } // Prep the engines - if _, err := registry.NewImageEngine(cmd, args); err != nil { + // Container engine MUST be first. + // We have special handling in there for passing flags to the runtime, that is ignored by the image engine. + // Which creates a container engine, because there isn't an entrypoint to Libimage that isn't Libpod. + if _, err := registry.NewContainerEngine(cmd, args); err != nil { // Note: this is gross, but it is the hand we are dealt if registry.IsRemote() && errors.As(err, &bindings.ConnectError{}) && cmd.Parent() == cmd.Root() { switch cmd.Name() { @@ -404,7 +407,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { } return err } - if _, err := registry.NewContainerEngine(cmd, args); err != nil { + if _, err := registry.NewImageEngine(cmd, args); err != nil { return err } diff --git a/cmd/podman/system/migrate.go b/cmd/podman/system/migrate.go index 7bf36bf308..49a48062ca 100644 --- a/cmd/podman/system/migrate.go +++ b/cmd/podman/system/migrate.go @@ -44,6 +44,8 @@ func init() { newRuntimeFlagName := "new-runtime" flags.StringVar(&migrateOptions.NewRuntime, newRuntimeFlagName, "", "Specify a new runtime for all containers") _ = migrateCommand.RegisterFlagCompletionFunc(newRuntimeFlagName, completion.AutocompleteNone) + + flags.BoolVar(&migrateOptions.MigrateDB, "migrate-db", false, "Migrate database from BoltDB to SQLite") } func migrate(_ *cobra.Command, _ []string) error { diff --git a/docs/source/markdown/podman-system-migrate.1.md b/docs/source/markdown/podman-system-migrate.1.md index f39ccf7564..99a3c2b722 100644 --- a/docs/source/markdown/podman-system-migrate.1.md +++ b/docs/source/markdown/podman-system-migrate.1.md @@ -26,6 +26,17 @@ newly configured mappings. ## OPTIONS +#### **--migrate-db** + +Migrate from the legacy BoltDB database to SQLite. +Support for BoltDB has been removed in Podman 6.0, and existing BoltDB databases must be migrated to continue using the containers, pods, and volumes stored in them. +This is also done automatically on system reboot. +Migrating as part of a reboot is generally preferred as there is less potential for race conditions caused by other Podman processes running at the same time. +If a migration is necessary, Podman will fail to run with a descriptive error indicating this command must be used or the system must be rebooted. +To ensure complete migration, all other Podman commands should be shut down before database migration. +In particular, systemd-activated services like **podman system service** and Quadlets should be manually stopped prior to migration. +The legacy database will not be removed, so no data loss should occur even on failure. + #### **--new-runtime**=*runtime* Set a new OCI runtime for all containers. diff --git a/libpod/container_graph.go b/libpod/container_graph.go index 97689b2c39..6d721d458a 100644 --- a/libpod/container_graph.go +++ b/libpod/container_graph.go @@ -289,6 +289,67 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError } } +// Migrates all nodes from BoltDB to SQLite +func migrateNodeDatabase(node *containerNode, setError bool, ctrErrors map[string]error, ctrsVisited map[string]bool, sqliteState State) { + // First, check if we have already visited the node + if ctrsVisited[node.id] { + return + } + + // If setError is true, a dependency of us failed + // Mark us as failed and recurse + if setError { + // Mark us as visited, and set an error + ctrsVisited[node.id] = true + ctrErrors[node.id] = fmt.Errorf("a dependency of container %s failed to migrate: %w", node.id, define.ErrCtrStateInvalid) + + // Hit anyone who depends on us, and set errors on them too + for _, successor := range node.dependedOn { + migrateNodeDatabase(successor, true, ctrErrors, ctrsVisited, sqliteState) + } + + return + } + + // Have all our dependencies started? + // If not, don't visit the node yet + depsVisited := true + for _, dep := range node.dependsOn { + depsVisited = depsVisited && ctrsVisited[dep.id] + } + if !depsVisited { + // Don't visit us yet, all dependencies are not up + // We'll hit the dependencies eventually, and when we do it will + // recurse here + return + } + + // Going to try to migrate the container, mark us as visited + ctrsVisited[node.id] = true + + ctrErrored := false + + // Add and save the container + if err := sqliteState.AddContainer(node.container); err != nil { + if errors.Is(err, define.ErrCtrExists) { + logrus.Warnf("Container with name %s already exists in the SQLite database; refusing to migrate from BoltDB", node.container.Name()) + } else { + ctrErrored = true + ctrErrors[node.id] = err + } + } else { + if err := sqliteState.SaveContainer(node.container); err != nil { + ctrErrored = true + ctrErrors[node.id] = err + } + } + + // Recurse to anyone who depends on us and start them + for _, successor := range node.dependedOn { + migrateNodeDatabase(successor, ctrErrored, ctrErrors, ctrsVisited, sqliteState) + } +} + // Contains all details required for traversing the container graph. type nodeTraversal struct { // Optional. but *MUST* be locked. diff --git a/libpod/options.go b/libpod/options.go index 0224b39c4d..dba9e5d1fa 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -350,26 +350,6 @@ func WithNetworkConfigDir(dir string) RuntimeOption { } } -// WithNamespace sets the namespace for libpod. -// Namespaces are used to create scopes to separate containers and pods -// in the state. -// When namespace is set, libpod will only view containers and pods in -// the same namespace. All containers and pods created will default to -// the namespace set here. -// A namespace of "", the empty string, is equivalent to no namespace, -// and all containers and pods will be visible. -func WithNamespace(ns string) RuntimeOption { - return func(rt *Runtime) error { - if rt.valid { - return define.ErrRuntimeFinalized - } - - rt.config.Engine.Namespace = ns - - return nil - } -} - // WithVolumePath sets the path under which all named volumes // should be created. // The path changes based on whether the user is running as root or not. @@ -446,6 +426,9 @@ func WithEnableSDNotify() RuntimeOption { // WithSyslog sets a runtime option so we know that we have to log to the syslog as well func WithSyslog() RuntimeOption { return func(rt *Runtime) error { + if rt.valid { + return define.ErrRuntimeFinalized + } rt.syslog = true return nil } @@ -462,6 +445,20 @@ func WithRuntimeFlags(runtimeFlags []string) RuntimeOption { } } +// WithNoBoltError suppresses errors when a Bolt database exists, which would +// otherwise prevent creation of a runtime. Useful when migrating the BoltDB +// database to SQLite, which is otherwise impossible as we'll error before +// getting to the migration code. +func WithNoBoltError() RuntimeOption { + return func(rt *Runtime) error { + if rt.valid { + return define.ErrRuntimeFinalized + } + rt.noBoltError = true + return nil + } +} + // Container Creation Options // WithMaxLogSize sets the maximum size of container logs. diff --git a/libpod/runtime.go b/libpod/runtime.go index 5bb1769b30..0aa82d25aa 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "io/fs" "os" "path/filepath" "slices" @@ -107,6 +108,10 @@ type Runtime struct { // errors related to lock initialization so a renumber can be performed // if something has gone wrong. doRenumber bool + // Do not error on a BoltDB database existing. Useful for commanding + // a database migration, as we can't actually get to the migration code + // with an existing Bolt database otherwise. + noBoltError bool // valid indicates whether the runtime is ready to use. // valid is set to true when a runtime is returned from GetRuntime(), @@ -278,6 +283,8 @@ func getLockManager(runtime *Runtime) (lock.Manager, error) { return manager, nil } +var errBoltExists = errors.New("legacy database exists") + func getDBState(runtime *Runtime) (State, error) { // TODO - if we further break out the state implementation into // libpod/state, the config could take care of the code below. It @@ -290,8 +297,24 @@ func getDBState(runtime *Runtime) (State, error) { switch backend { case config.DBBackendBoltDB: - return nil, fmt.Errorf("the BoltDB database backend was removed in Podman 6.0") + return nil, fmt.Errorf("the BoltDB database backend was removed in Podman 6.0 - please comment out the `database_backend` line in containers.conf and migrate to SQLite by rebooting the system or using the `podman system migrate --migrate-db` command") case config.DBBackendDefault: + if !runtime.noBoltError { + boltDBPath := getBoltDBPath(runtime) + if err := fileutils.Exists(boltDBPath); err == nil { + // We need to return a valid state. + // The error below can be discarded if we are performing a state refresh - in which case we migrate the BoltDB database. + // Problem: We cannot know if a refresh is happening until almost the end of runtime init. + // And we can't really change that - it has to be after the database is set up. + // (We need paths from the state to know where to check for the alive file). + // Solution is to return a valid state, and defer handling the errBoltExists error until we are sure we are/are not refreshing. + sqliteState, err := NewSqliteState(runtime) + if err != nil { + return nil, err + } + return sqliteState, fmt.Errorf("a BoltDB database exists but is no longer being used. BoltDB support was removed in the Podman 6.0 release. The legacy database can be migrated to SQLite by rebooting and running Podman again or using the `podman system migrate --migrate-db` command. IMPORTANT: Only use the `system migrate` command when you can ensure there are no other Podman processes running. If you are not absolutely sure of this, it is strongly recommended that you reboot. If you do not care about the contents of the legacy database, you can rename or remove the legacy database at %q to suppress this error: %w", boltDBPath, errBoltExists) + } + } fallthrough case config.DBBackendSQLite: return NewSqliteState(runtime) @@ -341,10 +364,44 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { return fmt.Errorf("creating runtime volume path directory: %w", err) } + // Check if a SQLite DB exists prior to getting a DB. + // If it does not, and we have a Bolt database, we may need to remove the SQLite DB + // that is created below if we are not performing a database migration. + // This ensures that reverting to Podman 5.8.2 will not default to SQLite and break things. + sqliteDBExists := checkSQLiteDBExists(runtime) + // Set up the state. + boltExists := false + var boltError error runtime.state, err = getDBState(runtime) if err != nil { - return err + if errors.Is(err, errBoltExists) { + boltExists = true + boltError = err + } else { + return err + } + } + + // We did not have a SQLite DB before Podman ran. + // Remove the one that was created (if one was created) so we don't break reverting to 5.8 + removeSQLite := false + if !sqliteDBExists && boltExists { + removeSQLite = true + defer func() { + if removeSQLite { + // Do a final check that the BoltDB state still exists. + // If it doesn't, someone else probably performed a migration to SQLite before we got here. + // In that case, it'd be bad to remove the now in use database. + if err := fileutils.Exists(getBoltDBPath(runtime)); err != nil { + return + } + sqlitePath := sqliteStatePath(runtime) + if err := os.Remove(sqlitePath); err != nil && !errors.Is(err, fs.ErrNotExist) { + logrus.Errorf("Error removing SQLite DB %s created during Podman init: %v", sqlitePath, err) + } + } + }() } // Grab config from the database so we can reset some defaults @@ -572,11 +629,21 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { // As such, it's not really a performance concern if errors.Is(err, os.ErrNotExist) { doRefresh = true + removeSQLite = false } else { return fmt.Errorf("reading runtime status file %s: %w", runtimeAliveFile, err) } } + if !doRefresh && boltExists { + // If the BoltDB database still exists, throw an error. + // If it doesn't: assume that migration happened in the background. + // In that case, safe to proceed. + if err := fileutils.Exists(getBoltDBPath(runtime)); err == nil || !errors.Is(err, fs.ErrNotExist) { + return boltError + } + } + runtime.lockManager, err = getLockManager(runtime) if err != nil { return err @@ -792,6 +859,14 @@ func (r *Runtime) Shutdown(force bool) error { func (r *Runtime) refresh(ctx context.Context, alivePath string) error { logrus.Debugf("Podman detected system restart - performing state refresh") + // Only error that can be returned is no BoltDB present. + // In that case, no need to do anything. + if err := r.checkCanMigrate(); err == nil { + if err := r.migrateDB(); err != nil { + logrus.Errorf("Automatic migration from BoltDB to SQLite failed: %v", err) + } + } + // Clear state of database if not running in container if !graphRootMounted() { // First clear the state in the database diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go index b624ef4bb3..ec050f9337 100644 --- a/libpod/runtime_migrate.go +++ b/libpod/runtime_migrate.go @@ -3,19 +3,22 @@ package libpod import ( + "errors" "fmt" + "os" "path/filepath" "strings" "github.com/sirupsen/logrus" "go.podman.io/podman/v6/libpod/define" "go.podman.io/podman/v6/pkg/namespaces" + "go.podman.io/storage/pkg/fileutils" ) // Migrate stops the rootless pause process and performs any necessary database // migrations that are required. It can also migrate all containers to a new OCI // runtime, if requested. -func (r *Runtime) Migrate(newRuntime string) error { +func (r *Runtime) Migrate(newRuntime string, migrateDB bool) error { // Acquire the alive lock and hold it. // Ensures that we don't let other Podman commands run while we are // rewriting things in the DB. @@ -97,5 +100,138 @@ func (r *Runtime) Migrate(newRuntime string) error { } } + if migrateDB { + if err := r.checkCanMigrate(); err != nil { + switch { + case errors.Is(err, errCannotMigrateNoBolt): + fmt.Printf("No migration is necessary: %v\n", err) + return r.stopPauseProcess() + default: + return err + } + } + + if err := r.migrateDB(); err != nil { + return fmt.Errorf("migrating database from BoltDB to SQLite: %w", err) + } + } + return r.stopPauseProcess() } + +var errCannotMigrateNoBolt = errors.New("no BoltDB database to migrate") + +func (r *Runtime) checkCanMigrate() error { + boltPath := getBoltDBPath(r) + if err := fileutils.Exists(boltPath); err != nil { + return errCannotMigrateNoBolt + } + + return nil +} + +func (r *Runtime) migrateDB() error { + boltPath := getBoltDBPath(r) + // Get us a Bolt database + oldState, err := NewBoltState(boltPath, r) + if err != nil { + return fmt.Errorf("opening legacy Bolt database at %s: %w", boltPath, err) + } + + // Migrate volumes, then pods, then containers. + // Containers must be last as the pods they are part of and volumes they use must already exist. + allVolumes, err := oldState.AllVolumes() + if err != nil { + return fmt.Errorf("retrieving volumes from boltdb: %w", err) + } + for _, vol := range allVolumes { + if err := r.state.AddVolume(vol); err != nil { + if errors.Is(err, define.ErrVolumeExists) { + logrus.Warnf("Volume with name %s already exists in the SQLite database; refusing to migrate from BoltDB", vol.Name()) + continue + } + return err + } + if err := oldState.UpdateVolume(vol); err != nil { + return err + } + if err := r.state.SaveVolume(vol); err != nil { + return err + } + } + + allPods, err := oldState.AllPods() + if err != nil { + return fmt.Errorf("retrieving pods from boltdb: %w", err) + } + for _, pod := range allPods { + if err := r.state.AddPod(pod); err != nil { + if errors.Is(err, define.ErrPodExists) { + logrus.Warnf("Pod with name %s already exists in the SQLite database; refusing to migrate from BoltDB", pod.Name()) + continue + } + return err + } + if err := oldState.UpdatePod(pod); err != nil { + return err + } + if err := r.state.SavePod(pod); err != nil { + return err + } + } + + // Containers must be done as a graph due to dependencies. + // The state will error if we add a container before its dependencies. + allCtrs, err := oldState.AllContainers(true) + if err != nil { + return fmt.Errorf("retrieving containers from boltdb: %w", err) + } + + // BoltDB doesn't actually populate container networks on initial pull + // from the database, that needs to be done separately. + for _, ctr := range allCtrs { + ctrNetworks, err := oldState.GetNetworks(ctr) + if err != nil { + return err + } + ctr.config.Networks = convertLegacyNetworks(ctrNetworks) + } + + graph, err := BuildContainerGraph(allCtrs) + if err != nil { + return err + } + + ctrErrors := make(map[string]error) + ctrsVisited := make(map[string]bool) + + for _, node := range graph.noDepNodes { + migrateNodeDatabase(node, false, ctrErrors, ctrsVisited, r.state) + } + if len(ctrErrors) > 0 { + newErrors := make([]error, 0, len(ctrErrors)) + for id, err := range ctrErrors { + newErrors = append(newErrors, fmt.Errorf("migrating container %s: %w", id, err)) + } + return errors.Join(newErrors...) + } + + oldState.Close() + + // Move the Bolt database so it is not reused, but preserve so data is not lost. + newBoltDBPath := fmt.Sprintf("%s-old", boltPath) + if err := os.Rename(boltPath, newBoltDBPath); err != nil { + return fmt.Errorf("renaming old database %s to %s: %w", boltPath, newBoltDBPath, err) + } + fmt.Printf("Old database has been renamed to %s and will no longer be used\n", newBoltDBPath) + + return nil +} + +func getBoltDBPath(runtime *Runtime) string { + baseDir := runtime.config.Engine.StaticDir + if runtime.storageConfig.TransientStore { + baseDir = runtime.config.Engine.TmpDir + } + return filepath.Join(baseDir, "bolt_state.db") +} diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go index 125d429296..8a40b9480d 100644 --- a/libpod/sqlite_state.go +++ b/libpod/sqlite_state.go @@ -32,6 +32,8 @@ type SQLiteState struct { } const ( + // Name of the actual database file + sqliteDbFilename = "db.sql" // Deal with timezone automatically. sqliteOptionLocation = "_loc=auto" // Force an fsync after each transaction (https://www.sqlite.org/pragma.html#pragma_synchronous). @@ -44,7 +46,7 @@ const ( sqliteOptionCaseSensitiveLike = "&_cslike=TRUE" // Assembled sqlite options used when opening the database. - sqliteOptions = "db.sql?" + + sqliteOptions = "?" + sqliteOptionLocation + sqliteOptionSynchronous + sqliteOptionForeignKeys + @@ -57,17 +59,12 @@ func NewSqliteState(runtime *Runtime) (_ State, defErr error) { logrus.Info("Using sqlite as database backend") state := new(SQLiteState) - basePath := runtime.storageConfig.GraphRoot - if runtime.storageConfig.TransientStore { - basePath = runtime.storageConfig.RunRoot - } else if !runtime.storageSet.StaticDirSet { - basePath = runtime.config.Engine.StaticDir - } + dbPath := sqliteStatePath(runtime) // c/storage is set up *after* the DB - so even though we use the c/s // root (or, for transient, runroot) dir, we need to make the dir // ourselves. - if err := os.MkdirAll(basePath, 0o700); err != nil { + if err := os.MkdirAll(filepath.Dir(dbPath), 0o700); err != nil { return nil, fmt.Errorf("creating root directory: %w", err) } @@ -82,7 +79,7 @@ func NewSqliteState(runtime *Runtime) (_ State, defErr error) { } sqliteOptionBusyTimeout := "&_busy_timeout=" + busyTimeout - conn, err := sql.Open("sqlite3", filepath.Join(basePath, sqliteOptions+sqliteOptionBusyTimeout)) + conn, err := sql.Open("sqlite3", dbPath+sqliteOptions+sqliteOptionBusyTimeout) if err != nil { return nil, fmt.Errorf("initializing sqlite database: %w", err) } diff --git a/libpod/sqlite_state_internal.go b/libpod/sqlite_state_internal.go index 7487abec84..d6f1032359 100644 --- a/libpod/sqlite_state_internal.go +++ b/libpod/sqlite_state_internal.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "slices" "sort" "strings" @@ -14,11 +15,30 @@ import ( "github.com/sirupsen/logrus" "go.podman.io/common/libnetwork/types" "go.podman.io/podman/v6/libpod/define" + "go.podman.io/storage/pkg/fileutils" // SQLite backend for database/sql _ "github.com/mattn/go-sqlite3" ) +// sqliteStatePath returns the path to the sqlite file. +func sqliteStatePath(runtime *Runtime) string { + basePath := runtime.storageConfig.GraphRoot + if runtime.storageConfig.TransientStore { + basePath = runtime.storageConfig.RunRoot + } else if !runtime.storageSet.StaticDirSet { + basePath = runtime.config.Engine.StaticDir + } + return filepath.Join(basePath, sqliteDbFilename) +} + +func checkSQLiteDBExists(runtime *Runtime) bool { + if err := fileutils.Exists(sqliteStatePath(runtime)); err != nil { + return false + } + return true +} + func initSQLiteDB(conn *sql.DB) (defErr error) { // Start with a transaction to avoid "database locked" errors. // See https://github.com/mattn/go-sqlite3/issues/274#issuecomment-1429054597 diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index c98c625c5c..4bd7feb3ac 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -43,6 +43,7 @@ type PodmanConfig struct { TLSDetailsFile string // Path to a containers-tls-details.yaml(5) file IsRenumber bool // Is this a system renumber command? If so, a number of checks will be relaxed IsReset bool // Is this a system reset command? If so, a number of checks will be skipped/omitted + IsMigrateDB bool // Is this a system migrate --migrate-db command? If so, certain BoltDB related errors will be suppressed MaxWorks int // maximum number of parallel threads MemoryProfile string // Hidden: Should memory profile be taken RegistriesConf string // allows for specifying a custom registries.conf diff --git a/pkg/domain/entities/types/system.go b/pkg/domain/entities/types/system.go index 711332205b..2a940297eb 100644 --- a/pkg/domain/entities/types/system.go +++ b/pkg/domain/entities/types/system.go @@ -63,6 +63,7 @@ type SystemPruneReport struct { // cli to migrate runtimes of containers type SystemMigrateOptions struct { NewRuntime string + MigrateDB bool } // SystemDfOptions describes the options for getting df information diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index fb5868b2ee..8e4a3f8d79 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -309,7 +309,7 @@ func (ic *ContainerEngine) Renumber(_ context.Context) error { } func (ic *ContainerEngine) Migrate(_ context.Context, options entities.SystemMigrateOptions) error { - return ic.Libpod.Migrate(options.NewRuntime) + return ic.Libpod.Migrate(options.NewRuntime, options.MigrateDB) } func unshareEnv(graphroot, runroot string) []string { diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index 71cf1c05f8..260eec21db 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -32,20 +32,22 @@ var ( ) type engineOpts struct { - withFDS bool - reset bool - renumber bool - config *entities.PodmanConfig + withFDS bool + reset bool + renumber bool + noBoltError bool + config *entities.PodmanConfig } // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(ctx context.Context, flags *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) { runtimeSync.Do(func() { runtimeLib, runtimeErr = getRuntime(ctx, flags, &engineOpts{ - withFDS: true, - reset: cfg.IsReset, - renumber: cfg.IsRenumber, - config: cfg, + withFDS: true, + reset: cfg.IsReset, + renumber: cfg.IsRenumber, + noBoltError: cfg.IsMigrateDB, + config: cfg, }) }) return runtimeLib, runtimeErr @@ -132,6 +134,9 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo if opts.renumber { options = append(options, libpod.WithRenumber()) } + if opts.noBoltError { + options = append(options, libpod.WithNoBoltError()) + } if len(cfg.RuntimeFlags) > 0 { runtimeFlags := []string{} @@ -149,10 +154,6 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo // TODO CLI flags for image config? // TODO CLI flag for signature policy? - if len(cfg.ContainersConf.Engine.Namespace) > 0 { - options = append(options, libpod.WithNamespace(cfg.ContainersConf.Engine.Namespace)) - } - if fs.Changed("runtime") { options = append(options, libpod.WithOCIRuntime(cfg.RuntimePath)) }