//go:build linux || freebsd package integration import ( . "github.com/containers/podman/v5/test/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman container clone", func() { BeforeEach(func() { SkipIfRemote("podman container clone is not supported in remote") }) It("podman container clone basic test", func() { SkipIfRootlessCgroupsV1("starting a container with the memory limits not supported") create := podmanTest.Podman([]string{"create", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", create.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) clone = podmanTest.Podman([]string{"container", "clone", clone.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) ctrInspect := podmanTest.Podman([]string{"inspect", clone.OutputToString()}) ctrInspect.WaitWithDefaultTimeout() Expect(ctrInspect).To(ExitCleanly()) Expect(ctrInspect.InspectContainerToJSON()[0].Name).To(ContainSubstring("-clone1")) ctrStart := podmanTest.Podman([]string{"container", "start", clone.OutputToString()}) ctrStart.WaitWithDefaultTimeout() Expect(ctrStart).To(ExitCleanly()) }) It("podman container clone image test", func() { create := podmanTest.Podman([]string{"create", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", create.OutputToString(), "new_name", fedoraMinimal}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) ctrInspect := podmanTest.Podman([]string{"inspect", clone.OutputToString()}) ctrInspect.WaitWithDefaultTimeout() Expect(ctrInspect).To(ExitCleanly()) Expect(ctrInspect.InspectContainerToJSON()[0]).To(HaveField("ImageName", fedoraMinimal)) Expect(ctrInspect.InspectContainerToJSON()[0]).To(HaveField("Name", "new_name")) }) It("podman container clone name test", func() { create := podmanTest.Podman([]string{"create", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", "--name", "testing123", create.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) cloneInspect := podmanTest.Podman([]string{"inspect", clone.OutputToString()}) cloneInspect.WaitWithDefaultTimeout() Expect(cloneInspect).To(ExitCleanly()) cloneData := cloneInspect.InspectContainerToJSON() Expect(cloneData[0]).To(HaveField("Name", "testing123")) }) It("podman container clone resource limits override", func() { SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") create := podmanTest.Podman([]string{"create", "--cpus=5", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", create.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) createInspect := podmanTest.Podman([]string{"inspect", create.OutputToString()}) createInspect.WaitWithDefaultTimeout() Expect(createInspect).To(ExitCleanly()) createData := createInspect.InspectContainerToJSON() cloneInspect := podmanTest.Podman([]string{"inspect", clone.OutputToString()}) cloneInspect.WaitWithDefaultTimeout() Expect(cloneInspect).To(ExitCleanly()) cloneData := cloneInspect.InspectContainerToJSON() Expect(createData[0].HostConfig).To(HaveField("NanoCpus", cloneData[0].HostConfig.NanoCpus)) create = podmanTest.Podman([]string{"create", "--memory=5", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone = podmanTest.Podman([]string{"container", "clone", "--cpus=6", create.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) createInspect = podmanTest.Podman([]string{"inspect", create.OutputToString()}) createInspect.WaitWithDefaultTimeout() Expect(createInspect).To(ExitCleanly()) createData = createInspect.InspectContainerToJSON() cloneInspect = podmanTest.Podman([]string{"inspect", clone.OutputToString()}) cloneInspect.WaitWithDefaultTimeout() Expect(cloneInspect).To(ExitCleanly()) cloneData = cloneInspect.InspectContainerToJSON() Expect(createData[0].HostConfig).To(HaveField("MemorySwap", cloneData[0].HostConfig.MemorySwap)) create = podmanTest.Podman([]string{"create", "--cpus=5", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone = podmanTest.Podman([]string{"container", "clone", "--cpus=4", create.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) var nanoCPUs int64 numCpus := 4 nanoCPUs = int64(numCpus * 1000000000) createInspect = podmanTest.Podman([]string{"inspect", create.OutputToString()}) createInspect.WaitWithDefaultTimeout() Expect(createInspect).To(ExitCleanly()) createData = createInspect.InspectContainerToJSON() cloneInspect = podmanTest.Podman([]string{"inspect", clone.OutputToString()}) cloneInspect.WaitWithDefaultTimeout() Expect(cloneInspect).To(ExitCleanly()) cloneData = cloneInspect.InspectContainerToJSON() Expect(createData[0].HostConfig.NanoCpus).ToNot(Equal(cloneData[0].HostConfig.NanoCpus)) Expect(cloneData[0].HostConfig).To(HaveField("NanoCpus", nanoCPUs)) create = podmanTest.Podman([]string{"create", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone = podmanTest.Podman([]string{"container", "clone", "--cpus=4", create.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) cloneInspect = podmanTest.Podman([]string{"inspect", clone.OutputToString()}) cloneInspect.WaitWithDefaultTimeout() Expect(cloneInspect).To(ExitCleanly()) cloneData = cloneInspect.InspectContainerToJSON() Expect(cloneData[0].HostConfig).To(HaveField("MemorySwappiness", int64(0))) }) It("podman container clone in a pod", func() { SkipIfRootlessCgroupsV1("starting a container with the memory limits not supported") run := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:1234", ALPINE, "sleep", "20"}) run.WaitWithDefaultTimeout() Expect(run).To(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", run.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) ctrStart := podmanTest.Podman([]string{"container", "start", clone.OutputToString()}) ctrStart.WaitWithDefaultTimeout() Expect(ctrStart).To(ExitCleanly()) checkClone := podmanTest.Podman([]string{"ps", "-f", "id=" + clone.OutputToString(), "--ns", "--format", "{{.Namespaces.IPC}} {{.Namespaces.UTS}} {{.Namespaces.NET}}"}) checkClone.WaitWithDefaultTimeout() Expect(checkClone).Should(ExitCleanly()) cloneArray := checkClone.OutputToStringArray() checkCreate := podmanTest.Podman([]string{"ps", "-f", "id=" + run.OutputToString(), "--ns", "--format", "{{.Namespaces.IPC}} {{.Namespaces.UTS}} {{.Namespaces.NET}}"}) checkCreate.WaitWithDefaultTimeout() Expect(checkCreate).Should(ExitCleanly()) createArray := checkCreate.OutputToStringArray() Expect(cloneArray).To(ContainElements(createArray)) ctrInspect := podmanTest.Podman([]string{"inspect", clone.OutputToString()}) ctrInspect.WaitWithDefaultTimeout() Expect(ctrInspect).Should(ExitCleanly()) runInspect := podmanTest.Podman([]string{"inspect", run.OutputToString()}) runInspect.WaitWithDefaultTimeout() Expect(runInspect).Should(ExitCleanly()) Expect(ctrInspect.InspectContainerToJSON()[0].Pod).Should(Equal(runInspect.InspectContainerToJSON()[0].Pod)) Expect(ctrInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode).Should(Equal(runInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode)) }) It("podman container clone to a pod", func() { createPod := podmanTest.Podman([]string{"pod", "create", "--share", "uts", "--name", "foo-pod"}) createPod.WaitWithDefaultTimeout() Expect(createPod).To(ExitCleanly()) ctr := podmanTest.RunTopContainer("ctr") ctr.WaitWithDefaultTimeout() Expect(ctr).Should(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", "--name", "cloned", "--pod", "foo-pod", "ctr"}) clone.WaitWithDefaultTimeout() // Cannot use ExitCleanly() because of warning: "At least one namespace was reset to the default configuration" Expect(clone).To(Exit(0)) ctrInspect := podmanTest.Podman([]string{"inspect", "cloned"}) ctrInspect.WaitWithDefaultTimeout() Expect(ctrInspect).Should(ExitCleanly()) Expect(ctrInspect.InspectContainerToJSON()[0].Pod).Should(Equal(createPod.OutputToString())) Expect(ctrInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode).Should(Not(ContainSubstring("container:"))) createPod = podmanTest.Podman([]string{"pod", "create", "--share", "uts,net", "--name", "bar-pod"}) createPod.WaitWithDefaultTimeout() Expect(createPod).To(ExitCleanly()) clone = podmanTest.Podman([]string{"container", "clone", "--name", "cloned2", "--pod", "bar-pod", "ctr"}) clone.WaitWithDefaultTimeout() // Cannot use ExitCleanly() because of warning: "At least one namespace was reset to the default configuration" Expect(clone).To(Exit(0)) ctrInspect = podmanTest.Podman([]string{"inspect", "cloned2"}) ctrInspect.WaitWithDefaultTimeout() Expect(ctrInspect).Should(ExitCleanly()) Expect(ctrInspect.InspectContainerToJSON()[0].Pod).Should(Equal(createPod.OutputToString())) Expect(ctrInspect.InspectContainerToJSON()[0].HostConfig.NetworkMode).Should(ContainSubstring("container:")) }) It("podman container clone --destroy --force test", func() { create := podmanTest.Podman([]string{"create", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", "--destroy", create.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) inspect := podmanTest.Podman([]string{"inspect", create.OutputToString()}) inspect.WaitWithDefaultTimeout() Expect(inspect).ToNot(ExitCleanly()) run := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"}) run.WaitWithDefaultTimeout() Expect(run).To(ExitCleanly()) clone = podmanTest.Podman([]string{"container", "clone", "--destroy", "-f", run.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) inspect = podmanTest.Podman([]string{"inspect", run.OutputToString()}) inspect.WaitWithDefaultTimeout() Expect(inspect).ToNot(ExitCleanly()) run = podmanTest.Podman([]string{"run", "-dt", ALPINE}) run.WaitWithDefaultTimeout() Expect(run).To(ExitCleanly()) clone = podmanTest.Podman([]string{"container", "clone", "-f", run.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).ToNot(ExitCleanly()) }) It("podman container clone network passing", func() { networkCreate := podmanTest.Podman([]string{"network", "create", "testing123"}) networkCreate.WaitWithDefaultTimeout() defer podmanTest.removeNetwork("testing123") Expect(networkCreate).To(ExitCleanly()) run := podmanTest.Podman([]string{"run", "--network", "bridge", "-dt", ALPINE}) run.WaitWithDefaultTimeout() Expect(run).To(ExitCleanly()) connect := podmanTest.Podman([]string{"network", "connect", "testing123", run.OutputToString()}) connect.WaitWithDefaultTimeout() Expect(connect).To(ExitCleanly()) clone := podmanTest.Podman([]string{"container", "clone", run.OutputToString()}) clone.WaitWithDefaultTimeout() Expect(clone).To(ExitCleanly()) inspect := podmanTest.Podman([]string{"inspect", clone.OutputToString()}) inspect.WaitWithDefaultTimeout() Expect(inspect).To(ExitCleanly()) networks := inspect.InspectContainerToJSON()[0].NetworkSettings.Networks Expect(networks).To(HaveLen(2)) Expect(networks).To(HaveKey("testing123")) }) It("podman container clone env test", func() { session := podmanTest.Podman([]string{"run", "--name", "env_ctr", "-e", "ENV_TEST=123", ALPINE, "printenv", "ENV_TEST"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) session = podmanTest.Podman([]string{"container", "clone", "env_ctr"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) session = podmanTest.Podman([]string{"start", "-a", "env_ctr-clone"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) Expect(session.OutputToString()).Should(ContainSubstring("123")) session = podmanTest.Podman([]string{"run", "--name", "env_ctr2", "-e", "ENV_TEST=12=3", ALPINE, "printenv", "ENV_TEST"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) session = podmanTest.Podman([]string{"container", "clone", "env_ctr2"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) session = podmanTest.Podman([]string{"start", "-a", "env_ctr2-clone"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) Expect(session.OutputToString()).Should(ContainSubstring("12=3")) }) It("podman container clone container with healthcheck", func() { podmanTest.PodmanExitCleanly( "run", "-d", "--rm", "--health-cmd", "true", "--health-start-period", "10s", "--health-interval", "10s", "--health-timeout", "10s", "--health-retries", "2", "--health-startup-cmd", "true", "--health-startup-interval", "10s", "--health-startup-timeout", "10s", "--health-startup-retries", "2", "--health-startup-success", "1", "--health-on-failure", "stop", "--health-max-log-count", "1", "--health-max-log-size", "1", "--health-log-destination", podmanTest.TempDir, "--name", "parent", ALPINE, "sleep", "200", ) podmanTest.PodmanExitCleanly("healthcheck", "run", "parent") podmanTest.PodmanExitCleanly("container", "clone", "--run", "parent", "clone", ALPINE) podmanTest.PodmanExitCleanly("healthcheck", "run", "clone") parentInspect := podmanTest.PodmanExitCleanly("inspect", "parent") parentData := parentInspect.InspectContainerToJSON()[0] cloneInspect := podmanTest.PodmanExitCleanly("inspect", "clone") cloneData := cloneInspect.InspectContainerToJSON()[0] Expect(parentData.Config.HealthcheckOnFailureAction).To(Equal(cloneData.Config.HealthcheckOnFailureAction)) Expect(*parentData.Config.Healthcheck).To(Equal(*cloneData.Config.Healthcheck)) Expect(*parentData.Config.StartupHealthCheck).To(Equal(*cloneData.Config.StartupHealthCheck)) Expect(parentData.Config.HealthcheckOnFailureAction).To(Equal(cloneData.Config.HealthcheckOnFailureAction)) Expect(parentData.Config.HealthLogDestination).To(Equal(cloneData.Config.HealthLogDestination)) Expect(parentData.Config.HealthMaxLogCount).To(Equal(cloneData.Config.HealthMaxLogCount)) Expect(parentData.Config.HealthMaxLogSize).To(Equal(cloneData.Config.HealthMaxLogSize)) }) })