mirror of
https://github.com/containers/podman.git
synced 2025-06-20 00:51:16 +08:00
Merge pull request #2033 from rhatdan/devices
Allow users to specify a directory for additonal devices
This commit is contained in:
@ -106,7 +106,7 @@ to be long lived. While Buildah containers are really just created to allow con
|
|||||||
to be added back to the container image. An easy way to think of it is the
|
to be added back to the container image. An easy way to think of it is the
|
||||||
`buildah run` command emulates the RUN command in a Dockerfile while the `podman run`
|
`buildah run` command emulates the RUN command in a Dockerfile while the `podman run`
|
||||||
command emulates the `docker run` command in functionality. Because of this and their underlying
|
command emulates the `docker run` command in functionality. Because of this and their underlying
|
||||||
storage differences, you can not see Podman containers from within Buildah or vice versa.
|
storage differences, you cannot see Podman containers from within Buildah or vice versa.
|
||||||
|
|
||||||
In short Buildah is an efficient way to create OCI images while Podman allows
|
In short Buildah is an efficient way to create OCI images while Podman allows
|
||||||
you to manage and maintain those images and containers in a production environment using
|
you to manage and maintain those images and containers in a production environment using
|
||||||
|
@ -462,7 +462,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
|
|||||||
tty := c.Bool("tty")
|
tty := c.Bool("tty")
|
||||||
|
|
||||||
if c.Bool("detach") && c.Bool("rm") {
|
if c.Bool("detach") && c.Bool("rm") {
|
||||||
return nil, errors.Errorf("--rm and --detach can not be specified together")
|
return nil, errors.Errorf("--rm and --detach cannot be specified together")
|
||||||
}
|
}
|
||||||
if c.Int64("cpu-period") != 0 && c.Float64("cpus") > 0 {
|
if c.Int64("cpu-period") != 0 && c.Float64("cpus") > 0 {
|
||||||
return nil, errors.Errorf("--cpu-period and --cpus cannot be set together")
|
return nil, errors.Errorf("--cpu-period and --cpus cannot be set together")
|
||||||
|
@ -144,7 +144,7 @@ func parseMounts(mounts []string) ([]spec.Mount, error) {
|
|||||||
mountInfo.Options = append(mountInfo.Options, kv[1])
|
mountInfo.Options = append(mountInfo.Options, kv[1])
|
||||||
case "src", "source":
|
case "src", "source":
|
||||||
if mountInfo.Type == "tmpfs" {
|
if mountInfo.Type == "tmpfs" {
|
||||||
return nil, errors.Errorf("can not use src= on a tmpfs file system")
|
return nil, errors.Errorf("cannot use src= on a tmpfs file system")
|
||||||
}
|
}
|
||||||
if err := validateVolumeHostDir(kv[1]); err != nil {
|
if err := validateVolumeHostDir(kv[1]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -288,7 +288,7 @@ func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, e
|
|||||||
return warnings, fmt.Errorf("minimum memory reservation allowed is 4MB")
|
return warnings, fmt.Errorf("minimum memory reservation allowed is 4MB")
|
||||||
}
|
}
|
||||||
if config.Resources.Memory > 0 && config.Resources.MemoryReservation > 0 && config.Resources.Memory < config.Resources.MemoryReservation {
|
if config.Resources.Memory > 0 && config.Resources.MemoryReservation > 0 && config.Resources.Memory < config.Resources.MemoryReservation {
|
||||||
return warnings, fmt.Errorf("minimum memory limit can not be less than memory reservation limit, see usage")
|
return warnings, fmt.Errorf("minimum memory limit cannot be less than memory reservation limit, see usage")
|
||||||
}
|
}
|
||||||
if config.Resources.KernelMemory > 0 && !sysInfo.KernelMemory {
|
if config.Resources.KernelMemory > 0 && !sysInfo.KernelMemory {
|
||||||
warnings = addWarning(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
|
warnings = addWarning(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
|
||||||
@ -318,14 +318,14 @@ func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, e
|
|||||||
config.Resources.CPUPeriod = 0
|
config.Resources.CPUPeriod = 0
|
||||||
}
|
}
|
||||||
if config.Resources.CPUPeriod != 0 && (config.Resources.CPUPeriod < 1000 || config.Resources.CPUPeriod > 1000000) {
|
if config.Resources.CPUPeriod != 0 && (config.Resources.CPUPeriod < 1000 || config.Resources.CPUPeriod > 1000000) {
|
||||||
return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
|
return warnings, fmt.Errorf("CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
|
||||||
}
|
}
|
||||||
if config.Resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota {
|
if config.Resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota {
|
||||||
warnings = addWarning(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
|
warnings = addWarning(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
|
||||||
config.Resources.CPUQuota = 0
|
config.Resources.CPUQuota = 0
|
||||||
}
|
}
|
||||||
if config.Resources.CPUQuota > 0 && config.Resources.CPUQuota < 1000 {
|
if config.Resources.CPUQuota > 0 && config.Resources.CPUQuota < 1000 {
|
||||||
return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)")
|
return warnings, fmt.Errorf("CPU cfs quota cannot be less than 1ms (i.e. 1000)")
|
||||||
}
|
}
|
||||||
// cpuset subsystem checks and adjustments
|
// cpuset subsystem checks and adjustments
|
||||||
if (config.Resources.CPUsetCPUs != "" || config.Resources.CPUsetMems != "") && !sysInfo.Cpuset {
|
if (config.Resources.CPUsetCPUs != "" || config.Resources.CPUsetMems != "") && !sysInfo.Cpuset {
|
||||||
|
@ -84,7 +84,7 @@ func (n UTSMode) Valid() bool {
|
|||||||
// IpcMode represents the container ipc stack.
|
// IpcMode represents the container ipc stack.
|
||||||
type IpcMode string
|
type IpcMode string
|
||||||
|
|
||||||
// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared.
|
// IsPrivate indicates whether the container uses its own private ipc namespace which cannot be shared.
|
||||||
func (n IpcMode) IsPrivate() bool {
|
func (n IpcMode) IsPrivate() bool {
|
||||||
return n == "private"
|
return n == "private"
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
package createconfig
|
package createconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/profiles/seccomp"
|
"github.com/docker/docker/profiles/seccomp"
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
@ -27,6 +31,52 @@ func Device(d *configs.Device) spec.LinuxDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// devicesFromPath computes a list of devices
|
||||||
|
func devicesFromPath(g *generate.Generator, devicePath string) error {
|
||||||
|
devs := strings.Split(devicePath, ":")
|
||||||
|
resolvedDevicePath := devs[0]
|
||||||
|
// check if it is a symbolic link
|
||||||
|
if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
|
if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil {
|
||||||
|
resolvedDevicePath = linkedPathOnHost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st, err := os.Stat(resolvedDevicePath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "cannot stat %s", devicePath)
|
||||||
|
}
|
||||||
|
if st.IsDir() {
|
||||||
|
if len(devs) > 2 {
|
||||||
|
return errors.Wrapf(unix.EINVAL, "not allowed to specify destination with a directory %s", devicePath)
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
// mount the internal devices recursively
|
||||||
|
if err := filepath.Walk(resolvedDevicePath, func(dpath string, f os.FileInfo, e error) error {
|
||||||
|
|
||||||
|
if f.Mode()&os.ModeDevice == os.ModeDevice {
|
||||||
|
found = true
|
||||||
|
device := dpath
|
||||||
|
|
||||||
|
if len(devs) > 1 {
|
||||||
|
device = fmt.Sprintf("%s:%s", dpath, devs[1])
|
||||||
|
}
|
||||||
|
if err := addDevice(g, device); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to add %s device", dpath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return errors.Wrapf(unix.EINVAL, "no devices found in %s", devicePath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return addDevice(g, devicePath)
|
||||||
|
}
|
||||||
|
|
||||||
func addDevice(g *generate.Generator, device string) error {
|
func addDevice(g *generate.Generator, device string) error {
|
||||||
src, dst, permissions, err := ParseDevice(device)
|
src, dst, permissions, err := ParseDevice(device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -166,7 +166,7 @@ func ParseDevice(device string) (string, string, string, error) { //nolint
|
|||||||
permissions = arr[1]
|
permissions = arr[1]
|
||||||
} else {
|
} else {
|
||||||
if arr[1][0] != '/' {
|
if arr[1][0] != '/' {
|
||||||
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2])
|
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[1])
|
||||||
}
|
}
|
||||||
dst = arr[1]
|
dst = arr[1]
|
||||||
}
|
}
|
||||||
|
@ -235,8 +235,8 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, device := range config.Devices {
|
for _, devicePath := range config.Devices {
|
||||||
if err := addDevice(&g, device); err != nil {
|
if err := devicesFromPath(&g, devicePath); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ var _ = Describe("Common functions test", func() {
|
|||||||
|
|
||||||
It("Test SystemExec", func() {
|
It("Test SystemExec", func() {
|
||||||
session := SystemExec(GoechoPath, []string{})
|
session := SystemExec(GoechoPath, []string{})
|
||||||
Expect(session.Command.Process).ShouldNot(BeNil(), "SystemExec can not start a process")
|
Expect(session.Command.Process).ShouldNot(BeNil(), "SystemExec cannot start a process")
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Test StringInSlice", func() {
|
It("Test StringInSlice", func() {
|
||||||
|
@ -18,7 +18,7 @@ and retry your command before reporting the issue.
|
|||||||
---
|
---
|
||||||
### 2) No such image or Bare keys cannot contain ':'
|
### 2) No such image or Bare keys cannot contain ':'
|
||||||
|
|
||||||
When doing a `podman pull` or `podman build` command and a "common" image can not be pulled,
|
When doing a `podman pull` or `podman build` command and a "common" image cannot be pulled,
|
||||||
it is likely that the `/etc/containers/registries.conf` file is either not installed or possibly
|
it is likely that the `/etc/containers/registries.conf` file is either not installed or possibly
|
||||||
misconfigured.
|
misconfigured.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user