mirror of
https://github.com/containers/podman.git
synced 2025-08-06 03:19:52 +08:00

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>
287 lines
8.4 KiB
Go
287 lines
8.4 KiB
Go
package generate
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
|
|
buildahDefine "github.com/containers/buildah/define"
|
|
"github.com/containers/common/pkg/config"
|
|
"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/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func buildPauseImage(rt *libpod.Runtime, rtConfig *config.Config) (string, error) {
|
|
version, err := define.GetVersion()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
imageName := fmt.Sprintf("localhost/podman-pause:%s-%d", version.Version, version.Built)
|
|
|
|
// First check if the image has already been built.
|
|
if _, _, err := rt.LibimageRuntime().LookupImage(imageName, nil); err == nil {
|
|
return imageName, nil
|
|
}
|
|
|
|
// Also look into the path as some distributions install catatonit in
|
|
// /usr/bin.
|
|
catatonitPath, err := rtConfig.FindHelperBinary("catatonit", true)
|
|
if err != nil {
|
|
return "", fmt.Errorf("finding pause binary: %w", err)
|
|
}
|
|
|
|
buildContent := fmt.Sprintf(`FROM scratch
|
|
COPY %s /catatonit
|
|
ENTRYPOINT ["/catatonit", "-P"]`, catatonitPath)
|
|
|
|
tmpF, err := ioutil.TempFile("", "pause.containerfile")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if _, err := tmpF.WriteString(buildContent); err != nil {
|
|
return "", err
|
|
}
|
|
if err := tmpF.Close(); err != nil {
|
|
return "", err
|
|
}
|
|
defer os.Remove(tmpF.Name())
|
|
|
|
buildOptions := buildahDefine.BuildOptions{
|
|
CommonBuildOpts: &buildahDefine.CommonBuildOptions{},
|
|
Output: imageName,
|
|
Quiet: true,
|
|
IIDFile: "/dev/null", // prevents Buildah from writing the ID on stdout
|
|
}
|
|
if _, _, err := rt.Build(context.Background(), buildOptions, tmpF.Name()); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return imageName, nil
|
|
}
|
|
|
|
func pullOrBuildInfraImage(p *entities.PodSpec, rt *libpod.Runtime) error {
|
|
if p.PodSpecGen.NoInfra {
|
|
return nil
|
|
}
|
|
|
|
rtConfig, err := rt.GetConfigNoCopy()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// NOTE: we need pull down the infra image if it was explicitly set by
|
|
// the user (or containers.conf) to the non-default one.
|
|
imageName := p.PodSpecGen.InfraImage
|
|
if imageName == "" {
|
|
imageName = rtConfig.Engine.InfraImage
|
|
}
|
|
|
|
if imageName != "" {
|
|
_, err := rt.LibimageRuntime().Pull(context.Background(), imageName, config.PullPolicyMissing, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
name, err := buildPauseImage(rt, rtConfig)
|
|
if err != nil {
|
|
return fmt.Errorf("building local pause image: %w", err)
|
|
}
|
|
imageName = name
|
|
}
|
|
|
|
p.PodSpecGen.InfraImage = imageName
|
|
p.PodSpecGen.InfraContainerSpec.RawImageName = imageName
|
|
|
|
return nil
|
|
}
|
|
|
|
func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) {
|
|
if err := p.PodSpecGen.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := pullOrBuildInfraImage(p, rt); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil {
|
|
var err error
|
|
p.PodSpecGen.InfraContainerSpec, err = MapSpec(&p.PodSpecGen)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
options, err := createPodOptions(&p.PodSpecGen, rt, p.PodSpecGen.InfraContainerSpec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pod, err := rt.NewPod(context.Background(), p.PodSpecGen, options...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil {
|
|
if p.PodSpecGen.InfraContainerSpec.Name == "" {
|
|
p.PodSpecGen.InfraContainerSpec.Name = pod.ID()[:12] + "-infra"
|
|
}
|
|
_, err = CompleteSpec(context.Background(), rt, p.PodSpecGen.InfraContainerSpec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.PodSpecGen.InfraContainerSpec.User = "" // infraSpec user will get incorrectly assigned via the container creation process, overwrite here
|
|
rtSpec, spec, opts, err := MakeContainer(context.Background(), rt, p.PodSpecGen.InfraContainerSpec, false, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
spec.Pod = pod.ID()
|
|
opts = append(opts, rt.WithPod(pod))
|
|
spec.CgroupParent = pod.CgroupParent()
|
|
infraCtr, err := ExecuteCreate(context.Background(), rt, rtSpec, spec, true, opts...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pod, err = rt.AddInfra(context.Background(), pod, infraCtr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
// SavePod is used to save the pod state and trigger a create event even if infra is not created
|
|
err := rt.SavePod(pod)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return pod, nil
|
|
}
|
|
|
|
func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime, infraSpec *specgen.SpecGenerator) ([]libpod.PodCreateOption, error) {
|
|
var (
|
|
options []libpod.PodCreateOption
|
|
)
|
|
if !p.NoInfra { //&& infraSpec != nil {
|
|
options = append(options, libpod.WithInfraContainer())
|
|
if p.ShareParent == nil || (p.ShareParent != nil && *p.ShareParent) {
|
|
options = append(options, libpod.WithPodParent())
|
|
}
|
|
nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.InfraContainerSpec.NetNS.IsHost())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, nsOptions...)
|
|
// Use pod user and infra userns only when --userns is not set to host
|
|
if !p.InfraContainerSpec.UserNS.IsHost() && !p.InfraContainerSpec.UserNS.IsDefault() {
|
|
options = append(options, libpod.WithPodUser())
|
|
}
|
|
}
|
|
if len(p.CgroupParent) > 0 {
|
|
options = append(options, libpod.WithPodCgroupParent(p.CgroupParent))
|
|
}
|
|
if len(p.Labels) > 0 {
|
|
options = append(options, libpod.WithPodLabels(p.Labels))
|
|
}
|
|
if len(p.Name) > 0 {
|
|
options = append(options, libpod.WithPodName(p.Name))
|
|
}
|
|
if p.PodCreateCommand != nil {
|
|
options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand))
|
|
}
|
|
|
|
if len(p.Hostname) > 0 {
|
|
options = append(options, libpod.WithPodHostname(p.Hostname))
|
|
}
|
|
|
|
return options, nil
|
|
}
|
|
|
|
// MapSpec modifies the already filled Infra specgenerator,
|
|
// replacing necessary values with those specified in pod creation
|
|
func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
|
|
if len(p.PortMappings) > 0 {
|
|
ports, err := ParsePortMapping(p.PortMappings, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.InfraContainerSpec.PortMappings = ports
|
|
}
|
|
switch p.NetNS.NSMode {
|
|
case specgen.Default, "":
|
|
if p.NoInfra {
|
|
logrus.Debugf("No networking because the infra container is missing")
|
|
break
|
|
}
|
|
case specgen.Bridge:
|
|
p.InfraContainerSpec.NetNS.NSMode = specgen.Bridge
|
|
logrus.Debugf("Pod using bridge network mode")
|
|
case specgen.Host:
|
|
logrus.Debugf("Pod will use host networking")
|
|
if len(p.InfraContainerSpec.PortMappings) > 0 ||
|
|
len(p.InfraContainerSpec.Networks) > 0 ||
|
|
p.InfraContainerSpec.NetNS.NSMode == specgen.NoNetwork {
|
|
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified")
|
|
}
|
|
p.InfraContainerSpec.NetNS.NSMode = specgen.Host
|
|
case specgen.Slirp:
|
|
logrus.Debugf("Pod will use slirp4netns")
|
|
if p.InfraContainerSpec.NetNS.NSMode != "host" {
|
|
p.InfraContainerSpec.NetworkOptions = p.NetworkOptions
|
|
p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("slirp4netns")
|
|
}
|
|
case specgen.NoNetwork:
|
|
logrus.Debugf("Pod will not use networking")
|
|
if len(p.InfraContainerSpec.PortMappings) > 0 ||
|
|
len(p.InfraContainerSpec.Networks) > 0 ||
|
|
p.InfraContainerSpec.NetNS.NSMode == "host" {
|
|
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified")
|
|
}
|
|
p.InfraContainerSpec.NetNS.NSMode = specgen.NoNetwork
|
|
default:
|
|
return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode)
|
|
}
|
|
|
|
if len(p.InfraCommand) > 0 {
|
|
p.InfraContainerSpec.Entrypoint = p.InfraCommand
|
|
}
|
|
|
|
if len(p.HostAdd) > 0 {
|
|
p.InfraContainerSpec.HostAdd = p.HostAdd
|
|
}
|
|
if len(p.DNSServer) > 0 {
|
|
var dnsServers []net.IP
|
|
dnsServers = append(dnsServers, p.DNSServer...)
|
|
|
|
p.InfraContainerSpec.DNSServers = dnsServers
|
|
}
|
|
if len(p.DNSOption) > 0 {
|
|
p.InfraContainerSpec.DNSOptions = p.DNSOption
|
|
}
|
|
if len(p.DNSSearch) > 0 {
|
|
p.InfraContainerSpec.DNSSearch = p.DNSSearch
|
|
}
|
|
if p.NoManageResolvConf {
|
|
p.InfraContainerSpec.UseImageResolvConf = true
|
|
}
|
|
if len(p.Networks) > 0 {
|
|
p.InfraContainerSpec.Networks = p.Networks
|
|
}
|
|
// deprecated cni networks for api users
|
|
if len(p.CNINetworks) > 0 {
|
|
p.InfraContainerSpec.CNINetworks = p.CNINetworks
|
|
}
|
|
if p.NoManageHosts {
|
|
p.InfraContainerSpec.UseImageHosts = p.NoManageHosts
|
|
}
|
|
|
|
if len(p.InfraConmonPidFile) > 0 {
|
|
p.InfraContainerSpec.ConmonPidFile = p.InfraConmonPidFile
|
|
}
|
|
|
|
p.InfraContainerSpec.Image = p.InfraImage
|
|
return p.InfraContainerSpec, nil
|
|
}
|