aboutsummaryrefslogtreecommitdiff
path: root/pkg/specgenutil
diff options
context:
space:
mode:
authorcdoern <cdoern@redhat.com>2022-01-14 13:49:34 -0500
committercdoern <cdoern@redhat.com>2022-02-20 21:11:14 -0500
commit94df7015121759ce69f35f7e7735aa2e4a2dc71a (patch)
tree154e04dc3073acc57cecedfc703e6c1c4daf4bf1 /pkg/specgenutil
parentf918a9418f5eeb00b289c127142953da2c394867 (diff)
downloadpodman-94df7015121759ce69f35f7e7735aa2e4a2dc71a.tar.gz
podman-94df7015121759ce69f35f7e7735aa2e4a2dc71a.tar.bz2
podman-94df7015121759ce69f35f7e7735aa2e4a2dc71a.zip
Implement Podman Container Clone
podman container clone takes the id of an existing continer and creates a specgen from the given container's config recreating all proper namespaces and overriding spec options like resource limits and the container name if given in the cli options this command utilizes the common function DefineCreateFlags meaning that we can funnel as many create options as we want into clone over time allowing the user to clone with as much or as little of the original config as they want. container clone takes a second argument which is a new name and a third argument which is an image name to use instead of the original container's the current supported flags are: --destroy (remove the original container) --name (new ctr name) --cpus (sets cpu period and quota) --cpuset-cpus --cpu-period --cpu-rt-period --cpu-rt-runtime --cpu-shares --cpuset-mems --memory --run resolves #10875 Signed-off-by: cdoern <cdoern@redhat.com> Signed-off-by: cdoern <cbdoer23@g.holycross.edu> Signed-off-by: cdoern <cdoern@redhat.com>
Diffstat (limited to 'pkg/specgenutil')
-rw-r--r--pkg/specgenutil/createparse.go2
-rw-r--r--pkg/specgenutil/specgen.go295
2 files changed, 209 insertions, 88 deletions
diff --git a/pkg/specgenutil/createparse.go b/pkg/specgenutil/createparse.go
index 9666e4be0..a51396227 100644
--- a/pkg/specgenutil/createparse.go
+++ b/pkg/specgenutil/createparse.go
@@ -26,6 +26,8 @@ func validate(c *entities.ContainerCreateOptions) error {
if _, ok := imageVolType[c.ImageVolume]; !ok {
if c.IsInfra {
c.ImageVolume = "bind"
+ } else if c.IsClone { // the image volume type will be deduced later from the container we are cloning
+ return nil
} else {
return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume)
}
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index 17699a038..b037e14cc 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -256,38 +256,43 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
if err := setNamespaces(s, c); err != nil {
return err
}
- userNS := namespaces.UsernsMode(s.UserNS.NSMode)
- tempIDMap, err := util.ParseIDMapping(namespaces.UsernsMode(c.UserNS), []string{}, []string{}, "", "")
- if err != nil {
- return err
- }
- s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName)
- if err != nil {
- return err
- }
- if len(s.IDMappings.GIDMap) == 0 {
- s.IDMappings.AutoUserNsOpts.AdditionalGIDMappings = tempIDMap.AutoUserNsOpts.AdditionalGIDMappings
- if s.UserNS.NSMode == specgen.NamespaceMode("auto") {
- s.IDMappings.AutoUserNs = true
+
+ if s.IDMappings == nil {
+ userNS := namespaces.UsernsMode(s.UserNS.NSMode)
+ tempIDMap, err := util.ParseIDMapping(namespaces.UsernsMode(c.UserNS), []string{}, []string{}, "", "")
+ if err != nil {
+ return err
}
- }
- if len(s.IDMappings.UIDMap) == 0 {
- s.IDMappings.AutoUserNsOpts.AdditionalUIDMappings = tempIDMap.AutoUserNsOpts.AdditionalUIDMappings
- if s.UserNS.NSMode == specgen.NamespaceMode("auto") {
- s.IDMappings.AutoUserNs = true
+ s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName)
+ if err != nil {
+ return err
+ }
+ if len(s.IDMappings.GIDMap) == 0 {
+ s.IDMappings.AutoUserNsOpts.AdditionalGIDMappings = tempIDMap.AutoUserNsOpts.AdditionalGIDMappings
+ if s.UserNS.NSMode == specgen.NamespaceMode("auto") {
+ s.IDMappings.AutoUserNs = true
+ }
+ }
+ if len(s.IDMappings.UIDMap) == 0 {
+ s.IDMappings.AutoUserNsOpts.AdditionalUIDMappings = tempIDMap.AutoUserNsOpts.AdditionalUIDMappings
+ if s.UserNS.NSMode == specgen.NamespaceMode("auto") {
+ s.IDMappings.AutoUserNs = true
+ }
+ }
+ if tempIDMap.AutoUserNsOpts.Size != 0 {
+ s.IDMappings.AutoUserNsOpts.Size = tempIDMap.AutoUserNsOpts.Size
+ }
+ // If some mappings are specified, assume a private user namespace
+ if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) {
+ s.UserNS.NSMode = specgen.Private
+ } else {
+ s.UserNS.NSMode = specgen.NamespaceMode(userNS)
}
- }
- if tempIDMap.AutoUserNsOpts.Size != 0 {
- s.IDMappings.AutoUserNsOpts.Size = tempIDMap.AutoUserNsOpts.Size
- }
- // If some mappings are specified, assume a private user namespace
- if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) {
- s.UserNS.NSMode = specgen.Private
- } else {
- s.UserNS.NSMode = specgen.NamespaceMode(userNS)
}
- s.Terminal = c.TTY
+ if !s.Terminal {
+ s.Terminal = c.TTY
+ }
if err := verifyExpose(c.Expose); err != nil {
return err
@@ -297,8 +302,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
if c.Net != nil {
s.PortMappings = c.Net.PublishPorts
}
- s.PublishExposedPorts = c.PublishAll
- s.Pod = c.Pod
+ if !s.PublishExposedPorts {
+ s.PublishExposedPorts = c.PublishAll
+ }
+
+ if len(s.Pod) == 0 {
+ s.Pod = c.Pod
+ }
if len(c.PodIDFile) > 0 {
if len(s.Pod) > 0 {
@@ -315,7 +325,10 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
if err != nil {
return err
}
- s.Expose = expose
+
+ if len(s.Expose) == 0 {
+ s.Expose = expose
+ }
if sig := c.StopSignal; len(sig) > 0 {
stopSignal, err := util.ParseSignal(sig)
@@ -341,8 +354,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
return errors.Wrap(err, "error parsing host environment variables")
}
- s.EnvHost = c.EnvHost
- s.HTTPProxy = c.HTTPProxy
+ if !s.EnvHost {
+ s.EnvHost = c.EnvHost
+ }
+
+ if !s.HTTPProxy {
+ s.HTTPProxy = c.HTTPProxy
+ }
// env-file overrides any previous variables
for _, f := range c.EnvFile {
@@ -359,7 +377,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
return err
}
- s.Env = envLib.Join(env, parsedEnv)
+ if len(s.Env) == 0 {
+ s.Env = envLib.Join(env, parsedEnv)
+ }
// LABEL VARIABLES
labels, err := parse.GetAllLabels(c.LabelFile, c.Label)
@@ -371,7 +391,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
labels[systemdDefine.EnvVariable] = systemdUnit
}
- s.Labels = labels
+ if len(s.Labels) == 0 {
+ s.Labels = labels
+ }
// ANNOTATIONS
annotations := make(map[string]string)
@@ -390,7 +412,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
}
annotations[splitAnnotation[0]] = splitAnnotation[1]
}
- s.Annotations = annotations
+ if len(s.Annotations) == 0 {
+ s.Annotations = annotations
+ }
if len(c.StorageOpts) > 0 {
opts := make(map[string]string, len(c.StorageOpts))
@@ -403,7 +427,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
}
s.StorageOpts = opts
}
- s.WorkDir = c.Workdir
+ if len(s.WorkDir) == 0 {
+ s.WorkDir = c.Workdir
+ }
if c.Entrypoint != nil {
entrypoint := []string{}
// Check if entrypoint specified is json
@@ -415,7 +441,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
// Include the command used to create the container.
- s.ContainerCreateCommand = os.Args
+ if len(s.ContainerCreateCommand) == 0 {
+ s.ContainerCreateCommand = os.Args
+ }
if len(inputCommand) > 0 {
s.Command = inputCommand
@@ -444,24 +472,37 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.NetworkOptions = c.Net.NetworkOptions
s.UseImageHosts = c.Net.NoHosts
}
- s.HostUsers = c.HostUsers
- s.ImageVolumeMode = c.ImageVolume
+ if len(s.HostUsers) == 0 || len(c.HostUsers) != 0 {
+ s.HostUsers = c.HostUsers
+ }
+ if len(s.ImageVolumeMode) == 0 || len(c.ImageVolume) != 0 {
+ s.ImageVolumeMode = c.ImageVolume
+ }
if s.ImageVolumeMode == "bind" {
s.ImageVolumeMode = "anonymous"
}
- s.Systemd = strings.ToLower(c.Systemd)
- s.SdNotifyMode = c.SdNotifyMode
+ if len(s.Systemd) == 0 || len(c.Systemd) != 0 {
+ s.Systemd = strings.ToLower(c.Systemd)
+ }
+ if len(s.SdNotifyMode) == 0 || len(c.SdNotifyMode) != 0 {
+ s.SdNotifyMode = c.SdNotifyMode
+ }
if s.ResourceLimits == nil {
s.ResourceLimits = &specs.LinuxResources{}
}
- s.ResourceLimits.Memory, err = getMemoryLimits(s, c)
- if err != nil {
- return err
+
+ if s.ResourceLimits.Memory == nil || (len(c.Memory) != 0 || len(c.MemoryReservation) != 0 || len(c.MemorySwap) != 0 || c.MemorySwappiness != 0) {
+ s.ResourceLimits.Memory, err = getMemoryLimits(s, c)
+ if err != nil {
+ return err
+ }
}
- s.ResourceLimits.BlockIO, err = getIOLimits(s, c)
- if err != nil {
- return err
+ if s.ResourceLimits.BlockIO == nil || (len(c.BlkIOWeight) != 0 || len(c.BlkIOWeightDevice) != 0) {
+ s.ResourceLimits.BlockIO, err = getIOLimits(s, c)
+ if err != nil {
+ return err
+ }
}
if c.PIDsLimit != nil {
pids := specs.LinuxPids{
@@ -470,7 +511,10 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.ResourceLimits.Pids = &pids
}
- s.ResourceLimits.CPU = getCPULimits(c)
+
+ if s.ResourceLimits.CPU == nil || (c.CPUPeriod != 0 || c.CPUQuota != 0 || c.CPURTPeriod != 0 || c.CPURTRuntime != 0 || c.CPUS != 0 || len(c.CPUSetCPUs) != 0 || len(c.CPUSetMems) != 0 || c.CPUShares != 0) {
+ s.ResourceLimits.CPU = getCPULimits(c)
+ }
unifieds := make(map[string]string)
for _, unified := range c.CgroupConf {
@@ -495,8 +539,12 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
if ld := c.LogDriver; len(ld) > 0 {
s.LogConfiguration.Driver = ld
}
- s.CgroupParent = c.CgroupParent
- s.CgroupsMode = c.CgroupsMode
+ if len(s.CgroupParent) == 0 || len(c.CgroupParent) != 0 {
+ s.CgroupParent = c.CgroupParent
+ }
+ if len(s.CgroupsMode) == 0 {
+ s.CgroupsMode = c.CgroupsMode
+ }
if s.CgroupsMode == "" {
rtc, err := config.Default()
if err != nil {
@@ -506,9 +554,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.CgroupsMode = rtc.Cgroups()
}
- s.Groups = c.GroupAdd
+ if len(s.Groups) == 0 || len(c.GroupAdd) != 0 {
+ s.Groups = c.GroupAdd
+ }
- s.Hostname = c.Hostname
+ if len(s.Hostname) == 0 || len(c.Hostname) != 0 {
+ s.Hostname = c.Hostname
+ }
sysctl := map[string]string{}
if ctl := c.Sysctl; len(ctl) > 0 {
sysctl, err = util.ValidateSysctls(ctl)
@@ -516,15 +568,29 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
return err
}
}
- s.Sysctl = sysctl
+ if len(s.Sysctl) == 0 || len(c.Sysctl) != 0 {
+ s.Sysctl = sysctl
+ }
- s.CapAdd = c.CapAdd
- s.CapDrop = c.CapDrop
- s.Privileged = c.Privileged
- s.ReadOnlyFilesystem = c.ReadOnly
- s.ConmonPidFile = c.ConmonPIDFile
+ if len(s.CapAdd) == 0 || len(c.CapAdd) != 0 {
+ s.CapAdd = c.CapAdd
+ }
+ if len(s.CapDrop) == 0 || len(c.CapDrop) != 0 {
+ s.CapDrop = c.CapDrop
+ }
+ if !s.Privileged {
+ s.Privileged = c.Privileged
+ }
+ if !s.ReadOnlyFilesystem {
+ s.ReadOnlyFilesystem = c.ReadOnly
+ }
+ if len(s.ConmonPidFile) == 0 || len(c.ConmonPIDFile) != 0 {
+ s.ConmonPidFile = c.ConmonPIDFile
+ }
- s.DependencyContainers = c.Requires
+ if len(s.DependencyContainers) == 0 || len(c.Requires) != 0 {
+ s.DependencyContainers = c.Requires
+ }
// TODO
// outside of specgen and oci though
@@ -540,7 +606,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
}
sysmap[splitCtl[0]] = splitCtl[1]
}
- s.Sysctl = sysmap
+ if len(s.Sysctl) == 0 || len(c.Sysctl) != 0 {
+ s.Sysctl = sysmap
+ }
if c.CIDFile != "" {
s.Annotations[define.InspectAnnotationCIDFile] = c.CIDFile
@@ -584,9 +652,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
}
}
- s.SeccompPolicy = c.SeccompPolicy
+ if len(s.SeccompPolicy) == 0 || len(c.SeccompPolicy) != 0 {
+ s.SeccompPolicy = c.SeccompPolicy
+ }
- s.VolumesFrom = c.VolumesFrom
+ if len(s.VolumesFrom) == 0 || len(c.VolumesFrom) != 0 {
+ s.VolumesFrom = c.VolumesFrom
+ }
// Only add read-only tmpfs mounts in case that we are read-only and the
// read-only tmpfs flag has been set.
@@ -594,10 +666,19 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
if err != nil {
return err
}
- s.Mounts = mounts
- s.Volumes = volumes
- s.OverlayVolumes = overlayVolumes
- s.ImageVolumes = imageVolumes
+ if len(s.Mounts) == 0 || len(c.Mount) != 0 {
+ s.Mounts = mounts
+ }
+ if len(s.Volumes) == 0 || len(c.Volume) != 0 {
+ s.Volumes = volumes
+ }
+ // TODO make sure these work in clone
+ if len(s.OverlayVolumes) == 0 {
+ s.OverlayVolumes = overlayVolumes
+ }
+ if len(s.ImageVolumes) == 0 {
+ s.ImageVolumes = imageVolumes
+ }
for _, dev := range c.Devices {
s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev})
@@ -611,9 +692,15 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.DeviceCgroupRule = append(s.DeviceCgroupRule, dev)
}
- s.Init = c.Init
- s.InitPath = c.InitPath
- s.Stdin = c.Interactive
+ if !s.Init {
+ s.Init = c.Init
+ }
+ if len(s.InitPath) == 0 || len(c.InitPath) != 0 {
+ s.InitPath = c.InitPath
+ }
+ if !s.Stdin {
+ s.Stdin = c.Interactive
+ }
// quiet
// DeviceCgroupRules: c.StringSlice("device-cgroup-rule"),
@@ -656,11 +743,19 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
logOpts[split[0]] = split[1]
}
}
- s.LogConfiguration.Options = logOpts
- s.Name = c.Name
- s.PreserveFDs = c.PreserveFDs
+ if len(s.LogConfiguration.Options) == 0 || len(c.LogOptions) != 0 {
+ s.LogConfiguration.Options = logOpts
+ }
+ if len(s.Name) == 0 || len(c.Name) != 0 {
+ s.Name = c.Name
+ }
+ if s.PreserveFDs == 0 || c.PreserveFDs != 0 {
+ s.PreserveFDs = c.PreserveFDs
+ }
- s.OOMScoreAdj = &c.OOMScoreAdj
+ if s.OOMScoreAdj == nil || c.OOMScoreAdj != 0 {
+ s.OOMScoreAdj = &c.OOMScoreAdj
+ }
if c.Restart != "" {
splitRestart := strings.Split(c.Restart, ":")
switch len(splitRestart) {
@@ -685,9 +780,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.RestartPolicy = splitRestart[0]
}
- s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets)
- if err != nil {
- return err
+ if len(s.Secrets) == 0 || len(c.Secrets) != 0 {
+ s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets)
+ if err != nil {
+ return err
+ }
}
if c.Personality != "" {
@@ -695,21 +792,43 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.Personality.Domain = specs.LinuxPersonalityDomain(c.Personality)
}
- s.Remove = c.Rm
- s.StopTimeout = &c.StopTimeout
- s.Timeout = c.Timeout
- s.Timezone = c.Timezone
- s.Umask = c.Umask
- s.PidFile = c.PidFile
- s.Volatile = c.Rm
- s.UnsetEnv = c.UnsetEnv
- s.UnsetEnvAll = c.UnsetEnvAll
+ if !s.Remove {
+ s.Remove = c.Rm
+ }
+ if s.StopTimeout == nil || c.StopTimeout != 0 {
+ s.StopTimeout = &c.StopTimeout
+ }
+ if s.Timeout == 0 || c.Timeout != 0 {
+ s.Timeout = c.Timeout
+ }
+ if len(s.Timezone) == 0 || len(c.Timezone) != 0 {
+ s.Timezone = c.Timezone
+ }
+ if len(s.Umask) == 0 || len(c.Umask) != 0 {
+ s.Umask = c.Umask
+ }
+ if len(s.PidFile) == 0 || len(c.PidFile) != 0 {
+ s.PidFile = c.PidFile
+ }
+ if !s.Volatile {
+ s.Volatile = c.Rm
+ }
+ if len(s.UnsetEnv) == 0 || len(c.UnsetEnv) != 0 {
+ s.UnsetEnv = c.UnsetEnv
+ }
+ if !s.UnsetEnvAll {
+ s.UnsetEnvAll = c.UnsetEnvAll
+ }
// Initcontainers
- s.InitContainerType = c.InitContainerType
+ if len(s.InitContainerType) == 0 || len(c.InitContainerType) != 0 {
+ s.InitContainerType = c.InitContainerType
+ }
t := true
- s.Passwd = &t
+ if s.Passwd == nil {
+ s.Passwd = &t
+ }
return nil
}