summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorcdoern <cbdoer23@g.holycross.edu>2022-05-13 10:52:57 -0400
committercdoern <cdoern@redhat.com>2022-06-10 14:23:19 -0400
commit958759a71955860b01b17bd3bebf38f9dae1018e (patch)
tree34872e81fb126f854c7612ca39f11932ccd3f8bb /pkg
parent6a2c0e96011d36e5b459a010d472facd26c67389 (diff)
downloadpodman-958759a71955860b01b17bd3bebf38f9dae1018e.tar.gz
podman-958759a71955860b01b17bd3bebf38f9dae1018e.tar.bz2
podman-958759a71955860b01b17bd3bebf38f9dae1018e.zip
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 <cdoern@redhat.com>
Diffstat (limited to 'pkg')
-rw-r--r--pkg/domain/entities/engine_container.go1
-rw-r--r--pkg/domain/entities/pods.go14
-rw-r--r--pkg/domain/infra/abi/containers.go9
-rw-r--r--pkg/domain/infra/abi/pods.go85
-rw-r--r--pkg/domain/infra/tunnel/pods.go4
-rw-r--r--pkg/specgen/generate/container.go2
-rw-r--r--pkg/specgen/generate/pod_create.go90
-rw-r--r--pkg/specgenutil/specgen.go2
8 files changed, 203 insertions, 4 deletions
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 6b70a3452..df42876f6 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -71,6 +71,7 @@ type ContainerEngine interface {
PlayKube(ctx context.Context, body io.Reader, opts PlayKubeOptions) (*PlayKubeReport, error)
PlayKubeDown(ctx context.Context, body io.Reader, opts PlayKubeDownOptions) (*PlayKubeReport, error)
PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error)
+ PodClone(ctx context.Context, podClone PodCloneOptions) (*PodCloneReport, error)
PodExists(ctx context.Context, nameOrID string) (*BoolReport, error)
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error)
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 9cbbe2bf1..2b6928d49 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -154,6 +154,16 @@ type PodLogsOptions struct {
Color bool
}
+// PodCloneOptions contains options for cloning an existing pod
+type PodCloneOptions struct {
+ ID string
+ Destroy bool
+ CreateOpts PodCreateOptions
+ InfraOptions ContainerCreateOptions
+ PerContainerOptions ContainerCreateOptions
+ Start bool
+}
+
type ContainerCreateOptions struct {
Annotation []string
Attach []string
@@ -290,6 +300,10 @@ type PodCreateReport struct {
Id string // nolint
}
+type PodCloneReport struct {
+ Id string //nolint
+}
+
func (p *PodCreateOptions) CPULimits() *specs.LinuxCPU {
cpu := &specs.LinuxCPU{}
hasLimits := false
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 8bd84a310..c7cd0cb56 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -1593,6 +1593,11 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti
return nil, err
}
+ conf := c.Config()
+ if conf.Spec != nil && conf.Spec.Process != nil && conf.Spec.Process.Terminal { // if we do not pass term, running ctrs exit
+ spec.Terminal = true
+ }
+
// Print warnings
if len(out) > 0 {
for _, w := range out {
@@ -1612,8 +1617,8 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti
switch {
case strings.Contains(n, "-clone"):
ind := strings.Index(n, "-clone") + 6
- num, _ := strconv.Atoi(n[ind:])
- if num == 0 { // clone1 is hard to get with this logic, just check for it here.
+ num, err := strconv.Atoi(n[ind:])
+ if num == 0 && err != nil { // clone1 is hard to get with this logic, just check for it here.
_, err = ic.Libpod.LookupContainer(n + "1")
if err != nil {
spec.Name = n + "1"
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index 32deb20e0..8638f4783 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -2,12 +2,15 @@ package abi
import (
"context"
+ "strconv"
+ "strings"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
dfilters "github.com/containers/podman/v4/pkg/domain/filters"
"github.com/containers/podman/v4/pkg/signal"
+ "github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/podman/v4/pkg/specgen/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -295,6 +298,88 @@ func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec
return &entities.PodCreateReport{Id: pod.ID()}, nil
}
+func (ic *ContainerEngine) PodClone(ctx context.Context, podClone entities.PodCloneOptions) (*entities.PodCloneReport, error) {
+ spec := specgen.NewPodSpecGenerator()
+ p, err := generate.PodConfigToSpec(ic.Libpod, spec, &podClone.InfraOptions, podClone.ID)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(podClone.CreateOpts.Name) > 0 {
+ spec.Name = podClone.CreateOpts.Name
+ } else {
+ n := p.Name()
+ _, err := ic.Libpod.LookupPod(n + "-clone")
+ if err == nil {
+ n += "-clone"
+ }
+ switch {
+ case strings.Contains(n, "-clone"): // meaning this name is taken!
+ ind := strings.Index(n, "-clone") + 6
+ num, err := strconv.Atoi(n[ind:])
+ if num == 0 && err != nil { // meaning invalid
+ _, err = ic.Libpod.LookupPod(n + "1")
+ if err != nil {
+ spec.Name = n + "1"
+ break
+ }
+ } else { // else we already have a number
+ n = n[0:ind]
+ }
+ err = nil
+ count := num
+ for err == nil { // until we cannot find a pod w/ this name, increment num and try again
+ count++
+ tempN := n + strconv.Itoa(count)
+ _, err = ic.Libpod.LookupPod(tempN)
+ }
+ n += strconv.Itoa(count)
+ spec.Name = n
+ default:
+ spec.Name = p.Name() + "-clone"
+ }
+ }
+
+ podSpec := entities.PodSpec{PodSpecGen: *spec}
+ pod, err := generate.MakePod(&podSpec, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+
+ ctrs, err := p.AllContainers()
+ if err != nil {
+ return nil, err
+ }
+ for _, ctr := range ctrs {
+ if ctr.IsInfra() {
+ continue // already copied infra
+ }
+
+ podClone.PerContainerOptions.Pod = pod.ID()
+ _, err := ic.ContainerClone(ctx, entities.ContainerCloneOptions{ID: ctr.ID(), CreateOpts: podClone.PerContainerOptions})
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if podClone.Destroy {
+ var timeout *uint
+ err = ic.Libpod.RemovePod(ctx, p, true, true, timeout)
+ if err != nil {
+ return &entities.PodCloneReport{Id: pod.ID()}, err
+ }
+ }
+
+ if podClone.Start {
+ _, err := ic.PodStart(ctx, []string{pod.ID()}, entities.PodStartOptions{})
+ if err != nil {
+ return &entities.PodCloneReport{Id: pod.ID()}, err
+ }
+ }
+
+ return &entities.PodCloneReport{Id: pod.ID()}, nil
+}
+
func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOptions) (*entities.StringSliceReport, error) {
var (
pod *libpod.Pod
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
index 2dbdfcf80..7b1fa231f 100644
--- a/pkg/domain/infra/tunnel/pods.go
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -195,6 +195,10 @@ func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec
return pods.CreatePodFromSpec(ic.ClientCtx, &specg)
}
+func (ic *ContainerEngine) PodClone(ctx context.Context, podClone entities.PodCloneOptions) (*entities.PodCloneReport, error) {
+ return nil, nil
+}
+
func (ic *ContainerEngine) PodTop(ctx context.Context, opts entities.PodTopOptions) (*entities.StringSliceReport, error) {
switch {
case opts.Latest:
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index cc376125f..fef62214f 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -450,7 +450,7 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s
specg.IpcNS = specgen.Namespace{NSMode: specgen.Default} // default
}
case "uts":
- specg.UtsNS = specgen.Namespace{NSMode: specgen.Default} // default
+ specg.UtsNS = specgen.Namespace{NSMode: specgen.Private} // default
case "user":
if conf.AddCurrentUserPasswdEntry {
specg.UserNS = specgen.Namespace{NSMode: specgen.KeepID}
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index d4f281a11..4ac8a0aa2 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -2,12 +2,17 @@ package generate
import (
"context"
+ "fmt"
"net"
+ "os"
+ "strconv"
+ "strings"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/specgen"
+ "github.com/containers/podman/v4/pkg/specgenutil"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -210,3 +215,88 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
p.InfraContainerSpec.Image = p.InfraImage
return p.InfraContainerSpec, nil
}
+
+func PodConfigToSpec(rt *libpod.Runtime, spec *specgen.PodSpecGenerator, infraOptions *entities.ContainerCreateOptions, id string) (p *libpod.Pod, err error) {
+ pod, err := rt.LookupPod(id)
+ if err != nil {
+ return nil, err
+ }
+
+ infraSpec := &specgen.SpecGenerator{}
+ if pod.HasInfraContainer() {
+ infraID, err := pod.InfraContainerID()
+ if err != nil {
+ return nil, err
+ }
+ _, _, err = ConfigToSpec(rt, infraSpec, infraID)
+ if err != nil {
+ return nil, err
+ }
+
+ infraSpec.Hostname = ""
+ infraSpec.CgroupParent = ""
+ infraSpec.Pod = "" // remove old pod...
+ infraOptions.IsClone = true
+ infraOptions.IsInfra = true
+
+ n := infraSpec.Name
+ _, err = rt.LookupContainer(n + "-clone")
+ if err == nil { // if we found a ctr with this name, set it so the below switch can tell
+ n += "-clone"
+ }
+
+ switch {
+ case strings.Contains(n, "-clone"):
+ ind := strings.Index(n, "-clone") + 6
+ num, err := strconv.Atoi(n[ind:])
+ if num == 0 && err != nil { // clone1 is hard to get with this logic, just check for it here.
+ _, err = rt.LookupContainer(n + "1")
+ if err != nil {
+ infraSpec.Name = n + "1"
+ break
+ }
+ } else {
+ n = n[0:ind]
+ }
+ err = nil
+ count := num
+ for err == nil {
+ count++
+ tempN := n + strconv.Itoa(count)
+ _, err = rt.LookupContainer(tempN)
+ }
+ n += strconv.Itoa(count)
+ infraSpec.Name = n
+ default:
+ infraSpec.Name = n + "-clone"
+ }
+
+ err = specgenutil.FillOutSpecGen(infraSpec, infraOptions, []string{})
+ if err != nil {
+ return nil, err
+ }
+
+ out, err := CompleteSpec(context.Background(), rt, infraSpec)
+ if err != nil {
+ return nil, err
+ }
+
+ // Print warnings
+ if len(out) > 0 {
+ for _, w := range out {
+ fmt.Println("Could not properly complete the spec as expected:")
+ fmt.Fprintf(os.Stderr, "%s\n", w)
+ }
+ }
+
+ spec.InfraContainerSpec = infraSpec
+ }
+
+ // need to reset hostname, name etc of both pod and infra
+ spec.Hostname = ""
+
+ if len(spec.InfraContainerSpec.Image) > 0 {
+ spec.InfraImage = spec.InfraContainerSpec.Image
+ }
+ return pod, nil
+}
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index 6d70af106..ab45a8d47 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -312,7 +312,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.PublishExposedPorts = c.PublishAll
}
- if len(s.Pod) == 0 {
+ if len(s.Pod) == 0 || len(c.Pod) > 0 {
s.Pod = c.Pod
}