From 958759a71955860b01b17bd3bebf38f9dae1018e Mon Sep 17 00:00:00 2001 From: cdoern Date: Fri, 13 May 2022 10:52:57 -0400 Subject: podman pod clone implement podman pod clone, a command to create an exact copy of a pod while changing certain config elements current supported flags are: --name change the pod name --destroy remove the original pod --start run the new pod on creation and all infra-container related flags from podman pod create (namespaces etc) resolves #12843 Signed-off-by: cdoern --- cmd/podman/common/completion.go | 6 +++ cmd/podman/common/create.go | 31 +++++++------- cmd/podman/common/create_opts.go | 22 +++++++++- cmd/podman/containers/clone.go | 1 + cmd/podman/containers/create.go | 1 + cmd/podman/containers/run.go | 1 + cmd/podman/pods/clone.go | 87 ++++++++++++++++++++++++++++++++++++++++ cmd/podman/pods/create.go | 1 + 8 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 cmd/podman/pods/clone.go (limited to 'cmd') diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 07dcc4e6a..aeb051001 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -1607,8 +1607,14 @@ func AutocompleteClone(cmd *cobra.Command, args []string, toComplete string) ([] } switch len(args) { case 0: + if cmd.Parent().Name() == "pod" { // needs to be " pod " to exclude 'podman' + return getPods(cmd, toComplete, completeDefault) + } return getContainers(cmd, toComplete, completeDefault) case 2: + if cmd.Parent().Name() == "pod" { + return nil, cobra.ShellCompDirectiveNoFileComp + } return getImages(cmd, toComplete) } return nil, cobra.ShellCompDirectiveNoFileComp diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index f89035be3..40bb0df78 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -98,7 +98,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, cgroupsFlagName := "cgroups" createFlags.StringVar( &cf.CgroupsMode, - cgroupsFlagName, cgroupConfig(), + cgroupsFlagName, cf.CgroupsMode, `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, ) _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) @@ -150,7 +150,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, envFlagName := "env" createFlags.StringArrayP( - envFlagName, "e", env(), + envFlagName, "e", Env(), "Set environment variables in container", ) _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) @@ -256,7 +256,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, imageVolumeFlagName := "image-volume" createFlags.String( - imageVolumeFlagName, containerConfig.Engine.ImageVolumeMode, + imageVolumeFlagName, cf.ImageVolume, `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, ) _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) @@ -298,7 +298,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, logDriverFlagName := "log-driver" createFlags.StringVar( &cf.LogDriver, - logDriverFlagName, LogDriver(), + logDriverFlagName, cf.LogDriver, "Logging driver for the container", ) _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) @@ -389,7 +389,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, pullFlagName := "pull" createFlags.StringVar( &cf.Pull, - pullFlagName, policy(), + pullFlagName, cf.Pull, `Pull image before creating ("always"|"missing"|"never")`, ) _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) @@ -406,7 +406,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ) createFlags.BoolVar( &cf.ReadOnlyTmpFS, - "read-only-tmpfs", true, + "read-only-tmpfs", cf.ReadOnlyTmpFS, "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", ) requiresFlagName := "requires" @@ -439,7 +439,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, sdnotifyFlagName := "sdnotify" createFlags.StringVar( &cf.SdNotifyMode, - sdnotifyFlagName, define.SdNotifyModeContainer, + sdnotifyFlagName, cf.SdNotifyMode, `control sd-notify behavior ("container"|"conmon"|"ignore")`, ) _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) @@ -470,7 +470,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, stopTimeoutFlagName := "stop-timeout" createFlags.UintVar( &cf.StopTimeout, - stopTimeoutFlagName, containerConfig.Engine.StopTimeout, + stopTimeoutFlagName, cf.StopTimeout, "Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.", ) _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) @@ -478,7 +478,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, systemdFlagName := "systemd" createFlags.StringVar( &cf.Systemd, - systemdFlagName, "true", + systemdFlagName, cf.Systemd, `Run container in systemd mode ("true"|"false"|"always")`, ) _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) @@ -522,7 +522,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, timezoneFlagName := "tz" createFlags.StringVar( &cf.Timezone, - timezoneFlagName, containerConfig.TZ(), + timezoneFlagName, cf.Timezone, "Set timezone in container", ) _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion @@ -530,7 +530,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, umaskFlagName := "umask" createFlags.StringVar( &cf.Umask, - umaskFlagName, containerConfig.Umask(), + umaskFlagName, cf.Umask, "Set umask in container", ) _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) @@ -538,7 +538,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, ulimitFlagName := "ulimit" createFlags.StringSliceVar( &cf.Ulimit, - ulimitFlagName, ulimits(), + ulimitFlagName, cf.Ulimit, "Ulimit options", ) _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) @@ -577,7 +577,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, seccompPolicyFlagName := "seccomp-policy" createFlags.StringVar( &cf.SeccompPolicy, - seccompPolicyFlagName, "default", + seccompPolicyFlagName, cf.SeccompPolicy, "Policy for selecting a seccomp profile (experimental)", ) _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) @@ -769,7 +769,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, volumeFlagName := "volume" createFlags.StringArrayVarP( &cf.Volume, - volumeFlagName, "v", volumes(), + volumeFlagName, "v", cf.Volume, volumeDesciption, ) _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) @@ -890,12 +890,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, memorySwappinessFlagName := "memory-swappiness" createFlags.Int64Var( &cf.MemorySwappiness, - memorySwappinessFlagName, -1, + memorySwappinessFlagName, cf.MemorySwappiness, "Tune container memory swappiness (0 to 100, or -1 for system default)", ) _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) } // anyone can use these + cpusFlagName := "cpus" createFlags.Float64Var( &cf.CPUS, diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index ad535ff59..fb5af8f59 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -2,6 +2,8 @@ package common import ( "github.com/containers/podman/v4/cmd/podman/registry" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/domain/entities" ) func ulimits() []string { @@ -25,7 +27,7 @@ func devices() []string { return nil } -func env() []string { +func Env() []string { if !registry.IsRemote() { return containerConfig.Env() } @@ -73,3 +75,21 @@ func LogDriver() string { } return "" } + +// DefineCreateDefault is used to initialize ctr create options before flag initialization +func DefineCreateDefaults(opts *entities.ContainerCreateOptions) { + opts.LogDriver = LogDriver() + opts.CgroupsMode = cgroupConfig() + opts.MemorySwappiness = -1 + opts.ImageVolume = containerConfig.Engine.ImageVolumeMode + opts.Pull = policy() + opts.ReadOnlyTmpFS = true + opts.SdNotifyMode = define.SdNotifyModeContainer + opts.StopTimeout = containerConfig.Engine.StopTimeout + opts.Systemd = "true" + opts.Timezone = containerConfig.TZ() + opts.Umask = containerConfig.Umask() + opts.Ulimit = ulimits() + opts.SeccompPolicy = "default" + opts.Volume = volumes() +} diff --git a/cmd/podman/containers/clone.go b/cmd/podman/containers/clone.go index 6912da1fc..2763adf3c 100644 --- a/cmd/podman/containers/clone.go +++ b/cmd/podman/containers/clone.go @@ -41,6 +41,7 @@ func cloneFlags(cmd *cobra.Command) { forceFlagName := "force" flags.BoolVarP(&ctrClone.Force, forceFlagName, "f", false, "force the existing container to be destroyed") + common.DefineCreateDefaults(&ctrClone.CreateOpts) common.DefineCreateFlags(cmd, &ctrClone.CreateOpts, false, true) } func init() { diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 0a513c606..a214ae8aa 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -71,6 +71,7 @@ func createFlags(cmd *cobra.Command) { ) flags.SetInterspersed(false) + common.DefineCreateDefaults(&cliVals) common.DefineCreateFlags(cmd, &cliVals, false, false) common.DefineNetFlags(cmd) diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index a6c500afa..e49bdaee4 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -61,6 +61,7 @@ func runFlags(cmd *cobra.Command) { flags := cmd.Flags() flags.SetInterspersed(false) + common.DefineCreateDefaults(&cliVals) common.DefineCreateFlags(cmd, &cliVals, false, false) common.DefineNetFlags(cmd) diff --git a/cmd/podman/pods/clone.go b/cmd/podman/pods/clone.go new file mode 100644 index 000000000..d95d74b05 --- /dev/null +++ b/cmd/podman/pods/clone.go @@ -0,0 +1,87 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/registry" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + podCloneDescription = `Create an exact copy of a pod and the containers within it` + + podCloneCommand = &cobra.Command{ + Use: "clone [options] POD NAME", + Short: "Clone an existing pod", + Long: podCloneDescription, + RunE: clone, + Args: cobra.RangeArgs(1, 2), + ValidArgsFunction: common.AutocompleteClone, + Example: `podman pod clone pod_name new_name`, + } +) + +var ( + podClone entities.PodCloneOptions +) + +func cloneFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + destroyFlagName := "destroy" + flags.BoolVar(&podClone.Destroy, destroyFlagName, false, "destroy the original pod") + + startFlagName := "start" + flags.BoolVar(&podClone.Start, startFlagName, false, "start the new pod") + + nameFlagName := "name" + flags.StringVarP(&podClone.CreateOpts.Name, nameFlagName, "n", "", "name the new pod") + _ = podCloneCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + + common.DefineCreateDefaults(&podClone.InfraOptions) + common.DefineCreateFlags(cmd, &podClone.InfraOptions, true, false) + podClone.InfraOptions.MemorySwappiness = -1 // this is not implemented for pods yet, need to set -1 default manually + + // need to fill an empty ctr create option for each container for sane defaults + // for now, these cannot be used. The flag names conflict too much + // this makes sense since this is a pod command not a container command + // TODO: add support for container specific arguments/flags + common.DefineCreateDefaults(&podClone.PerContainerOptions) +} +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: podCloneCommand, + Parent: podCmd, + }) + + cloneFlags(podCloneCommand) +} + +func clone(cmd *cobra.Command, args []string) error { + switch len(args) { + case 0: + return errors.Wrapf(define.ErrInvalidArg, "must specify at least 1 argument") + case 2: + podClone.CreateOpts.Name = args[1] + } + + podClone.ID = args[0] + podClone.PerContainerOptions.IsClone = true + rep, err := registry.ContainerEngine().PodClone(context.Background(), podClone) + if err != nil { + if rep != nil { + fmt.Printf("pod %s created but error after creation\n", rep.Id) + } + return err + } + + fmt.Println(rep.Id) + + return nil +} diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index e2f80bdbc..ca9d60174 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -64,6 +64,7 @@ func init() { }) flags := createCommand.Flags() flags.SetInterspersed(false) + common.DefineCreateDefaults(&infraOptions) common.DefineCreateFlags(createCommand, &infraOptions, true, false) common.DefineNetFlags(createCommand) -- cgit v1.2.3-54-g00ecf