github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/container_create.go (about) 1 package generate 2 3 import ( 4 "context" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/containers/common/pkg/config" 10 "github.com/containers/podman/v2/libpod" 11 "github.com/containers/podman/v2/libpod/image" 12 "github.com/containers/podman/v2/pkg/specgen" 13 "github.com/containers/podman/v2/pkg/util" 14 "github.com/containers/storage" 15 "github.com/opencontainers/selinux/go-selinux/label" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 ) 19 20 // MakeContainer creates a container based on the SpecGenerator. 21 // Returns the created, container and any warnings resulting from creating the 22 // container, or an error. 23 func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Container, error) { 24 rtc, err := rt.GetConfig() 25 if err != nil { 26 return nil, err 27 } 28 29 // If joining a pod, retrieve the pod for use. 30 var pod *libpod.Pod 31 if s.Pod != "" { 32 pod, err = rt.LookupPod(s.Pod) 33 if err != nil { 34 return nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) 35 } 36 } 37 38 // Set defaults for unset namespaces 39 if s.PidNS.IsDefault() { 40 defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod) 41 if err != nil { 42 return nil, err 43 } 44 s.PidNS = defaultNS 45 } 46 if s.IpcNS.IsDefault() { 47 defaultNS, err := GetDefaultNamespaceMode("ipc", rtc, pod) 48 if err != nil { 49 return nil, err 50 } 51 s.IpcNS = defaultNS 52 } 53 if s.UtsNS.IsDefault() { 54 defaultNS, err := GetDefaultNamespaceMode("uts", rtc, pod) 55 if err != nil { 56 return nil, err 57 } 58 s.UtsNS = defaultNS 59 } 60 if s.UserNS.IsDefault() { 61 defaultNS, err := GetDefaultNamespaceMode("user", rtc, pod) 62 if err != nil { 63 return nil, err 64 } 65 s.UserNS = defaultNS 66 } 67 if s.NetNS.IsDefault() { 68 defaultNS, err := GetDefaultNamespaceMode("net", rtc, pod) 69 if err != nil { 70 return nil, err 71 } 72 s.NetNS = defaultNS 73 } 74 if s.CgroupNS.IsDefault() { 75 defaultNS, err := GetDefaultNamespaceMode("cgroup", rtc, pod) 76 if err != nil { 77 return nil, err 78 } 79 s.CgroupNS = defaultNS 80 } 81 82 options := []libpod.CtrCreateOption{} 83 if s.ContainerCreateCommand != nil { 84 options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand)) 85 } 86 87 var newImage *image.Image 88 if s.Rootfs != "" { 89 options = append(options, libpod.WithRootFS(s.Rootfs)) 90 } else { 91 newImage, err = rt.ImageRuntime().NewFromLocal(s.Image) 92 if err != nil { 93 return nil, err 94 } 95 // If the input name changed, we could properly resolve the 96 // image. Otherwise, it must have been an ID where we're 97 // defaulting to the first name or an empty one if no names are 98 // present. 99 imgName := newImage.InputName 100 if s.Image == newImage.InputName && strings.HasPrefix(newImage.ID(), s.Image) { 101 names := newImage.Names() 102 if len(names) > 0 { 103 imgName = names[0] 104 } 105 } 106 107 options = append(options, libpod.WithRootFSFromImage(newImage.ID(), imgName, s.RawImageName)) 108 } 109 if err := s.Validate(); err != nil { 110 return nil, errors.Wrap(err, "invalid config provided") 111 } 112 113 finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage) 114 if err != nil { 115 return nil, err 116 } 117 118 command, err := makeCommand(ctx, s, newImage, rtc) 119 if err != nil { 120 return nil, err 121 } 122 123 opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, newImage, command) 124 if err != nil { 125 return nil, err 126 } 127 options = append(options, opts...) 128 129 exitCommandArgs, err := CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false) 130 if err != nil { 131 return nil, err 132 } 133 options = append(options, libpod.WithExitCommand(exitCommandArgs)) 134 135 if len(s.Aliases) > 0 { 136 options = append(options, libpod.WithNetworkAliases(s.Aliases)) 137 } 138 139 runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command) 140 if err != nil { 141 return nil, err 142 } 143 return rt.NewContainer(ctx, runtimeSpec, options...) 144 } 145 146 func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) { 147 var options []libpod.CtrCreateOption 148 var err error 149 150 if s.PreserveFDs > 0 { 151 options = append(options, libpod.WithPreserveFDs(s.PreserveFDs)) 152 } 153 154 if s.Stdin { 155 options = append(options, libpod.WithStdin()) 156 } 157 158 if s.Timezone != "" { 159 options = append(options, libpod.WithTimezone(s.Timezone)) 160 } 161 if s.Umask != "" { 162 options = append(options, libpod.WithUmask(s.Umask)) 163 } 164 165 useSystemd := false 166 switch s.Systemd { 167 case "always": 168 useSystemd = true 169 case "false": 170 break 171 case "", "true": 172 if len(command) == 0 { 173 command, err = img.Cmd(ctx) 174 if err != nil { 175 return nil, err 176 } 177 } 178 179 if len(command) > 0 { 180 useSystemdCommands := map[string]bool{ 181 "/sbin/init": true, 182 "/usr/sbin/init": true, 183 "/usr/local/sbin/init": true, 184 } 185 if useSystemdCommands[command[0]] || (filepath.Base(command[0]) == "systemd") { 186 useSystemd = true 187 } 188 } 189 default: 190 return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd) 191 } 192 logrus.Debugf("using systemd mode: %t", useSystemd) 193 if useSystemd { 194 // is StopSignal was not set by the user then set it to systemd 195 // expected StopSigal 196 if s.StopSignal == nil { 197 stopSignal, err := util.ParseSignal("RTMIN+3") 198 if err != nil { 199 return nil, errors.Wrapf(err, "error parsing systemd signal") 200 } 201 s.StopSignal = &stopSignal 202 } 203 204 options = append(options, libpod.WithSystemd()) 205 } 206 if len(s.SdNotifyMode) > 0 { 207 options = append(options, libpod.WithSdNotifyMode(s.SdNotifyMode)) 208 } 209 210 if len(s.Name) > 0 { 211 logrus.Debugf("setting container name %s", s.Name) 212 options = append(options, libpod.WithName(s.Name)) 213 } 214 if pod != nil { 215 logrus.Debugf("adding container to pod %s", pod.Name()) 216 options = append(options, rt.WithPod(pod)) 217 } 218 destinations := []string{} 219 // Take all mount and named volume destinations. 220 for _, mount := range s.Mounts { 221 destinations = append(destinations, mount.Destination) 222 } 223 for _, volume := range volumes { 224 destinations = append(destinations, volume.Dest) 225 } 226 for _, overlayVolume := range overlays { 227 destinations = append(destinations, overlayVolume.Destination) 228 } 229 for _, imageVolume := range s.ImageVolumes { 230 destinations = append(destinations, imageVolume.Destination) 231 } 232 options = append(options, libpod.WithUserVolumes(destinations)) 233 234 if len(volumes) != 0 { 235 var vols []*libpod.ContainerNamedVolume 236 for _, v := range volumes { 237 vols = append(vols, &libpod.ContainerNamedVolume{ 238 Name: v.Name, 239 Dest: v.Dest, 240 Options: v.Options, 241 }) 242 } 243 options = append(options, libpod.WithNamedVolumes(vols)) 244 } 245 246 if len(overlays) != 0 { 247 var vols []*libpod.ContainerOverlayVolume 248 for _, v := range overlays { 249 vols = append(vols, &libpod.ContainerOverlayVolume{ 250 Dest: v.Destination, 251 Source: v.Source, 252 }) 253 } 254 options = append(options, libpod.WithOverlayVolumes(vols)) 255 } 256 257 if len(s.ImageVolumes) != 0 { 258 var vols []*libpod.ContainerImageVolume 259 for _, v := range s.ImageVolumes { 260 vols = append(vols, &libpod.ContainerImageVolume{ 261 Dest: v.Destination, 262 Source: v.Source, 263 ReadWrite: v.ReadWrite, 264 }) 265 } 266 options = append(options, libpod.WithImageVolumes(vols)) 267 } 268 269 if s.Command != nil { 270 options = append(options, libpod.WithCommand(s.Command)) 271 } 272 if s.Entrypoint != nil { 273 options = append(options, libpod.WithEntrypoint(s.Entrypoint)) 274 } 275 // If the user did not set an workdir but the image did, ensure it is 276 // created. 277 if s.WorkDir == "" && img != nil { 278 options = append(options, libpod.WithCreateWorkingDir()) 279 } 280 if s.StopSignal != nil { 281 options = append(options, libpod.WithStopSignal(*s.StopSignal)) 282 } 283 if s.StopTimeout != nil { 284 options = append(options, libpod.WithStopTimeout(*s.StopTimeout)) 285 } 286 if s.LogConfiguration != nil { 287 if len(s.LogConfiguration.Path) > 0 { 288 options = append(options, libpod.WithLogPath(s.LogConfiguration.Path)) 289 } 290 if s.LogConfiguration.Size > 0 { 291 options = append(options, libpod.WithMaxLogSize(s.LogConfiguration.Size)) 292 } 293 if len(s.LogConfiguration.Options) > 0 && s.LogConfiguration.Options["tag"] != "" { 294 // Note: I'm really guessing here. 295 options = append(options, libpod.WithLogTag(s.LogConfiguration.Options["tag"])) 296 } 297 298 if len(s.LogConfiguration.Driver) > 0 { 299 options = append(options, libpod.WithLogDriver(s.LogConfiguration.Driver)) 300 } 301 } 302 303 // Security options 304 if len(s.SelinuxOpts) > 0 { 305 options = append(options, libpod.WithSecLabels(s.SelinuxOpts)) 306 } else { 307 if pod != nil { 308 // duplicate the security options from the pod 309 processLabel, err := pod.ProcessLabel() 310 if err != nil { 311 return nil, err 312 } 313 if processLabel != "" { 314 selinuxOpts, err := label.DupSecOpt(processLabel) 315 if err != nil { 316 return nil, err 317 } 318 options = append(options, libpod.WithSecLabels(selinuxOpts)) 319 } 320 } 321 } 322 options = append(options, libpod.WithPrivileged(s.Privileged)) 323 324 // Get namespace related options 325 namespaceOptions, err := namespaceOptions(ctx, s, rt, pod, img) 326 if err != nil { 327 return nil, err 328 } 329 options = append(options, namespaceOptions...) 330 331 if len(s.ConmonPidFile) > 0 { 332 options = append(options, libpod.WithConmonPidFile(s.ConmonPidFile)) 333 } 334 options = append(options, libpod.WithLabels(s.Labels)) 335 if s.ShmSize != nil { 336 options = append(options, libpod.WithShmSize(*s.ShmSize)) 337 } 338 if s.Rootfs != "" { 339 options = append(options, libpod.WithRootFS(s.Rootfs)) 340 } 341 // Default used if not overridden on command line 342 343 if s.RestartPolicy != "" { 344 if s.RestartRetries != nil { 345 options = append(options, libpod.WithRestartRetries(*s.RestartRetries)) 346 } 347 options = append(options, libpod.WithRestartPolicy(s.RestartPolicy)) 348 } 349 350 if s.ContainerHealthCheckConfig.HealthConfig != nil { 351 options = append(options, libpod.WithHealthCheck(s.ContainerHealthCheckConfig.HealthConfig)) 352 logrus.Debugf("New container has a health check") 353 } 354 return options, nil 355 } 356 357 func CreateExitCommandArgs(storageConfig storage.StoreOptions, config *config.Config, syslog, rm, exec bool) ([]string, error) { 358 // We need a cleanup process for containers in the current model. 359 // But we can't assume that the caller is Podman - it could be another 360 // user of the API. 361 // As such, provide a way to specify a path to Podman, so we can 362 // still invoke a cleanup process. 363 364 podmanPath, err := os.Executable() 365 if err != nil { 366 return nil, err 367 } 368 369 command := []string{podmanPath, 370 "--root", storageConfig.GraphRoot, 371 "--runroot", storageConfig.RunRoot, 372 "--log-level", logrus.GetLevel().String(), 373 "--cgroup-manager", config.Engine.CgroupManager, 374 "--tmpdir", config.Engine.TmpDir, 375 } 376 if config.Engine.OCIRuntime != "" { 377 command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...) 378 } 379 if storageConfig.GraphDriverName != "" { 380 command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...) 381 } 382 for _, opt := range storageConfig.GraphDriverOptions { 383 command = append(command, []string{"--storage-opt", opt}...) 384 } 385 if config.Engine.EventsLogger != "" { 386 command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...) 387 } 388 389 if syslog { 390 command = append(command, "--syslog") 391 } 392 command = append(command, []string{"container", "cleanup"}...) 393 394 if rm { 395 command = append(command, "--rm") 396 } 397 398 // This has to be absolutely last, to ensure that the exec session ID 399 // will be added after it by Libpod. 400 if exec { 401 command = append(command, "--exec") 402 } 403 404 return command, nil 405 }