From 525f0b30ac280565a1f98fd4208821a4984a7515 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Mon, 15 Apr 2019 22:03:47 +0200 Subject: [PATCH] system: add new subcommand "migrate" it is useful to migrate existing containers to a new version of podman. Currently, it is needed to migrate rootless containers that were created with podman <= 1.2 to a newer version which requires all containers to be running in the same user namespace. Closes: https://github.com/containers/libpod/issues/2935 Signed-off-by: Giuseppe Scrivano --- cmd/podman/cliconfig/config.go | 4 +++ cmd/podman/commands.go | 1 + cmd/podman/libpodruntime/runtime.go | 14 ++++++-- cmd/podman/main_local.go | 2 +- cmd/podman/system_migrate.go | 50 +++++++++++++++++++++++++++++ docs/podman-system-migrate.1.md | 21 ++++++++++++ docs/podman-system.1.md | 1 + libpod/options.go | 16 +++++++++ libpod/runtime.go | 20 ++++++++++++ libpod/runtime_migrate.go | 48 +++++++++++++++++++++++++++ 10 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 cmd/podman/system_migrate.go create mode 100644 docs/podman-system-migrate.1.md create mode 100644 libpod/runtime_migrate.go diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 640a4bff44f5..77156f47a9ef 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -581,6 +581,10 @@ type SystemRenumberValues struct { PodmanCommand } +type SystemMigrateValues struct { + PodmanCommand +} + type SystemDfValues struct { PodmanCommand Verbose bool diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index c43ecec5ceb3..36c28696f0b9 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -77,6 +77,7 @@ func getSystemSubCommands() []*cobra.Command { _pruneSystemCommand, _renumberCommand, _dfSystemCommand, + _migrateCommand, } } diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index 78adf1252908..595f8267f230 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -9,17 +9,22 @@ import ( "github.com/pkg/errors" ) +// GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers +func GetRuntimeMigrate(c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { + return getRuntime(c, false, true) +} + // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber func GetRuntimeRenumber(c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(c, true) + return getRuntime(c, true, false) } // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(c, false) + return getRuntime(c, false, false) } -func getRuntime(c *cliconfig.PodmanCommand, renumber bool) (*libpod.Runtime, error) { +func getRuntime(c *cliconfig.PodmanCommand, renumber bool, migrate bool) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} storageSet := false @@ -63,6 +68,9 @@ func getRuntime(c *cliconfig.PodmanCommand, renumber bool) (*libpod.Runtime, err storageSet = true storageOpts.GraphDriverOptions = c.GlobalFlags.StorageOpts } + if migrate { + options = append(options, libpod.WithMigrate()) + } if renumber { options = append(options, libpod.WithRenumber()) diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 91ad426301b3..42a8e434e50f 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -103,7 +103,7 @@ func profileOff(cmd *cobra.Command) error { } func setupRootless(cmd *cobra.Command, args []string) error { - if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || cmd == _mountCommand || strings.HasPrefix(cmd.Use, "help") { + if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || cmd == _mountCommand || cmd == _migrateCommand || strings.HasPrefix(cmd.Use, "help") { return nil } podmanCmd := cliconfig.PodmanCommand{ diff --git a/cmd/podman/system_migrate.go b/cmd/podman/system_migrate.go new file mode 100644 index 000000000000..b08eedcde03f --- /dev/null +++ b/cmd/podman/system_migrate.go @@ -0,0 +1,50 @@ +package main + +import ( + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + migrateCommand cliconfig.SystemMigrateValues + migrateDescription = ` + podman system migrate + + Migrate existing containers to a new version of Podman. +` + + _migrateCommand = &cobra.Command{ + Use: "migrate", + Args: noSubArgs, + Short: "Migrate containers", + Long: migrateDescription, + RunE: func(cmd *cobra.Command, args []string) error { + migrateCommand.InputArgs = args + migrateCommand.GlobalFlags = MainGlobalOpts + return migrateCmd(&migrateCommand) + }, + } +) + +func init() { + migrateCommand.Command = _migrateCommand + migrateCommand.SetHelpTemplate(HelpTemplate()) + migrateCommand.SetUsageTemplate(UsageTemplate()) +} + +func migrateCmd(c *cliconfig.SystemMigrateValues) error { + // We need to pass one extra option to NewRuntime. + // This will inform the OCI runtime to start a migrate. + // That's controlled by the last argument to GetRuntime. + r, err := libpodruntime.GetRuntimeMigrate(&c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "error migrating containers") + } + if err := r.Shutdown(false); err != nil { + return err + } + + return nil +} diff --git a/docs/podman-system-migrate.1.md b/docs/podman-system-migrate.1.md new file mode 100644 index 000000000000..7c2d1823cb23 --- /dev/null +++ b/docs/podman-system-migrate.1.md @@ -0,0 +1,21 @@ +% podman-system-migrate(1) podman + +## NAME +podman\-system\-migrate - Migrate container to the latest version of podman + +## SYNOPSIS +** podman system migrate** + +## DESCRIPTION +** podman system migrate** migrates containers to the latest podman version. + +**podman system migrate** takes care of migrating existing containers to the latest version of podman if any change is necessary. + +## SYNOPSIS +**podman system migrate** + +## SEE ALSO +`podman(1)`, `libpod.conf(5)` + +# HISTORY +April 2019, Originally compiled by Giuseppe Scrivano (gscrivan at redhat dot com) diff --git a/docs/podman-system.1.md b/docs/podman-system.1.md index 32b3efdd9ae4..d36715feb1cc 100644 --- a/docs/podman-system.1.md +++ b/docs/podman-system.1.md @@ -17,6 +17,7 @@ The system command allows you to manage the podman systems | info | [podman-system-info(1)](podman-info.1.md) | Displays Podman related system information. | | prune | [podman-system-prune(1)](podman-system-prune.1.md) | Remove all unused data | | renumber | [podman-system-renumber(1)](podman-system-renumber.1.md)| Migrate lock numbers to handle a change in maximum number of locks. | +| migrate | [podman-system-migrate(1)](podman-system-migrate.1.md)| Migrate existing containers to a new podman version. | ## SEE ALSO podman(1) diff --git a/libpod/options.go b/libpod/options.go index 8038f1935fd7..949894dcbb9a 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -436,6 +436,22 @@ func WithRenumber() RuntimeOption { } } +// WithMigrate instructs libpod to perform a lock migrateing while +// initializing. This will handle migrations from early versions of libpod with +// file locks to newer versions with SHM locking, as well as changes in the +// number of configured locks. +func WithMigrate() RuntimeOption { + return func(rt *Runtime) error { + if rt.valid { + return ErrRuntimeFinalized + } + + rt.doMigrate = true + + return nil + } +} + // Container Creation Options // WithShmDir sets the directory that should be mounted on /dev/shm. diff --git a/libpod/runtime.go b/libpod/runtime.go index 69cc1038950d..6d279a107909 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -100,6 +100,8 @@ type Runtime struct { // unused. doRenumber bool + doMigrate bool + // valid indicates whether the runtime is ready to use. // valid is set to true when a runtime is returned from GetRuntime(), // and remains true until the runtime is shut down (rendering its @@ -962,6 +964,24 @@ func makeRuntime(runtime *Runtime) (err error) { // further runtime.valid = true + if runtime.doMigrate { + if os.Geteuid() != 0 { + aliveLock.Unlock() + locked = false + + became, ret, err := rootless.BecomeRootInUserNS() + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } + if err := runtime.migrate(); err != nil { + return err + } + } + return nil } diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go new file mode 100644 index 000000000000..7d7fa108da4d --- /dev/null +++ b/libpod/runtime_migrate.go @@ -0,0 +1,48 @@ +package libpod + +import ( + "context" + "path/filepath" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (r *Runtime) migrate() error { + runningContainers, err := r.GetRunningContainers() + if err != nil { + return err + } + + allCtrs, err := r.state.AllContainers() + if err != nil { + return err + } + + logrus.Infof("stopping all containers") + for _, ctr := range runningContainers { + logrus.Infof("stopping %s", ctr.ID()) + if err := ctr.Stop(); err != nil { + return errors.Wrapf(err, "cannot stop container %s", ctr.ID()) + } + } + + for _, ctr := range allCtrs { + oldLocation := filepath.Join(ctr.state.RunDir, "conmon.pid") + if ctr.config.ConmonPidFile == oldLocation { + logrus.Infof("changing conmon PID file for %s", ctr.ID()) + ctr.config.ConmonPidFile = filepath.Join(ctr.config.StaticDir, "conmon.pid") + if err := r.state.RewriteContainerConfig(ctr, ctr.config); err != nil { + return errors.Wrapf(err, "error rewriting config for container %s", ctr.ID()) + } + } + } + + for _, ctr := range runningContainers { + if err := ctr.Start(context.Background(), true); err != nil { + logrus.Errorf("error restarting container %s", ctr.ID()) + } + } + + return nil +}