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 }