github.com/containers/podman/v4@v4.9.4/pkg/specgen/generate/pod_create.go (about)

     1  //go:build !remote
     2  // +build !remote
     3  
     4  package generate
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"net"
    11  	"os"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/containers/podman/v4/libpod"
    16  	"github.com/containers/podman/v4/libpod/define"
    17  	"github.com/containers/podman/v4/pkg/domain/entities"
    18  	"github.com/containers/podman/v4/pkg/specgen"
    19  	"github.com/containers/podman/v4/pkg/specgenutil"
    20  	"github.com/opencontainers/runtime-spec/specs-go"
    21  	"github.com/sirupsen/logrus"
    22  )
    23  
    24  func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (_ *libpod.Pod, finalErr error) {
    25  	var createdPod *libpod.Pod
    26  	defer func() {
    27  		if finalErr != nil && createdPod != nil {
    28  			if _, err := rt.RemovePod(context.Background(), createdPod, true, true, nil); err != nil {
    29  				logrus.Errorf("Removing pod: %v", err)
    30  			}
    31  		}
    32  	}()
    33  	if err := p.PodSpecGen.Validate(); err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	if p.PodSpecGen.ResourceLimits == nil {
    38  		p.PodSpecGen.ResourceLimits = &specs.LinuxResources{}
    39  	}
    40  
    41  	if !p.PodSpecGen.NoInfra {
    42  		imageName, err := PullOrBuildInfraImage(rt, p.PodSpecGen.InfraImage)
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  		p.PodSpecGen.InfraImage = imageName
    47  		p.PodSpecGen.InfraContainerSpec.RawImageName = imageName
    48  	}
    49  
    50  	spec, err := MapSpec(&p.PodSpecGen)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	if err := specgen.FinishThrottleDevices(spec); err != nil {
    55  		return nil, err
    56  	}
    57  	if err := specgen.WeightDevices(spec); err != nil {
    58  		return nil, err
    59  	}
    60  	if spec.ResourceLimits != nil && spec.ResourceLimits.BlockIO != nil {
    61  		p.PodSpecGen.ResourceLimits.BlockIO = spec.ResourceLimits.BlockIO
    62  	}
    63  
    64  	options, err := createPodOptions(&p.PodSpecGen)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	pod, err := rt.NewPod(context.Background(), p.PodSpecGen, options...)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	createdPod = pod
    74  
    75  	if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil {
    76  		if p.PodSpecGen.InfraContainerSpec.Name == "" {
    77  			p.PodSpecGen.InfraContainerSpec.Name = pod.ID()[:12] + "-infra"
    78  		}
    79  		_, err = CompleteSpec(context.Background(), rt, p.PodSpecGen.InfraContainerSpec)
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  		p.PodSpecGen.InfraContainerSpec.User = "" // infraSpec user will get incorrectly assigned via the container creation process, overwrite here
    84  		// infra's resource limits are used as a parsing tool,
    85  		// we do not want infra to get these resources in its cgroup
    86  		// make sure of that here.
    87  		p.PodSpecGen.InfraContainerSpec.ResourceLimits = nil
    88  		p.PodSpecGen.InfraContainerSpec.WeightDevice = nil
    89  		rtSpec, spec, opts, err := MakeContainer(context.Background(), rt, p.PodSpecGen.InfraContainerSpec, false, nil)
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  
    94  		spec.Pod = pod.ID()
    95  		opts = append(opts, rt.WithPod(pod))
    96  		spec.CgroupParent = pod.CgroupParent()
    97  		infraCtr, err := ExecuteCreate(context.Background(), rt, rtSpec, spec, true, opts...)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  		pod, err = rt.AddInfra(context.Background(), pod, infraCtr)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	} else {
   106  		// SavePod is used to save the pod state and trigger a create event even if infra is not created
   107  		err := rt.SavePod(pod)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  	}
   112  	return pod, nil
   113  }
   114  
   115  func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, error) {
   116  	var (
   117  		options []libpod.PodCreateOption
   118  	)
   119  
   120  	if p.ShareParent == nil || (p.ShareParent != nil && *p.ShareParent) {
   121  		options = append(options, libpod.WithPodParent())
   122  	}
   123  	if !p.NoInfra {
   124  		options = append(options, libpod.WithInfraContainer())
   125  		nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.InfraContainerSpec.NetNS.IsHost())
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  		options = append(options, nsOptions...)
   130  		// Use pod user and infra userns only when --userns is not set to host
   131  		if !p.InfraContainerSpec.UserNS.IsHost() && !p.InfraContainerSpec.UserNS.IsDefault() {
   132  			options = append(options, libpod.WithPodUser())
   133  		}
   134  	}
   135  
   136  	if len(p.ServiceContainerID) > 0 {
   137  		options = append(options, libpod.WithServiceContainer(p.ServiceContainerID))
   138  	}
   139  
   140  	if len(p.CgroupParent) > 0 {
   141  		options = append(options, libpod.WithPodCgroupParent(p.CgroupParent))
   142  	}
   143  	if len(p.Labels) > 0 {
   144  		options = append(options, libpod.WithPodLabels(p.Labels))
   145  	}
   146  	if len(p.Name) > 0 {
   147  		options = append(options, libpod.WithPodName(p.Name))
   148  	}
   149  	if p.PodCreateCommand != nil {
   150  		options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand))
   151  	}
   152  
   153  	if len(p.Hostname) > 0 {
   154  		options = append(options, libpod.WithPodHostname(p.Hostname))
   155  	}
   156  
   157  	if p.ResourceLimits != nil {
   158  		options = append(options, libpod.WithPodResources(*p.ResourceLimits))
   159  	}
   160  
   161  	options = append(options, libpod.WithPodExitPolicy(p.ExitPolicy))
   162  	options = append(options, libpod.WithPodRestartPolicy(p.RestartPolicy))
   163  	if p.RestartRetries != nil {
   164  		options = append(options, libpod.WithPodRestartRetries(*p.RestartRetries))
   165  	}
   166  
   167  	return options, nil
   168  }
   169  
   170  // MapSpec modifies the already filled Infra specgenerator,
   171  // replacing necessary values with those specified in pod creation
   172  func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
   173  	var spec *specgen.SpecGenerator
   174  	if p.InfraContainerSpec != nil {
   175  		spec = p.InfraContainerSpec
   176  	} else {
   177  		spec = &specgen.SpecGenerator{}
   178  	}
   179  	if len(p.PortMappings) > 0 {
   180  		ports, err := ParsePortMapping(p.PortMappings, nil)
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  		spec.PortMappings = ports
   185  	}
   186  	switch p.NetNS.NSMode {
   187  	case specgen.Default, "":
   188  		if p.NoInfra {
   189  			logrus.Debugf("No networking because the infra container is missing")
   190  			break
   191  		}
   192  	case specgen.Bridge:
   193  		spec.NetNS.NSMode = specgen.Bridge
   194  		logrus.Debugf("Pod using bridge network mode")
   195  	case specgen.Private:
   196  		spec.NetNS.NSMode = specgen.Private
   197  		logrus.Debugf("Pod will use default network mode")
   198  	case specgen.Host:
   199  		logrus.Debugf("Pod will use host networking")
   200  		if len(spec.PortMappings) > 0 ||
   201  			len(spec.Networks) > 0 ||
   202  			spec.NetNS.NSMode == specgen.NoNetwork {
   203  			return nil, fmt.Errorf("cannot set host network if network-related configuration is specified: %w", define.ErrInvalidArg)
   204  		}
   205  		spec.NetNS.NSMode = specgen.Host
   206  	case specgen.Slirp:
   207  		logrus.Debugf("Pod will use slirp4netns")
   208  		if spec.NetNS.NSMode != specgen.Host {
   209  			spec.NetworkOptions = p.NetworkOptions
   210  			spec.NetNS.NSMode = specgen.Slirp
   211  		}
   212  	case specgen.Pasta:
   213  		logrus.Debugf("Pod will use pasta")
   214  		if spec.NetNS.NSMode != specgen.Host {
   215  			spec.NetworkOptions = p.NetworkOptions
   216  			spec.NetNS.NSMode = specgen.Pasta
   217  		}
   218  	case specgen.Path:
   219  		logrus.Debugf("Pod will use namespace path networking")
   220  		spec.NetNS.NSMode = specgen.Path
   221  		spec.NetNS.Value = p.PodNetworkConfig.NetNS.Value
   222  	case specgen.NoNetwork:
   223  		logrus.Debugf("Pod will not use networking")
   224  		if len(spec.PortMappings) > 0 ||
   225  			len(spec.Networks) > 0 ||
   226  			spec.NetNS.NSMode == specgen.Host {
   227  			return nil, fmt.Errorf("cannot disable pod network if network-related configuration is specified: %w", define.ErrInvalidArg)
   228  		}
   229  		spec.NetNS.NSMode = specgen.NoNetwork
   230  	default:
   231  		return nil, fmt.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode)
   232  	}
   233  
   234  	if len(p.InfraCommand) > 0 {
   235  		spec.Entrypoint = p.InfraCommand
   236  	}
   237  
   238  	if len(p.HostAdd) > 0 {
   239  		spec.HostAdd = p.HostAdd
   240  	}
   241  	if len(p.DNSServer) > 0 {
   242  		var dnsServers []net.IP
   243  		dnsServers = append(dnsServers, p.DNSServer...)
   244  
   245  		spec.DNSServers = dnsServers
   246  	}
   247  	if len(p.DNSOption) > 0 {
   248  		spec.DNSOptions = p.DNSOption
   249  	}
   250  	if len(p.DNSSearch) > 0 {
   251  		spec.DNSSearch = p.DNSSearch
   252  	}
   253  	if p.NoManageResolvConf {
   254  		spec.UseImageResolvConf = true
   255  	}
   256  	if len(p.Networks) > 0 {
   257  		spec.Networks = p.Networks
   258  	}
   259  	// deprecated cni networks for api users
   260  	if len(p.CNINetworks) > 0 {
   261  		spec.CNINetworks = p.CNINetworks
   262  	}
   263  	if p.NoManageHosts {
   264  		spec.UseImageHosts = p.NoManageHosts
   265  	}
   266  
   267  	if len(p.InfraConmonPidFile) > 0 {
   268  		spec.ConmonPidFile = p.InfraConmonPidFile
   269  	}
   270  
   271  	if p.Sysctl != nil && len(p.Sysctl) > 0 {
   272  		spec.Sysctl = p.Sysctl
   273  	}
   274  
   275  	spec.Image = p.InfraImage
   276  	return spec, nil
   277  }
   278  
   279  func PodConfigToSpec(rt *libpod.Runtime, spec *specgen.PodSpecGenerator, infraOptions *entities.ContainerCreateOptions, id string) (p *libpod.Pod, err error) {
   280  	pod, err := rt.LookupPod(id)
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	infraSpec := &specgen.SpecGenerator{}
   286  	if pod.HasInfraContainer() {
   287  		infraID, err := pod.InfraContainerID()
   288  		if err != nil {
   289  			return nil, err
   290  		}
   291  		_, _, err = ConfigToSpec(rt, infraSpec, infraID)
   292  		if err != nil {
   293  			return nil, err
   294  		}
   295  
   296  		infraSpec.Hostname = ""
   297  		infraSpec.CgroupParent = ""
   298  		infraSpec.Pod = "" // remove old pod...
   299  		infraOptions.IsClone = true
   300  		infraOptions.IsInfra = true
   301  
   302  		n := infraSpec.Name
   303  		_, err = rt.LookupContainer(n + "-clone")
   304  		if err == nil { // if we found a ctr with this name, set it so the below switch can tell
   305  			n += "-clone"
   306  		}
   307  
   308  		switch {
   309  		case strings.Contains(n, "-clone"):
   310  			ind := strings.Index(n, "-clone") + 6
   311  			num, err := strconv.Atoi(n[ind:])
   312  			if num == 0 && err != nil { // clone1 is hard to get with this logic, just check for it here.
   313  				_, err = rt.LookupContainer(n + "1")
   314  				if err != nil {
   315  					infraSpec.Name = n + "1"
   316  					break
   317  				}
   318  			} else {
   319  				n = n[0:ind]
   320  			}
   321  			err = nil
   322  			count := num
   323  			for err == nil {
   324  				count++
   325  				tempN := n + strconv.Itoa(count)
   326  				_, err = rt.LookupContainer(tempN)
   327  			}
   328  			n += strconv.Itoa(count)
   329  			infraSpec.Name = n
   330  		default:
   331  			infraSpec.Name = n + "-clone"
   332  		}
   333  
   334  		err = specgenutil.FillOutSpecGen(infraSpec, infraOptions, []string{})
   335  		if err != nil {
   336  			return nil, err
   337  		}
   338  
   339  		out, err := CompleteSpec(context.Background(), rt, infraSpec)
   340  		if err != nil {
   341  			return nil, err
   342  		}
   343  
   344  		// Print warnings
   345  		if len(out) > 0 {
   346  			for _, w := range out {
   347  				fmt.Println("Could not properly complete the spec as expected:")
   348  				fmt.Fprintf(os.Stderr, "%s\n", w)
   349  			}
   350  		}
   351  
   352  		spec.InfraContainerSpec = infraSpec
   353  		matching, err := json.Marshal(infraSpec)
   354  		if err != nil {
   355  			return nil, err
   356  		}
   357  
   358  		// track name before unmarshal so we do not overwrite w/ infra
   359  		name := spec.Name
   360  		err = json.Unmarshal(matching, spec)
   361  		if err != nil {
   362  			return nil, err
   363  		}
   364  
   365  		spec.Name = name
   366  	}
   367  
   368  	// need to reset hostname, name etc of both pod and infra
   369  	spec.Hostname = ""
   370  
   371  	if len(spec.InfraContainerSpec.Image) > 0 {
   372  		spec.InfraImage = spec.InfraContainerSpec.Image
   373  	}
   374  	return pod, nil
   375  }