diff options
Diffstat (limited to 'cmd/podman/common')
-rw-r--r-- | cmd/podman/common/create_opts.go | 130 | ||||
-rw-r--r-- | cmd/podman/common/specgen.go | 8 | ||||
-rw-r--r-- | cmd/podman/common/volumes.go | 139 |
3 files changed, 180 insertions, 97 deletions
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 5dea49c8b..05bb9de13 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -293,66 +293,61 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig) (*Cont // like with start. We believe this is just a difference in podman/compat cliOpts := ContainerCLIOpts{ //Attach: nil, // dont need? - Authfile: "", - BlkIOWeight: strconv.Itoa(int(cc.HostConfig.BlkioWeight)), - BlkIOWeightDevice: nil, // TODO - CapAdd: append(capAdd, cc.HostConfig.CapAdd...), - CapDrop: append(cappDrop, cc.HostConfig.CapDrop...), - CGroupParent: cc.HostConfig.CgroupParent, - CIDFile: cc.HostConfig.ContainerIDFile, - CPUPeriod: uint64(cc.HostConfig.CPUPeriod), - CPUQuota: cc.HostConfig.CPUQuota, - CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod), - CPURTRuntime: cc.HostConfig.CPURealtimeRuntime, - CPUShares: uint64(cc.HostConfig.CPUShares), + Authfile: "", + CapAdd: append(capAdd, cc.HostConfig.CapAdd...), + CapDrop: append(cappDrop, cc.HostConfig.CapDrop...), + CGroupParent: cc.HostConfig.CgroupParent, + CIDFile: cc.HostConfig.ContainerIDFile, + CPUPeriod: uint64(cc.HostConfig.CPUPeriod), + CPUQuota: cc.HostConfig.CPUQuota, + CPURTPeriod: uint64(cc.HostConfig.CPURealtimePeriod), + CPURTRuntime: cc.HostConfig.CPURealtimeRuntime, + CPUShares: uint64(cc.HostConfig.CPUShares), //CPUS: 0, // dont need? CPUSetCPUs: cc.HostConfig.CpusetCpus, CPUSetMems: cc.HostConfig.CpusetMems, //Detach: false, // dont need //DetachKeys: "", // dont need - Devices: devices, - DeviceCGroupRule: nil, - DeviceReadBPs: readBps, - DeviceReadIOPs: readIops, - DeviceWriteBPs: writeBps, - DeviceWriteIOPs: writeIops, - Entrypoint: &entrypoint, - Env: cc.Config.Env, - Expose: expose, - GroupAdd: cc.HostConfig.GroupAdd, - Hostname: cc.Config.Hostname, - ImageVolume: "bind", - Init: init, - Interactive: cc.Config.OpenStdin, - IPC: string(cc.HostConfig.IpcMode), - Label: stringMaptoArray(cc.Config.Labels), - LogDriver: cc.HostConfig.LogConfig.Type, - LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), - Memory: strconv.Itoa(int(cc.HostConfig.Memory)), - MemoryReservation: strconv.Itoa(int(cc.HostConfig.MemoryReservation)), - MemorySwap: strconv.Itoa(int(cc.HostConfig.MemorySwap)), - Name: cc.Name, - OOMScoreAdj: cc.HostConfig.OomScoreAdj, - OverrideArch: "", - OverrideOS: "", - OverrideVariant: "", - PID: string(cc.HostConfig.PidMode), - PIDsLimit: cc.HostConfig.PidsLimit, - Privileged: cc.HostConfig.Privileged, - PublishAll: cc.HostConfig.PublishAllPorts, - Quiet: false, - ReadOnly: cc.HostConfig.ReadonlyRootfs, - ReadOnlyTmpFS: true, // podman default - Rm: cc.HostConfig.AutoRemove, - SecurityOpt: cc.HostConfig.SecurityOpt, - ShmSize: strconv.Itoa(int(cc.HostConfig.ShmSize)), - StopSignal: cc.Config.StopSignal, - StoreageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), - Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), - Systemd: "true", // podman default - TmpFS: stringMaptoArray(cc.HostConfig.Tmpfs), - TTY: cc.Config.Tty, + Devices: devices, + DeviceCGroupRule: nil, + DeviceReadBPs: readBps, + DeviceReadIOPs: readIops, + DeviceWriteBPs: writeBps, + DeviceWriteIOPs: writeIops, + Entrypoint: &entrypoint, + Env: cc.Config.Env, + Expose: expose, + GroupAdd: cc.HostConfig.GroupAdd, + Hostname: cc.Config.Hostname, + ImageVolume: "bind", + Init: init, + Interactive: cc.Config.OpenStdin, + IPC: string(cc.HostConfig.IpcMode), + Label: stringMaptoArray(cc.Config.Labels), + LogDriver: cc.HostConfig.LogConfig.Type, + LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), + Name: cc.Name, + OOMScoreAdj: cc.HostConfig.OomScoreAdj, + OverrideArch: "", + OverrideOS: "", + OverrideVariant: "", + PID: string(cc.HostConfig.PidMode), + PIDsLimit: cc.HostConfig.PidsLimit, + Privileged: cc.HostConfig.Privileged, + PublishAll: cc.HostConfig.PublishAllPorts, + Quiet: false, + ReadOnly: cc.HostConfig.ReadonlyRootfs, + ReadOnlyTmpFS: true, // podman default + Rm: cc.HostConfig.AutoRemove, + SecurityOpt: cc.HostConfig.SecurityOpt, + StopSignal: cc.Config.StopSignal, + StoreageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), + Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), + Systemd: "true", // podman default + TmpFS: stringMaptoArray(cc.HostConfig.Tmpfs), + TTY: cc.Config.Tty, //Ulimit: cc.HostConfig.Ulimits, // ask dan, no documented format + Ulimit: []string{"nproc=4194304:4194304"}, User: cc.Config.User, UserNS: string(cc.HostConfig.UsernsMode), UTS: string(cc.HostConfig.UTSMode), @@ -363,10 +358,37 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig) (*Cont Net: &netInfo, } + if len(cc.HostConfig.BlkioWeightDevice) > 0 { + devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice)) + for _, d := range cc.HostConfig.BlkioWeightDevice { + devices = append(devices, d.String()) + } + cliOpts.BlkIOWeightDevice = devices + } + if cc.HostConfig.BlkioWeight > 0 { + cliOpts.BlkIOWeight = strconv.Itoa(int(cc.HostConfig.BlkioWeight)) + } + + if cc.HostConfig.Memory > 0 { + cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory)) + } + + if cc.HostConfig.MemoryReservation > 0 { + cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation)) + } + + if cc.HostConfig.MemorySwap > 0 { + cliOpts.MemorySwap = strconv.Itoa(int(cc.HostConfig.MemorySwap)) + } + if cc.Config.StopTimeout != nil { cliOpts.StopTimeout = uint(*cc.Config.StopTimeout) } + if cc.HostConfig.ShmSize > 0 { + cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize)) + } + if cc.HostConfig.KernelMemory > 0 { cliOpts.KernelMemory = strconv.Itoa(int(cc.HostConfig.KernelMemory)) } diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index f427830c6..ca1e25be1 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -25,11 +25,8 @@ func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { cpu := &specs.LinuxCPU{} hasLimits := false - const cpuPeriod = 100000 - if c.CPUS > 0 { - quota := int64(c.CPUS * cpuPeriod) - period := uint64(cpuPeriod) + period, quota := util.CoresToPeriodAndQuota(c.CPUS) cpu.Period = &period cpu.Quota = "a @@ -533,13 +530,14 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string // Only add read-only tmpfs mounts in case that we are read-only and the // read-only tmpfs flag has been set. - mounts, volumes, overlayVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) + mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) if err != nil { return err } s.Mounts = mounts s.Volumes = volumes s.OverlayVolumes = overlayVolumes + s.ImageVolumes = imageVolumes for _, dev := range c.Devices { s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev}) diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index 71f897264..b3c160ddf 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -37,43 +37,43 @@ var ( // Does not handle image volumes, init, and --volumes-from flags. // Can also add tmpfs mounts from read-only tmpfs. // TODO: handle options parsing/processing via containers/storage/pkg/mount -func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) { +func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { // Get mounts from the --mounts flag. - unifiedMounts, unifiedVolumes, err := getMounts(mountFlag) + unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := getMounts(mountFlag) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } // Next --volumes flag. volumeMounts, volumeVolumes, overlayVolumes, err := getVolumeMounts(volumeFlag) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } // Next --tmpfs flag. tmpfsMounts, err := getTmpfsMounts(tmpfsFlag) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } // Unify mounts from --mount, --volume, --tmpfs. // Start with --volume. for dest, mount := range volumeMounts { if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) } unifiedMounts[dest] = mount } for dest, volume := range volumeVolumes { if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) } unifiedVolumes[dest] = volume } // Now --tmpfs for dest, tmpfs := range tmpfsMounts { if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) } unifiedMounts[dest] = tmpfs } @@ -98,29 +98,34 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo } } - // Check for conflicts between named volumes, overlay volumes, and mounts - for dest := range unifiedMounts { - if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + // Check for conflicts between named volumes, overlay & image volumes, + // and mounts + allMounts := make(map[string]bool) + testAndSet := func(dest string) error { + if _, ok := allMounts[dest]; ok { + return errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) } - if _, ok := overlayVolumes[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + allMounts[dest] = true + return nil + } + for dest := range unifiedMounts { + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err } } for dest := range unifiedVolumes { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) - } - if _, ok := overlayVolumes[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err } } for dest := range overlayVolumes { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err } - if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + } + for dest := range unifiedImageVolumes { + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err } } @@ -130,7 +135,7 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo if mount.Type == TypeBind { absSrc, err := filepath.Abs(mount.Source) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) + return nil, nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) } mount.Source = absSrc } @@ -144,8 +149,12 @@ func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bo for _, volume := range overlayVolumes { finalOverlayVolume = append(finalOverlayVolume, volume) } + finalImageVolumes := make([]*specgen.ImageVolume, 0, len(unifiedImageVolumes)) + for _, volume := range unifiedImageVolumes { + finalImageVolumes = append(finalImageVolumes, volume) + } - return finalMounts, finalVolumes, finalOverlayVolume, nil + return finalMounts, finalVolumes, finalOverlayVolume, finalImageVolumes, nil } // findMountType parses the input and extracts the type of the mount type and @@ -174,59 +183,69 @@ func findMountType(input string) (mountType string, tokens []string, err error) // podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... // podman run --mount type=tmpfs,target=/dev/shm ... // podman run --mount type=volume,source=test-volume, ... -func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) { +func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) { finalMounts := make(map[string]spec.Mount) finalNamedVolumes := make(map[string]*specgen.NamedVolume) + finalImageVolumes := make(map[string]*specgen.ImageVolume) for _, mount := range mountFlag { // TODO: Docker defaults to "volume" if no mount type is specified. mountType, tokens, err := findMountType(mount) if err != nil { - return nil, nil, err + return nil, nil, nil, err } switch mountType { case TypeBind: mount, err := getBindMount(tokens) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) } finalMounts[mount.Destination] = mount case TypeTmpfs: mount, err := getTmpfsMount(tokens) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) } finalMounts[mount.Destination] = mount case TypeDevpts: mount, err := getDevptsMount(tokens) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) } finalMounts[mount.Destination] = mount + case "image": + volume, err := getImageVolume(tokens) + if err != nil { + return nil, nil, nil, err + } + if _, ok := finalImageVolumes[volume.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Destination) + } + finalImageVolumes[volume.Destination] = volume case "volume": volume, err := getNamedVolume(tokens) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if _, ok := finalNamedVolumes[volume.Dest]; ok { - return nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) + return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) } finalNamedVolumes[volume.Dest] = volume default: - return nil, nil, errors.Errorf("invalid filesystem type %q", mountType) + return nil, nil, nil, errors.Errorf("invalid filesystem type %q", mountType) } } - return finalMounts, finalNamedVolumes, nil + return finalMounts, finalNamedVolumes, finalImageVolumes, nil } // Parse a single bind mount entry from the --mount flag. @@ -294,7 +313,7 @@ func getBindMount(args []string) (spec.Mount, error) { } setExec = true newMount.Options = append(newMount.Options, kv[0]) - case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z": + case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": newMount.Options = append(newMount.Options, kv[0]) case "bind-propagation": if len(kv) == 1 { @@ -531,6 +550,50 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { return newVolume, nil } +// Parse the arguments into an image volume. An image volume is a volume based +// on a container image. The container image is first mounted on the host and +// is then bind-mounted into the container. An ImageVolume is always mounted +// read only. +func getImageVolume(args []string) (*specgen.ImageVolume, error) { + newVolume := new(specgen.ImageVolume) + + for _, val := range args { + kv := strings.SplitN(val, "=", 2) + switch kv[0] { + case "src", "source": + if len(kv) == 1 { + return nil, errors.Wrapf(optionArgError, kv[0]) + } + newVolume.Source = kv[1] + case "target", "dst", "destination": + if len(kv) == 1 { + return nil, errors.Wrapf(optionArgError, kv[0]) + } + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { + return nil, err + } + newVolume.Destination = filepath.Clean(kv[1]) + case "rw", "readwrite": + switch kv[1] { + case "true": + newVolume.ReadWrite = true + case "false": + // Nothing to do. RO is default. + default: + return nil, errors.Wrapf(util.ErrBadMntOption, "invalid rw value %q", kv[1]) + } + default: + return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) + } + } + + if len(newVolume.Source)*len(newVolume.Destination) == 0 { + return nil, errors.Errorf("must set source and destination for image volume") + } + + return newVolume, nil +} + func getVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.OverlayVolume, error) { mounts := make(map[string]spec.Mount) volumes := make(map[string]*specgen.NamedVolume) |