github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/specgen/generate/container_create.go (about) 1 package generate 2 3 import ( 4 "context" 5 "encoding/json" 6 "path/filepath" 7 "strings" 8 9 cdi "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" 10 "github.com/containers/common/libimage" 11 "github.com/hanks177/podman/v4/libpod" 12 "github.com/hanks177/podman/v4/libpod/define" 13 "github.com/hanks177/podman/v4/pkg/namespaces" 14 "github.com/hanks177/podman/v4/pkg/specgen" 15 "github.com/hanks177/podman/v4/pkg/util" 16 spec "github.com/opencontainers/runtime-spec/specs-go" 17 "github.com/opencontainers/selinux/go-selinux/label" 18 "github.com/pkg/errors" 19 "github.com/sirupsen/logrus" 20 ) 21 22 // MakeContainer creates a container based on the SpecGenerator. 23 // Returns the created, container and any warnings resulting from creating the 24 // container, or an error. 25 func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, clone bool, c *libpod.Container) (*spec.Spec, *specgen.SpecGenerator, []libpod.CtrCreateOption, error) { 26 rtc, err := rt.GetConfigNoCopy() 27 if err != nil { 28 return nil, nil, nil, err 29 } 30 31 // If joining a pod, retrieve the pod for use, and its infra container 32 var pod *libpod.Pod 33 var infra *libpod.Container 34 if s.Pod != "" { 35 pod, err = rt.LookupPod(s.Pod) 36 if err != nil { 37 return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) 38 } 39 if pod.HasInfraContainer() { 40 infra, err = pod.InfraContainer() 41 if err != nil { 42 return nil, nil, nil, err 43 } 44 } 45 } 46 47 options := []libpod.CtrCreateOption{} 48 compatibleOptions := &libpod.InfraInherit{} 49 var infraSpec *spec.Spec 50 if infra != nil { 51 options, infraSpec, compatibleOptions, err = Inherit(*infra, s, rt) 52 if err != nil { 53 return nil, nil, nil, err 54 } 55 } 56 57 if err := FinishThrottleDevices(s); err != nil { 58 return nil, nil, nil, err 59 } 60 // Set defaults for unset namespaces 61 if s.PidNS.IsDefault() { 62 defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod) 63 if err != nil { 64 return nil, nil, nil, err 65 } 66 s.PidNS = defaultNS 67 } 68 if s.IpcNS.IsDefault() { 69 defaultNS, err := GetDefaultNamespaceMode("ipc", rtc, pod) 70 if err != nil { 71 return nil, nil, nil, err 72 } 73 s.IpcNS = defaultNS 74 } 75 if s.UtsNS.IsDefault() { 76 defaultNS, err := GetDefaultNamespaceMode("uts", rtc, pod) 77 if err != nil { 78 return nil, nil, nil, err 79 } 80 s.UtsNS = defaultNS 81 } 82 if s.UserNS.IsDefault() { 83 defaultNS, err := GetDefaultNamespaceMode("user", rtc, pod) 84 if err != nil { 85 return nil, nil, nil, err 86 } 87 s.UserNS = defaultNS 88 89 mappings, err := util.ParseIDMapping(namespaces.UsernsMode(s.UserNS.NSMode), nil, nil, "", "") 90 if err != nil { 91 return nil, nil, nil, err 92 } 93 s.IDMappings = mappings 94 } 95 if s.NetNS.IsDefault() { 96 defaultNS, err := GetDefaultNamespaceMode("net", rtc, pod) 97 if err != nil { 98 return nil, nil, nil, err 99 } 100 s.NetNS = defaultNS 101 } 102 if s.CgroupNS.IsDefault() { 103 defaultNS, err := GetDefaultNamespaceMode("cgroup", rtc, pod) 104 if err != nil { 105 return nil, nil, nil, err 106 } 107 s.CgroupNS = defaultNS 108 } 109 110 if s.ContainerCreateCommand != nil { 111 options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand)) 112 } 113 114 if s.Rootfs != "" { 115 options = append(options, libpod.WithRootFS(s.Rootfs, s.RootfsOverlay)) 116 } 117 118 newImage, resolvedImageName, imageData, err := getImageFromSpec(ctx, rt, s) 119 if err != nil { 120 return nil, nil, nil, err 121 } 122 if newImage != nil { 123 // If the input name changed, we could properly resolve the 124 // image. Otherwise, it must have been an ID where we're 125 // defaulting to the first name or an empty one if no names are 126 // present. 127 if strings.HasPrefix(newImage.ID(), resolvedImageName) { 128 names := newImage.Names() 129 if len(names) > 0 { 130 resolvedImageName = names[0] 131 } 132 } 133 134 options = append(options, libpod.WithRootFSFromImage(newImage.ID(), resolvedImageName, s.RawImageName)) 135 } 136 if err := s.Validate(); err != nil { 137 return nil, nil, nil, errors.Wrap(err, "invalid config provided") 138 } 139 140 finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage) 141 if err != nil { 142 return nil, nil, nil, err 143 } 144 145 if len(s.HostUsers) > 0 { 146 options = append(options, libpod.WithHostUsers(s.HostUsers)) 147 } 148 149 command, err := makeCommand(s, imageData, rtc) 150 if err != nil { 151 return nil, nil, nil, err 152 } 153 154 infraVol := (len(compatibleOptions.Mounts) > 0 || len(compatibleOptions.Volumes) > 0 || len(compatibleOptions.ImageVolumes) > 0 || len(compatibleOptions.OverlayVolumes) > 0) 155 opts, err := createContainerOptions(rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVol, *compatibleOptions) 156 if err != nil { 157 return nil, nil, nil, err 158 } 159 options = append(options, opts...) 160 161 if containerType := s.InitContainerType; len(containerType) > 0 { 162 options = append(options, libpod.WithInitCtrType(containerType)) 163 } 164 if len(s.Name) > 0 { 165 logrus.Debugf("setting container name %s", s.Name) 166 options = append(options, libpod.WithName(s.Name)) 167 } 168 if len(s.Devices) > 0 { 169 opts = ExtractCDIDevices(s) 170 options = append(options, opts...) 171 } 172 runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command, compatibleOptions) 173 if clone { // the container fails to start if cloned due to missing Linux spec entries 174 if c == nil { 175 return nil, nil, nil, errors.New("the given container could not be retrieved") 176 } 177 conf := c.Config() 178 if conf != nil && conf.Spec != nil && conf.Spec.Linux != nil { 179 out, err := json.Marshal(conf.Spec.Linux) 180 if err != nil { 181 return nil, nil, nil, err 182 } 183 err = json.Unmarshal(out, runtimeSpec.Linux) 184 if err != nil { 185 return nil, nil, nil, err 186 } 187 } 188 if s.ResourceLimits != nil { 189 switch { 190 case s.ResourceLimits.CPU != nil: 191 runtimeSpec.Linux.Resources.CPU = s.ResourceLimits.CPU 192 case s.ResourceLimits.Memory != nil: 193 runtimeSpec.Linux.Resources.Memory = s.ResourceLimits.Memory 194 case s.ResourceLimits.BlockIO != nil: 195 runtimeSpec.Linux.Resources.BlockIO = s.ResourceLimits.BlockIO 196 case s.ResourceLimits.Devices != nil: 197 runtimeSpec.Linux.Resources.Devices = s.ResourceLimits.Devices 198 } 199 } 200 } 201 if len(s.HostDeviceList) > 0 { 202 options = append(options, libpod.WithHostDevice(s.HostDeviceList)) 203 } 204 if infraSpec != nil && infraSpec.Linux != nil { // if we are inheriting Linux info from a pod... 205 // Pass Security annotations 206 if len(infraSpec.Annotations[define.InspectAnnotationLabel]) > 0 && len(runtimeSpec.Annotations[define.InspectAnnotationLabel]) == 0 { 207 runtimeSpec.Annotations[define.InspectAnnotationLabel] = infraSpec.Annotations[define.InspectAnnotationLabel] 208 } 209 if len(infraSpec.Annotations[define.InspectAnnotationSeccomp]) > 0 && len(runtimeSpec.Annotations[define.InspectAnnotationSeccomp]) == 0 { 210 runtimeSpec.Annotations[define.InspectAnnotationSeccomp] = infraSpec.Annotations[define.InspectAnnotationSeccomp] 211 } 212 if len(infraSpec.Annotations[define.InspectAnnotationApparmor]) > 0 && len(runtimeSpec.Annotations[define.InspectAnnotationApparmor]) == 0 { 213 runtimeSpec.Annotations[define.InspectAnnotationApparmor] = infraSpec.Annotations[define.InspectAnnotationApparmor] 214 } 215 } 216 return runtimeSpec, s, options, err 217 } 218 func ExecuteCreate(ctx context.Context, rt *libpod.Runtime, runtimeSpec *spec.Spec, s *specgen.SpecGenerator, infra bool, options ...libpod.CtrCreateOption) (*libpod.Container, error) { 219 ctr, err := rt.NewContainer(ctx, runtimeSpec, s, infra, options...) 220 if err != nil { 221 return ctr, err 222 } 223 224 return ctr, rt.PrepareVolumeOnCreateContainer(ctx, ctr) 225 } 226 227 // ExtractCDIDevices process the list of Devices in the spec and determines if any of these are CDI devices. 228 // The CDI devices are added to the list of CtrCreateOptions. 229 // Note that this may modify the device list associated with the spec, which should then only contain non-CDI devices. 230 func ExtractCDIDevices(s *specgen.SpecGenerator) []libpod.CtrCreateOption { 231 devs := make([]spec.LinuxDevice, 0, len(s.Devices)) 232 var cdiDevs []string 233 var options []libpod.CtrCreateOption 234 235 for _, device := range s.Devices { 236 if isCDIDevice(device.Path) { 237 logrus.Debugf("Identified CDI device %v", device.Path) 238 cdiDevs = append(cdiDevs, device.Path) 239 continue 240 } 241 logrus.Debugf("Non-CDI device %v; assuming standard device", device.Path) 242 devs = append(devs, device) 243 } 244 s.Devices = devs 245 if len(cdiDevs) > 0 { 246 options = append(options, libpod.WithCDI(cdiDevs)) 247 } 248 return options 249 } 250 251 // isCDIDevice checks whether the specified device is a CDI device. 252 func isCDIDevice(device string) bool { 253 return cdi.IsQualifiedName(device) 254 } 255 256 func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, imageData *libimage.ImageData, command []string, infraVolumes bool, compatibleOptions libpod.InfraInherit) ([]libpod.CtrCreateOption, error) { 257 var options []libpod.CtrCreateOption 258 var err error 259 260 if s.PreserveFDs > 0 { 261 options = append(options, libpod.WithPreserveFDs(s.PreserveFDs)) 262 } 263 264 if s.Stdin { 265 options = append(options, libpod.WithStdin()) 266 } 267 268 if s.Timezone != "" { 269 options = append(options, libpod.WithTimezone(s.Timezone)) 270 } 271 if s.Umask != "" { 272 options = append(options, libpod.WithUmask(s.Umask)) 273 } 274 if s.Volatile { 275 options = append(options, libpod.WithVolatile()) 276 } 277 if s.PasswdEntry != "" { 278 options = append(options, libpod.WithPasswdEntry(s.PasswdEntry)) 279 } 280 281 if s.Privileged { 282 options = append(options, libpod.WithMountAllDevices()) 283 } 284 285 useSystemd := false 286 switch s.Systemd { 287 case "always": 288 useSystemd = true 289 case "false": 290 break 291 case "", "true": 292 if len(command) == 0 && imageData != nil { 293 command = imageData.Config.Cmd 294 } 295 296 if len(command) > 0 { 297 useSystemdCommands := map[string]bool{ 298 "/sbin/init": true, 299 "/usr/sbin/init": true, 300 "/usr/local/sbin/init": true, 301 } 302 // Grab last command in case this is launched from a shell 303 cmd := command 304 if len(command) > 2 { 305 // Podman build will add "/bin/sh" "-c" to 306 // Entrypoint. Remove and search for systemd 307 if command[0] == "/bin/sh" && command[1] == "-c" { 308 cmd = command[2:] 309 } 310 } 311 if useSystemdCommands[cmd[0]] || (filepath.Base(cmd[0]) == "systemd") { 312 useSystemd = true 313 } 314 } 315 default: 316 return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd) 317 } 318 logrus.Debugf("using systemd mode: %t", useSystemd) 319 if useSystemd { 320 // is StopSignal was not set by the user then set it to systemd 321 // expected StopSigal 322 if s.StopSignal == nil { 323 stopSignal, err := util.ParseSignal("RTMIN+3") 324 if err != nil { 325 return nil, errors.Wrapf(err, "error parsing systemd signal") 326 } 327 s.StopSignal = &stopSignal 328 } 329 330 options = append(options, libpod.WithSystemd()) 331 } 332 if len(s.SdNotifyMode) > 0 { 333 options = append(options, libpod.WithSdNotifyMode(s.SdNotifyMode)) 334 } 335 if pod != nil { 336 logrus.Debugf("adding container to pod %s", pod.Name()) 337 options = append(options, rt.WithPod(pod)) 338 } 339 destinations := []string{} 340 // Take all mount and named volume destinations. 341 for _, mount := range s.Mounts { 342 destinations = append(destinations, mount.Destination) 343 } 344 for _, volume := range volumes { 345 destinations = append(destinations, volume.Dest) 346 } 347 for _, overlayVolume := range overlays { 348 destinations = append(destinations, overlayVolume.Destination) 349 } 350 for _, imageVolume := range s.ImageVolumes { 351 destinations = append(destinations, imageVolume.Destination) 352 } 353 354 if len(destinations) > 0 || !infraVolumes { 355 options = append(options, libpod.WithUserVolumes(destinations)) 356 } 357 358 if len(volumes) != 0 { 359 var vols []*libpod.ContainerNamedVolume 360 for _, v := range volumes { 361 vols = append(vols, &libpod.ContainerNamedVolume{ 362 Name: v.Name, 363 Dest: v.Dest, 364 Options: v.Options, 365 }) 366 } 367 options = append(options, libpod.WithNamedVolumes(vols)) 368 } 369 370 if len(overlays) != 0 { 371 var vols []*libpod.ContainerOverlayVolume 372 for _, v := range overlays { 373 vols = append(vols, &libpod.ContainerOverlayVolume{ 374 Dest: v.Destination, 375 Source: v.Source, 376 Options: v.Options, 377 }) 378 } 379 options = append(options, libpod.WithOverlayVolumes(vols)) 380 } 381 382 if len(s.ImageVolumes) != 0 { 383 var vols []*libpod.ContainerImageVolume 384 for _, v := range s.ImageVolumes { 385 vols = append(vols, &libpod.ContainerImageVolume{ 386 Dest: v.Destination, 387 Source: v.Source, 388 ReadWrite: v.ReadWrite, 389 }) 390 } 391 options = append(options, libpod.WithImageVolumes(vols)) 392 } 393 394 if s.Command != nil { 395 options = append(options, libpod.WithCommand(s.Command)) 396 } 397 if s.Entrypoint != nil { 398 options = append(options, libpod.WithEntrypoint(s.Entrypoint)) 399 } 400 if len(s.ContainerStorageConfig.StorageOpts) > 0 { 401 options = append(options, libpod.WithStorageOpts(s.StorageOpts)) 402 } 403 // If the user did not specify a workdir on the CLI, let's extract it 404 // from the image. 405 if s.WorkDir == "" && imageData != nil { 406 options = append(options, libpod.WithCreateWorkingDir()) 407 s.WorkDir = imageData.Config.WorkingDir 408 } 409 if s.WorkDir == "" { 410 s.WorkDir = "/" 411 } 412 if s.CreateWorkingDir { 413 options = append(options, libpod.WithCreateWorkingDir()) 414 } 415 if s.StopSignal != nil { 416 options = append(options, libpod.WithStopSignal(*s.StopSignal)) 417 } 418 if s.StopTimeout != nil { 419 options = append(options, libpod.WithStopTimeout(*s.StopTimeout)) 420 } 421 if s.Timeout != 0 { 422 options = append(options, libpod.WithTimeout(s.Timeout)) 423 } 424 if s.LogConfiguration != nil { 425 if len(s.LogConfiguration.Path) > 0 { 426 options = append(options, libpod.WithLogPath(s.LogConfiguration.Path)) 427 } 428 if s.LogConfiguration.Size > 0 { 429 options = append(options, libpod.WithMaxLogSize(s.LogConfiguration.Size)) 430 } 431 if len(s.LogConfiguration.Options) > 0 && s.LogConfiguration.Options["tag"] != "" { 432 options = append(options, libpod.WithLogTag(s.LogConfiguration.Options["tag"])) 433 } 434 435 if len(s.LogConfiguration.Driver) > 0 { 436 options = append(options, libpod.WithLogDriver(s.LogConfiguration.Driver)) 437 } 438 } 439 // Security options 440 if len(s.SelinuxOpts) > 0 { 441 options = append(options, libpod.WithSecLabels(s.SelinuxOpts)) 442 } else if pod != nil && len(compatibleOptions.SelinuxOpts) == 0 { 443 // duplicate the security options from the pod 444 processLabel, err := pod.ProcessLabel() 445 if err != nil { 446 return nil, err 447 } 448 if processLabel != "" { 449 selinuxOpts, err := label.DupSecOpt(processLabel) 450 if err != nil { 451 return nil, err 452 } 453 options = append(options, libpod.WithSecLabels(selinuxOpts)) 454 } 455 } 456 options = append(options, libpod.WithPrivileged(s.Privileged)) 457 458 // Get namespace related options 459 namespaceOpts, err := namespaceOptions(s, rt, pod, imageData) 460 if err != nil { 461 return nil, err 462 } 463 options = append(options, namespaceOpts...) 464 465 if len(s.ConmonPidFile) > 0 { 466 options = append(options, libpod.WithConmonPidFile(s.ConmonPidFile)) 467 } 468 options = append(options, libpod.WithLabels(s.Labels)) 469 if s.ShmSize != nil { 470 options = append(options, libpod.WithShmSize(*s.ShmSize)) 471 } 472 if s.Rootfs != "" { 473 options = append(options, libpod.WithRootFS(s.Rootfs, s.RootfsOverlay)) 474 } 475 // Default used if not overridden on command line 476 477 if s.RestartPolicy != "" { 478 if s.RestartRetries != nil { 479 options = append(options, libpod.WithRestartRetries(*s.RestartRetries)) 480 } 481 options = append(options, libpod.WithRestartPolicy(s.RestartPolicy)) 482 } 483 484 if s.ContainerHealthCheckConfig.HealthConfig != nil { 485 options = append(options, libpod.WithHealthCheck(s.ContainerHealthCheckConfig.HealthConfig)) 486 logrus.Debugf("New container has a health check") 487 } 488 489 if len(s.Secrets) != 0 { 490 manager, err := rt.SecretsManager() 491 if err != nil { 492 return nil, err 493 } 494 var secrs []*libpod.ContainerSecret 495 for _, s := range s.Secrets { 496 secr, err := manager.Lookup(s.Source) 497 if err != nil { 498 return nil, err 499 } 500 secrs = append(secrs, &libpod.ContainerSecret{ 501 Secret: secr, 502 UID: s.UID, 503 GID: s.GID, 504 Mode: s.Mode, 505 Target: s.Target, 506 }) 507 } 508 options = append(options, libpod.WithSecrets(secrs)) 509 } 510 511 if len(s.EnvSecrets) != 0 { 512 options = append(options, libpod.WithEnvSecrets(s.EnvSecrets)) 513 } 514 515 if len(s.DependencyContainers) > 0 { 516 deps := make([]*libpod.Container, 0, len(s.DependencyContainers)) 517 for _, ctr := range s.DependencyContainers { 518 depCtr, err := rt.LookupContainer(ctr) 519 if err != nil { 520 return nil, errors.Wrapf(err, "%q is not a valid container, cannot be used as a dependency", ctr) 521 } 522 deps = append(deps, depCtr) 523 } 524 options = append(options, libpod.WithDependencyCtrs(deps)) 525 } 526 if s.PidFile != "" { 527 options = append(options, libpod.WithPidFile(s.PidFile)) 528 } 529 530 if len(s.ChrootDirs) != 0 { 531 options = append(options, libpod.WithChrootDirs(s.ChrootDirs)) 532 } 533 534 options = append(options, libpod.WithSelectedPasswordManagement(s.Passwd)) 535 536 return options, nil 537 } 538 539 func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtime) (opts []libpod.CtrCreateOption, infraS *spec.Spec, compat *libpod.InfraInherit, err error) { 540 inheritSpec := &specgen.SpecGenerator{} 541 _, compatibleOptions, err := ConfigToSpec(rt, inheritSpec, infra.ID()) 542 if err != nil { 543 return nil, nil, nil, err 544 } 545 options := []libpod.CtrCreateOption{} 546 infraConf := infra.Config() 547 infraSpec := infraConf.Spec 548 549 // need to set compatOptions to the currently filled specgenOptions so we do not overwrite 550 compatibleOptions.CapAdd = append(compatibleOptions.CapAdd, s.CapAdd...) 551 compatibleOptions.CapDrop = append(compatibleOptions.CapDrop, s.CapDrop...) 552 compatibleOptions.HostDeviceList = append(compatibleOptions.HostDeviceList, s.HostDeviceList...) 553 compatibleOptions.ImageVolumes = append(compatibleOptions.ImageVolumes, s.ImageVolumes...) 554 compatibleOptions.Mounts = append(compatibleOptions.Mounts, s.Mounts...) 555 compatibleOptions.OverlayVolumes = append(compatibleOptions.OverlayVolumes, s.OverlayVolumes...) 556 compatibleOptions.SelinuxOpts = append(compatibleOptions.SelinuxOpts, s.SelinuxOpts...) 557 compatibleOptions.Volumes = append(compatibleOptions.Volumes, s.Volumes...) 558 559 compatByte, err := json.Marshal(compatibleOptions) 560 if err != nil { 561 return nil, nil, nil, err 562 } 563 err = json.Unmarshal(compatByte, s) 564 if err != nil { 565 return nil, nil, nil, err 566 } 567 return options, infraSpec, compatibleOptions, nil 568 }