diff options
Diffstat (limited to 'pkg/specgen/generate')
-rw-r--r-- | pkg/specgen/generate/config_linux.go | 150 | ||||
-rw-r--r-- | pkg/specgen/generate/config_linux_cgo.go | 11 | ||||
-rw-r--r-- | pkg/specgen/generate/container.go | 38 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 51 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/kube.go | 67 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/play_test.go | 4 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/seccomp.go | 6 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/volume.go | 31 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 94 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 43 | ||||
-rw-r--r-- | pkg/specgen/generate/pod_create.go | 107 | ||||
-rw-r--r-- | pkg/specgen/generate/ports.go | 27 | ||||
-rw-r--r-- | pkg/specgen/generate/security.go | 27 | ||||
-rw-r--r-- | pkg/specgen/generate/storage.go | 38 | ||||
-rw-r--r-- | pkg/specgen/generate/validate.go | 24 |
15 files changed, 368 insertions, 350 deletions
diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index ed2e5408d..a46966161 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -3,7 +3,6 @@ package generate import ( "fmt" "io/fs" - "io/ioutil" "os" "path" "path/filepath" @@ -11,63 +10,13 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/rootless" + "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -var ( - errNotADevice = errors.New("not a device node") -) - -func addPrivilegedDevices(g *generate.Generator) error { - hostDevices, err := getDevices("/dev") - if err != nil { - return err - } - g.ClearLinuxDevices() - - if rootless.IsRootless() { - mounts := make(map[string]interface{}) - for _, m := range g.Mounts() { - mounts[m.Destination] = true - } - newMounts := []spec.Mount{} - for _, d := range hostDevices { - devMnt := spec.Mount{ - Destination: d.Path, - Type: define.TypeBind, - Source: d.Path, - Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"}, - } - if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") { - continue - } - if _, found := mounts[d.Path]; found { - continue - } - newMounts = append(newMounts, devMnt) - } - g.Config.Mounts = append(newMounts, g.Config.Mounts...) - if g.Config.Linux.Resources != nil { - g.Config.Linux.Resources.Devices = nil - } - } else { - for _, d := range hostDevices { - g.AddDevice(d) - } - // Add resources device - need to clear the existing one first. - if g.Config.Linux.Resources != nil { - g.Config.Linux.Resources.Devices = nil - } - g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm") - } - - return nil -} - // DevicesFromPath computes a list of devices func DevicesFromPath(g *generate.Generator, devicePath string) error { devs := strings.Split(devicePath, ":") @@ -96,7 +45,7 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { } if len(devs) > 2 { if devmode != "" { - return errors.Wrapf(unix.EINVAL, "invalid device specification %s", devicePath) + return fmt.Errorf("invalid device specification %s: %w", devicePath, unix.EINVAL) } devmode = devs[2] } @@ -110,7 +59,7 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { device = fmt.Sprintf("%s:%s", device, devmode) } if err := addDevice(g, device); err != nil { - return errors.Wrapf(err, "failed to add %s device", dpath) + return fmt.Errorf("failed to add %s device: %w", dpath, err) } } return nil @@ -118,7 +67,7 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { return err } if !found { - return errors.Wrapf(unix.EINVAL, "no devices found in %s", devicePath) + return fmt.Errorf("no devices found in %s: %w", devicePath, unix.EINVAL) } return nil } @@ -174,62 +123,14 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask } } -// based on getDevices from runc (libcontainer/devices/devices.go) -func getDevices(path string) ([]spec.LinuxDevice, error) { - files, err := ioutil.ReadDir(path) - if err != nil { - if rootless.IsRootless() && os.IsPermission(err) { - return nil, nil - } - return nil, err - } - out := []spec.LinuxDevice{} - for _, f := range files { - switch { - case f.IsDir(): - switch f.Name() { - // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 - case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts": - continue - default: - sub, err := getDevices(filepath.Join(path, f.Name())) - if err != nil { - return nil, err - } - if sub != nil { - out = append(out, sub...) - } - continue - } - case f.Name() == "console": - continue - case f.Mode()&os.ModeSymlink != 0: - continue - } - - device, err := deviceFromPath(filepath.Join(path, f.Name())) - if err != nil { - if err == errNotADevice { - continue - } - if os.IsNotExist(err) { - continue - } - return nil, err - } - out = append(out, *device) - } - return out, nil -} - func addDevice(g *generate.Generator, device string) error { src, dst, permissions, err := ParseDevice(device) if err != nil { return err } - dev, err := deviceFromPath(src) + dev, err := util.DeviceFromPath(src) if err != nil { - return errors.Wrapf(err, "%s is not a valid device", src) + return fmt.Errorf("%s is not a valid device: %w", src, err) } if rootless.IsRootless() { if _, err := os.Stat(src); err != nil { @@ -262,7 +163,7 @@ func addDevice(g *generate.Generator, device string) error { } // ParseDevice parses device mapping string to a src, dest & permissions string -func ParseDevice(device string) (string, string, string, error) { //nolint +func ParseDevice(device string) (string, string, string, error) { var src string var dst string permissions := "rwm" @@ -316,43 +217,6 @@ func IsValidDeviceMode(mode string) bool { return true } -// Copied from github.com/opencontainers/runc/libcontainer/devices -// Given the path to a device look up the information about a linux device -func deviceFromPath(path string) (*spec.LinuxDevice, error) { - var stat unix.Stat_t - err := unix.Lstat(path, &stat) - if err != nil { - return nil, err - } - var ( - devType string - mode = stat.Mode - devNumber = uint64(stat.Rdev) // nolint: unconvert - m = os.FileMode(mode) - ) - - switch { - case mode&unix.S_IFBLK == unix.S_IFBLK: - devType = "b" - case mode&unix.S_IFCHR == unix.S_IFCHR: - devType = "c" - case mode&unix.S_IFIFO == unix.S_IFIFO: - devType = "p" - default: - return nil, errNotADevice - } - - return &spec.LinuxDevice{ - Type: devType, - Path: path, - FileMode: &m, - UID: &stat.Uid, - GID: &stat.Gid, - Major: int64(unix.Major(devNumber)), - Minor: int64(unix.Minor(devNumber)), - }, nil -} - func supportAmbientCapabilities() bool { err := unix.Prctl(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, 0, 0, 0) return err == nil diff --git a/pkg/specgen/generate/config_linux_cgo.go b/pkg/specgen/generate/config_linux_cgo.go index efab6679a..74ba4aeeb 100644 --- a/pkg/specgen/generate/config_linux_cgo.go +++ b/pkg/specgen/generate/config_linux_cgo.go @@ -5,6 +5,8 @@ package generate import ( "context" + "errors" + "fmt" "io/ioutil" "github.com/containers/common/libimage" @@ -12,7 +14,6 @@ import ( "github.com/containers/podman/v4/pkg/seccomp" "github.com/containers/podman/v4/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -39,7 +40,7 @@ func getSeccompConfig(s *specgen.SpecGenerator, configSpec *spec.Spec, img *libi logrus.Debug("Loading seccomp profile from the security config") seccompConfig, err = goSeccomp.LoadProfile(imagePolicy, configSpec) if err != nil { - return nil, errors.Wrap(err, "loading seccomp profile failed") + return nil, fmt.Errorf("loading seccomp profile failed: %w", err) } return seccompConfig, nil } @@ -48,17 +49,17 @@ func getSeccompConfig(s *specgen.SpecGenerator, configSpec *spec.Spec, img *libi logrus.Debugf("Loading seccomp profile from %q", s.SeccompProfilePath) seccompProfile, err := ioutil.ReadFile(s.SeccompProfilePath) if err != nil { - return nil, errors.Wrap(err, "opening seccomp profile failed") + return nil, fmt.Errorf("opening seccomp profile failed: %w", err) } seccompConfig, err = goSeccomp.LoadProfile(string(seccompProfile), configSpec) if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", s.SeccompProfilePath) + return nil, fmt.Errorf("loading seccomp profile (%s) failed: %w", s.SeccompProfilePath, err) } } else { logrus.Debug("Loading default seccomp profile") seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec) if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", s.SeccompProfilePath) + return nil, fmt.Errorf("loading seccomp profile (%s) failed: %w", s.SeccompProfilePath, err) } } diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index cc376125f..2248c9235 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -3,6 +3,7 @@ package generate import ( "context" "encoding/json" + "errors" "fmt" "os" "strings" @@ -17,7 +18,6 @@ import ( "github.com/containers/podman/v4/pkg/signal" "github.com/containers/podman/v4/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -38,10 +38,19 @@ func getImageFromSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGen } // Need to look up image. - image, resolvedName, err := r.LibimageRuntime().LookupImage(s.Image, nil) + lookupOptions := &libimage.LookupImageOptions{ManifestList: true} + image, resolvedName, err := r.LibimageRuntime().LookupImage(s.Image, lookupOptions) if err != nil { return nil, "", nil, err } + manifestList, err := image.ToManifestList() + // only process if manifest list found otherwise expect it to be regular image + if err == nil { + image, err = manifestList.LookupInstance(ctx, s.ImageArch, s.ImageOS, s.ImageVariant) + if err != nil { + return nil, "", nil, err + } + } s.SetImage(image, resolvedName) inspectData, err := image.Inspect(ctx, nil) if err != nil { @@ -106,7 +115,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // Get Default Environment from containers.conf defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnvEx(s.EnvHost, s.HTTPProxy)) if err != nil { - return nil, errors.Wrap(err, "error parsing fields in containers.conf") + return nil, fmt.Errorf("error parsing fields in containers.conf: %w", err) } var envs map[string]string @@ -116,7 +125,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // already, overriding the default environments envs, err = envLib.ParseSlice(inspectData.Config.Env) if err != nil { - return nil, errors.Wrap(err, "Env fields from image failed to parse") + return nil, fmt.Errorf("env fields from image failed to parse: %w", err) } defaultEnvs = envLib.Join(envLib.DefaultEnvVariables(), envLib.Join(defaultEnvs, envs)) } @@ -132,7 +141,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat // any case. osEnv, err := envLib.ParseSlice(os.Environ()) if err != nil { - return nil, errors.Wrap(err, "error parsing host environment variables") + return nil, fmt.Errorf("error parsing host environment variables: %w", err) } // Caller Specified defaults if s.EnvHost { @@ -303,8 +312,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert if s.ResourceLimits.BlockIO == nil { s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO) } @@ -317,8 +326,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v) } } @@ -328,8 +337,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) } } @@ -339,8 +348,8 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(uint64(statT.Rdev)))) // nolint: unconvert - v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) // nolint: unconvert + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) //nolint: unconvert + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) //nolint: unconvert s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v) } } @@ -450,7 +459,7 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s specg.IpcNS = specgen.Namespace{NSMode: specgen.Default} // default } case "uts": - specg.UtsNS = specgen.Namespace{NSMode: specgen.Default} // default + specg.UtsNS = specgen.Namespace{NSMode: specgen.Private} // default case "user": if conf.AddCurrentUserPasswdEntry { specg.UserNS = specgen.Namespace{NSMode: specgen.KeepID} @@ -506,6 +515,7 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID s specg.Mounts = mounts specg.HostDeviceList = conf.DeviceHostSrc specg.Networks = conf.Networks + specg.ShmSize = &conf.ShmSize mapSecurityConfig(conf, specg) diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 04e24d625..51d290bb4 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -3,6 +3,8 @@ package generate import ( "context" "encoding/json" + "errors" + "fmt" "path/filepath" "strings" @@ -15,7 +17,6 @@ import ( "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -34,7 +35,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if s.Pod != "" { pod, err = rt.LookupPod(s.Pod) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) + return nil, nil, nil, fmt.Errorf("error retrieving pod %s: %w", s.Pod, err) } if pod.HasInfraContainer() { infra, err = pod.InfraContainer() @@ -133,8 +134,14 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener options = append(options, libpod.WithRootFSFromImage(newImage.ID(), resolvedImageName, s.RawImageName)) } + + _, err = rt.LookupPod(s.Hostname) + if len(s.Hostname) > 0 && !s.UtsNS.IsPrivate() && err == nil { + // ok, we are incorrectly setting the pod as the hostname, lets undo that before validation + s.Hostname = "" + } if err := s.Validate(); err != nil { - return nil, nil, nil, errors.Wrap(err, "invalid config provided") + return nil, nil, nil, fmt.Errorf("invalid config provided: %w", err) } finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage) @@ -180,10 +187,23 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if err != nil { return nil, nil, nil, err } + resources := runtimeSpec.Linux.Resources + + // resources get overwrritten similarly to pod inheritance, manually assign here if there is a new value + marshalRes, err := json.Marshal(resources) + if err != nil { + return nil, nil, nil, err + } + err = json.Unmarshal(out, runtimeSpec.Linux) if err != nil { return nil, nil, nil, err } + + err = json.Unmarshal(marshalRes, runtimeSpec.Linux.Resources) + if err != nil { + return nil, nil, nil, err + } } if s.ResourceLimits != nil { switch { @@ -278,6 +298,10 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l options = append(options, libpod.WithPasswdEntry(s.PasswdEntry)) } + if s.Privileged { + options = append(options, libpod.WithMountAllDevices()) + } + useSystemd := false switch s.Systemd { case "always": @@ -309,7 +333,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l } } default: - return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd) + return nil, fmt.Errorf("invalid value %q systemd option requires 'true, false, always': %w", s.Systemd, err) } logrus.Debugf("using systemd mode: %t", useSystemd) if useSystemd { @@ -318,7 +342,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l if s.StopSignal == nil { stopSignal, err := util.ParseSignal("RTMIN+3") if err != nil { - return nil, errors.Wrapf(err, "error parsing systemd signal") + return nil, fmt.Errorf("error parsing systemd signal: %w", err) } s.StopSignal = &stopSignal } @@ -513,7 +537,7 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l for _, ctr := range s.DependencyContainers { depCtr, err := rt.LookupContainer(ctr) if err != nil { - return nil, errors.Wrapf(err, "%q is not a valid container, cannot be used as a dependency", ctr) + return nil, fmt.Errorf("%q is not a valid container, cannot be used as a dependency: %w", ctr, err) } deps = append(deps, depCtr) } @@ -542,6 +566,16 @@ func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtim infraConf := infra.Config() infraSpec := infraConf.Spec + // need to set compatOptions to the currently filled specgenOptions so we do not overwrite + compatibleOptions.CapAdd = append(compatibleOptions.CapAdd, s.CapAdd...) + compatibleOptions.CapDrop = append(compatibleOptions.CapDrop, s.CapDrop...) + compatibleOptions.HostDeviceList = append(compatibleOptions.HostDeviceList, s.HostDeviceList...) + compatibleOptions.ImageVolumes = append(compatibleOptions.ImageVolumes, s.ImageVolumes...) + compatibleOptions.Mounts = append(compatibleOptions.Mounts, s.Mounts...) + compatibleOptions.OverlayVolumes = append(compatibleOptions.OverlayVolumes, s.OverlayVolumes...) + compatibleOptions.SelinuxOpts = append(compatibleOptions.SelinuxOpts, s.SelinuxOpts...) + compatibleOptions.Volumes = append(compatibleOptions.Volumes, s.Volumes...) + compatByte, err := json.Marshal(compatibleOptions) if err != nil { return nil, nil, nil, err @@ -550,5 +584,10 @@ func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtim if err != nil { return nil, nil, nil, err } + + // this causes errors when shmSize is the default value, it will still get passed down unless we manually override. + if s.IpcNS.NSMode == specgen.Host && (compatibleOptions.ShmSize != nil && compatibleOptions.IsDefaultShmSize()) { + s.ShmSize = nil + } return options, infraSpec, compatibleOptions, nil } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index f37d79798..454a1e1d0 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -3,6 +3,7 @@ package kube import ( "context" "encoding/json" + "errors" "fmt" "math" "net" @@ -16,6 +17,7 @@ import ( "github.com/containers/common/libnetwork/types" "github.com/containers/common/pkg/parse" "github.com/containers/common/pkg/secrets" + cutil "github.com/containers/common/pkg/util" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v4/libpod/define" ann "github.com/containers/podman/v4/pkg/annotations" @@ -28,7 +30,6 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/go-units" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -145,7 +146,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // pod name should be non-empty for Deployment objects to be able to create // multiple pods having containers with unique names if len(opts.PodName) < 1 { - return nil, errors.Errorf("got empty pod name on container creation when playing kube") + return nil, errors.New("got empty pod name on container creation when playing kube") } s.Name = fmt.Sprintf("%s-%s", opts.PodName, opts.Container.Name) @@ -162,7 +163,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener for _, o := range opts.LogOptions { split := strings.SplitN(o, "=", 2) if len(split) < 2 { - return nil, errors.Errorf("invalid log option %q", o) + return nil, fmt.Errorf("invalid log option %q", o) } switch strings.ToLower(split[0]) { case "driver": @@ -178,7 +179,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener default: switch len(split[1]) { case 0: - return nil, errors.Wrapf(define.ErrInvalidArg, "invalid log option") + return nil, fmt.Errorf("invalid log option: %w", define.ErrInvalidArg) default: // tags for journald only if s.LogConfiguration.Driver == "" || s.LogConfiguration.Driver == define.JournaldLogging { @@ -195,7 +196,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener setupSecurityContext(s, opts.Container.SecurityContext, opts.PodSecurityContext) err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy) if err != nil { - return nil, errors.Wrap(err, "Failed to configure livenessProbe") + return nil, fmt.Errorf("failed to configure livenessProbe: %w", err) } // Since we prefix the container name with pod name to work-around the uniqueness requirement, @@ -206,7 +207,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener s.ResourceLimits = &spec.LinuxResources{} milliCPU, err := quantityToInt64(opts.Container.Resources.Limits.Cpu()) if err != nil { - return nil, errors.Wrap(err, "Failed to set CPU quota") + return nil, fmt.Errorf("failed to set CPU quota: %w", err) } if milliCPU > 0 { period, quota := util.CoresToPeriodAndQuota(float64(milliCPU)) @@ -218,12 +219,12 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener limit, err := quantityToInt64(opts.Container.Resources.Limits.Memory()) if err != nil { - return nil, errors.Wrap(err, "Failed to set memory limit") + return nil, fmt.Errorf("failed to set memory limit: %w", err) } memoryRes, err := quantityToInt64(opts.Container.Resources.Requests.Memory()) if err != nil { - return nil, errors.Wrap(err, "Failed to set memory reservation") + return nil, fmt.Errorf("failed to set memory reservation: %w", err) } if limit > 0 || memoryRes > 0 { @@ -336,7 +337,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener for _, volume := range opts.Container.VolumeMounts { volumeSource, exists := opts.Volumes[volume.Name] if !exists { - return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) + return nil, fmt.Errorf("volume mount %s specified for container but not configured in volumes", volume.Name) } // Skip if the volume is optional. This means that a configmap for a configmap volume was not found but it was // optional so we can move on without throwing an error @@ -356,7 +357,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // a selinux mount option exists for it for k, v := range opts.Annotations { // Make sure the z/Z option is not already there (from editing the YAML) - if strings.Replace(k, define.BindMountPrefix, "", 1) == volumeSource.Source && !util.StringInSlice("z", options) && !util.StringInSlice("Z", options) { + if strings.Replace(k, define.BindMountPrefix, "", 1) == volumeSource.Source && !cutil.StringInSlice("z", options) && !cutil.StringInSlice("Z", options) { options = append(options, v) } } @@ -398,7 +399,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } s.Devices = append(s.Devices, device) default: - return nil, errors.Errorf("Unsupported volume source type") + return nil, errors.New("unsupported volume source type") } } @@ -431,21 +432,21 @@ func parseMountPath(mountPath string, readOnly bool, propagationMode *v1.MountPr options := []string{} splitVol := strings.Split(mountPath, ":") if len(splitVol) > 2 { - return "", options, errors.Errorf("%q incorrect volume format, should be ctr-dir[:option]", mountPath) + return "", options, fmt.Errorf("%q incorrect volume format, should be ctr-dir[:option]", mountPath) } dest := splitVol[0] if len(splitVol) > 1 { options = strings.Split(splitVol[1], ",") } if err := parse.ValidateVolumeCtrDir(dest); err != nil { - return "", options, errors.Wrapf(err, "parsing MountPath") + return "", options, fmt.Errorf("parsing MountPath: %w", err) } if readOnly { options = append(options, "ro") } opts, err := parse.ValidateVolumeOpts(options) if err != nil { - return "", opts, errors.Wrapf(err, "parsing MountOptions") + return "", opts, fmt.Errorf("parsing MountOptions: %w", err) } if propagationMode != nil { switch *propagationMode { @@ -456,7 +457,7 @@ func parseMountPath(mountPath string, readOnly bool, propagationMode *v1.MountPr case v1.MountPropagationBidirectional: opts = append(opts, "rshared") default: - return "", opts, errors.Errorf("unknown propagation mode %q", *propagationMode) + return "", opts, fmt.Errorf("unknown propagation mode %q", *propagationMode) } } return dest, opts, nil @@ -503,19 +504,19 @@ func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, re func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32, startPeriod int32) (*manifest.Schema2HealthConfig, error) { // Every healthcheck requires a command if len(inCmd) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") + return nil, errors.New("must define a healthcheck command for all healthchecks") } // first try to parse option value as JSON array of strings... cmd := []string{} if inCmd == "none" { - cmd = []string{"NONE"} + cmd = []string{define.HealthConfigTestNone} } else { err := json.Unmarshal([]byte(inCmd), &cmd) if err != nil { // ...otherwise pass it to "/bin/sh -c" inside the container - cmd = []string{"CMD-SHELL"} + cmd = []string{define.HealthConfigTestCmdShell} cmd = append(cmd, strings.Split(inCmd, " ")...) } } @@ -629,7 +630,7 @@ func quantityToInt64(quantity *resource.Quantity) (int64, error) { return i, nil } - return 0, errors.Errorf("Quantity cannot be represented as int64: %v", quantity) + return 0, fmt.Errorf("quantity cannot be represented as int64: %v", quantity) } // read a k8s secret in JSON format from the secret manager @@ -641,7 +642,7 @@ func k8sSecretFromSecretManager(name string, secretsManager *secrets.SecretsMana var secrets map[string][]byte if err := json.Unmarshal(jsonSecret, &secrets); err != nil { - return nil, errors.Errorf("Secret %v is not valid JSON: %v", name, err) + return nil, fmt.Errorf("secret %v is not valid JSON: %v", name, err) } return secrets, nil } @@ -652,7 +653,7 @@ func envVarsFrom(envFrom v1.EnvFromSource, opts *CtrSpecGenOptions) (map[string] if envFrom.ConfigMapRef != nil { cmRef := envFrom.ConfigMapRef - err := errors.Errorf("Configmap %v not found", cmRef.Name) + err := fmt.Errorf("configmap %v not found", cmRef.Name) for _, c := range opts.ConfigMaps { if cmRef.Name == c.Name { @@ -688,14 +689,14 @@ func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { if env.ValueFrom != nil { if env.ValueFrom.ConfigMapKeyRef != nil { cmKeyRef := env.ValueFrom.ConfigMapKeyRef - err := errors.Errorf("Cannot set env %v: configmap %v not found", env.Name, cmKeyRef.Name) + err := fmt.Errorf("cannot set env %v: configmap %v not found", env.Name, cmKeyRef.Name) for _, c := range opts.ConfigMaps { if cmKeyRef.Name == c.Name { if value, ok := c.Data[cmKeyRef.Key]; ok { return &value, nil } - err = errors.Errorf("Cannot set env %v: key %s not found in configmap %v", env.Name, cmKeyRef.Key, cmKeyRef.Name) + err = fmt.Errorf("cannot set env %v: key %s not found in configmap %v", env.Name, cmKeyRef.Key, cmKeyRef.Name) break } } @@ -713,10 +714,10 @@ func envVarValue(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error) { value := string(val) return &value, nil } - err = errors.Errorf("Secret %v has not %v key", secKeyRef.Name, secKeyRef.Key) + err = fmt.Errorf("secret %v has not %v key", secKeyRef.Name, secKeyRef.Key) } if secKeyRef.Optional == nil || !*secKeyRef.Optional { - return nil, errors.Errorf("Cannot set env %v: %v", env.Name, err) + return nil, fmt.Errorf("cannot set env %v: %v", env.Name, err) } return nil, nil } @@ -760,8 +761,8 @@ func envVarValueFieldRef(env v1.EnvVar, opts *CtrSpecGenOptions) (*string, error return &annotationValue, nil } - return nil, errors.Errorf( - "Can not set env %v. Reason: fieldPath %v is either not valid or not supported", + return nil, fmt.Errorf( + "can not set env %v. Reason: fieldPath %v is either not valid or not supported", env.Name, fieldPath, ) } @@ -795,22 +796,22 @@ func envVarValueResourceFieldRef(env v1.EnvVar, opts *CtrSpecGenOptions) (*strin value = resources.Requests.Cpu() isValidDivisor = isCPUDivisor(divisor) default: - return nil, errors.Errorf( - "Can not set env %v. Reason: resource %v is either not valid or not supported", + return nil, fmt.Errorf( + "can not set env %v. Reason: resource %v is either not valid or not supported", env.Name, resourceName, ) } if !isValidDivisor { - return nil, errors.Errorf( - "Can not set env %s. Reason: divisor value %s is not valid", + return nil, fmt.Errorf( + "can not set env %s. Reason: divisor value %s is not valid", env.Name, divisor.String(), ) } // k8s rounds up the result to the nearest integer - intValue := int(math.Ceil(value.AsApproximateFloat64() / divisor.AsApproximateFloat64())) - stringValue := strconv.Itoa(intValue) + intValue := int64(math.Ceil(value.AsApproximateFloat64() / divisor.AsApproximateFloat64())) + stringValue := strconv.FormatInt(intValue, 10) return &stringValue, nil } diff --git a/pkg/specgen/generate/kube/play_test.go b/pkg/specgen/generate/kube/play_test.go index e01d62b08..466dab610 100644 --- a/pkg/specgen/generate/kube/play_test.go +++ b/pkg/specgen/generate/kube/play_test.go @@ -2,7 +2,6 @@ package kube import ( "encoding/json" - "fmt" "math" "runtime" "strconv" @@ -777,8 +776,7 @@ func TestEnvVarValue(t *testing.T) { if test.expected == nilString { assert.Nil(t, result) } else { - fmt.Println(*result, test.expected) - assert.Equal(t, &(test.expected), result) + assert.Equal(t, test.expected, *result) } }) } diff --git a/pkg/specgen/generate/kube/seccomp.go b/pkg/specgen/generate/kube/seccomp.go index 8f93b34ff..6e3accd8b 100644 --- a/pkg/specgen/generate/kube/seccomp.go +++ b/pkg/specgen/generate/kube/seccomp.go @@ -1,12 +1,12 @@ package kube import ( + "fmt" "path/filepath" "strings" "github.com/containers/podman/v4/libpod" v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" - "github.com/pkg/errors" ) // KubeSeccompPaths holds information about a pod YAML's seccomp configuration @@ -42,7 +42,7 @@ func InitializeSeccompPaths(annotations map[string]string, profileRoot string) ( // this could be caused by a user inputting either of // container.seccomp.security.alpha.kubernetes.io{,/} // both of which are invalid - return nil, errors.Errorf("Invalid seccomp path: %s", prefixAndCtr[0]) + return nil, fmt.Errorf("invalid seccomp path: %s", prefixAndCtr[0]) } path, err := verifySeccompPath(seccomp, profileRoot) @@ -80,6 +80,6 @@ func verifySeccompPath(path string, profileRoot string) (string, error) { if parts[0] == "localhost" { return filepath.Join(profileRoot, parts[1]), nil } - return "", errors.Errorf("invalid seccomp path: %s", path) + return "", fmt.Errorf("invalid seccomp path: %s", path) } } diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index 1d6d49b9d..f5c0c241d 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -1,12 +1,13 @@ package kube import ( + "errors" + "fmt" "os" "github.com/containers/common/pkg/parse" "github.com/containers/podman/v4/libpod" v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -56,13 +57,13 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) } // Label a newly created volume if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) + return nil, fmt.Errorf("error giving %s a label: %w", hostPath.Path, err) } case v1.HostPathFileOrCreate: if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission) if err != nil { - return nil, errors.Wrap(err, "error creating HostPath") + return nil, fmt.Errorf("error creating HostPath: %w", err) } if err := f.Close(); err != nil { logrus.Warnf("Error in closing newly created HostPath file: %v", err) @@ -70,23 +71,23 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) } // unconditionally label a newly created volume if err := libpod.LabelVolumePath(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) + return nil, fmt.Errorf("error giving %s a label: %w", hostPath.Path, err) } case v1.HostPathSocket: st, err := os.Stat(hostPath.Path) if err != nil { - return nil, errors.Wrap(err, "error checking HostPathSocket") + return nil, fmt.Errorf("error checking HostPathSocket: %w", err) } if st.Mode()&os.ModeSocket != os.ModeSocket { - return nil, errors.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path) + return nil, fmt.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path) } case v1.HostPathBlockDev: dev, err := os.Stat(hostPath.Path) if err != nil { - return nil, errors.Wrap(err, "error checking HostPathBlockDevice") + return nil, fmt.Errorf("error checking HostPathBlockDevice: %w", err) } if dev.Mode()&os.ModeCharDevice == os.ModeCharDevice { - return nil, errors.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path) + return nil, fmt.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path) } return &KubeVolume{ Type: KubeVolumeTypeBlockDevice, @@ -95,10 +96,10 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) case v1.HostPathCharDev: dev, err := os.Stat(hostPath.Path) if err != nil { - return nil, errors.Wrap(err, "error checking HostPathCharDevice") + return nil, fmt.Errorf("error checking HostPathCharDevice: %w", err) } if dev.Mode()&os.ModeCharDevice != os.ModeCharDevice { - return nil, errors.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path) + return nil, fmt.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path) } return &KubeVolume{ Type: KubeVolumeTypeCharDevice, @@ -110,12 +111,12 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) // do nothing here because we will verify the path exists in validateVolumeHostDir break default: - return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type) + return nil, fmt.Errorf("invalid HostPath type %v", hostPath.Type) } } if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { - return nil, errors.Wrapf(err, "error in parsing HostPath in YAML") + return nil, fmt.Errorf("error in parsing HostPath in YAML: %w", err) } return &KubeVolume{ @@ -152,7 +153,7 @@ func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, config kv.Optional = *configMapVolumeSource.Optional return kv, nil } - return nil, errors.Errorf("no such ConfigMap %q", configMapVolumeSource.Name) + return nil, fmt.Errorf("no such ConfigMap %q", configMapVolumeSource.Name) } // If there are Items specified in the volumeSource, that overwrites the Data from the configmap @@ -180,7 +181,7 @@ func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap) ( case volumeSource.ConfigMap != nil: return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps) default: - return nil, errors.Errorf("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") + return nil, errors.New("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") } } @@ -191,7 +192,7 @@ func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap) (map[ for _, specVolume := range specVolumes { volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps) if err != nil { - return nil, errors.Wrapf(err, "failed to create volume %q", specVolume.Name) + return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err) } volumes[specVolume.Name] = volume diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 37d561ec2..f0d4e9153 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -1,6 +1,7 @@ package generate import ( + "errors" "fmt" "os" "strings" @@ -15,10 +16,11 @@ import ( "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) +const host = "host" + // Get the default namespace mode for any given namespace type. func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) (specgen.Namespace, error) { // The default for most is private @@ -33,16 +35,38 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) podMode := false switch { case nsType == "pid" && pod.SharesPID(): + if pod.NamespaceMode(spec.PIDNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "ipc" && pod.SharesIPC(): + if pod.NamespaceMode(spec.IPCNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "uts" && pod.SharesUTS(): + if pod.NamespaceMode(spec.UTSNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "user" && pod.SharesUser(): + // user does not need a special check for host, this is already validated on pod creation + // if --userns=host then pod.SharesUser == false podMode = true case nsType == "net" && pod.SharesNet(): + if pod.NetworkMode() == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "cgroup" && pod.SharesCgroup(): + if pod.NamespaceMode(spec.CgroupNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true } if podMode { @@ -70,7 +94,7 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) return ns, err } - return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %q passed", nsType) + return toReturn, fmt.Errorf("invalid namespace type %q passed: %w", nsType, define.ErrInvalidArg) } // namespaceOptions generates container creation options for all @@ -89,18 +113,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if err != nil { // This is likely to be of the fatal kind (pod was // removed) so hard fail - return nil, errors.Wrapf(err, "error looking up pod %s infra container", pod.ID()) + return nil, fmt.Errorf("error looking up pod %s infra container: %w", pod.ID(), err) } if infraID != "" { ctr, err := rt.GetContainer(infraID) if err != nil { - return nil, errors.Wrapf(err, "error retrieving pod %s infra container %s", pod.ID(), infraID) + return nil, fmt.Errorf("error retrieving pod %s infra container %s: %w", pod.ID(), infraID, err) } infraCtr = ctr } } - errNoInfra := errors.Wrapf(define.ErrInvalidArg, "cannot use pod namespace as container is not joining a pod or pod has no infra container") + errNoInfra := fmt.Errorf("cannot use pod namespace as container is not joining a pod or pod has no infra container: %w", define.ErrInvalidArg) // PID switch s.PidNS.NSMode { @@ -112,7 +136,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: pidCtr, err := rt.LookupContainer(s.PidNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share pid namespace with") + return nil, fmt.Errorf("error looking up container to share pid namespace with: %w", err) } toReturn = append(toReturn, libpod.WithPIDNSFrom(pidCtr)) } @@ -131,10 +155,10 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: ipcCtr, err := rt.LookupContainer(s.IpcNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share ipc namespace with") + return nil, fmt.Errorf("error looking up container to share ipc namespace with: %w", err) } if ipcCtr.ConfigNoCopy().NoShmShare { - return nil, errors.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID()) + return nil, fmt.Errorf("joining IPC of container %s is not allowed: non-shareable IPC (hint: use IpcMode:shareable for the donor container)", ipcCtr.ID()) } toReturn = append(toReturn, libpod.WithIPCNSFrom(ipcCtr)) if !ipcCtr.ConfigNoCopy().NoShm { @@ -152,11 +176,18 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if pod == nil || infraCtr == nil { return nil, errNoInfra } - toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr)) + if pod.NamespaceMode(spec.UTSNamespace) == host { + // adding infra as a nsCtr is not what we want to do when uts == host + // this leads the new ctr to try to add an ns path which is should not in this mode + logrus.Debug("pod has host uts, not adding infra as a nsCtr") + s.UtsNS = specgen.Namespace{NSMode: specgen.Host} + } else { + toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr)) + } case specgen.FromContainer: utsCtr, err := rt.LookupContainer(s.UtsNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share uts namespace with") + return nil, fmt.Errorf("error looking up container to share uts namespace with: %w", err) } toReturn = append(toReturn, libpod.WithUTSNSFrom(utsCtr)) } @@ -191,7 +222,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: userCtr, err := rt.LookupContainer(s.UserNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share user namespace with") + return nil, fmt.Errorf("error looking up container to share user namespace with: %w", err) } toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr)) } @@ -203,7 +234,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. if pod == nil { toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings)) } else if pod.HasInfraContainer() && (len(s.IDMappings.UIDMap) > 0 || len(s.IDMappings.GIDMap) > 0) { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot specify a new uid/gid map when entering a pod with an infra container") + return nil, fmt.Errorf("cannot specify a new uid/gid map when entering a pod with an infra container: %w", define.ErrInvalidArg) } } if s.User != "" { @@ -223,7 +254,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: cgroupCtr, err := rt.LookupContainer(s.CgroupNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share cgroup namespace with") + return nil, fmt.Errorf("error looking up container to share cgroup namespace with: %w", err) } toReturn = append(toReturn, libpod.WithCgroupNSFrom(cgroupCtr)) } @@ -236,10 +267,12 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode)) } - // Net - // TODO validate CNINetworks, StaticIP, StaticIPv6 are only set if we - // are in bridge mode. postConfigureNetNS := !s.UserNS.IsHost() + // when we are rootless we default to slirp4netns + if rootless.IsRootless() && (s.NetNS.IsPrivate() || s.NetNS.IsDefault()) { + s.NetNS.NSMode = specgen.Slirp + } + switch s.NetNS.NSMode { case specgen.FromPod: if pod == nil || infraCtr == nil { @@ -249,7 +282,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. case specgen.FromContainer: netCtr, err := rt.LookupContainer(s.NetNS.Value) if err != nil { - return nil, errors.Wrapf(err, "error looking up container to share net namespace with") + return nil, fmt.Errorf("error looking up container to share net namespace with: %w", err) } toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr)) case specgen.Slirp: @@ -262,9 +295,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) } toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) - case specgen.Private: - fallthrough - case specgen.Bridge: + case specgen.Bridge, specgen.Private, specgen.Default: portMappings, expose, err := createPortMappings(s, imageData) if err != nil { return nil, err @@ -331,7 +362,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.PidNS.NSMode { case specgen.Path: if _, err := os.Stat(s.PidNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified PID namespace path") + return fmt.Errorf("cannot find specified PID namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), s.PidNS.Value); err != nil { return err @@ -350,7 +381,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.IpcNS.NSMode { case specgen.Path: if _, err := os.Stat(s.IpcNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified IPC namespace path") + return fmt.Errorf("cannot find specified IPC namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), s.IpcNS.Value); err != nil { return err @@ -369,7 +400,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.UtsNS.NSMode { case specgen.Path: if _, err := os.Stat(s.UtsNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified UTS namespace path") + return fmt.Errorf("cannot find specified UTS namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), s.UtsNS.Value); err != nil { return err @@ -392,13 +423,13 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt case s.UtsNS.NSMode == specgen.FromContainer: utsCtr, err := rt.LookupContainer(s.UtsNS.Value) if err != nil { - return errors.Wrapf(err, "error looking up container to share uts namespace with") + return fmt.Errorf("error looking up container to share uts namespace with: %w", err) } hostname = utsCtr.Hostname() case (s.NetNS.NSMode == specgen.Host && hostname == "") || s.UtsNS.NSMode == specgen.Host: tmpHostname, err := os.Hostname() if err != nil { - return errors.Wrap(err, "unable to retrieve hostname of the host") + return fmt.Errorf("unable to retrieve hostname of the host: %w", err) } hostname = tmpHostname default: @@ -427,7 +458,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.CgroupNS.NSMode { case specgen.Path: if _, err := os.Stat(s.CgroupNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified cgroup namespace path") + return fmt.Errorf("cannot find specified cgroup namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), s.CgroupNS.Value); err != nil { return err @@ -446,7 +477,7 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt switch s.NetNS.NSMode { case specgen.Path: if _, err := os.Stat(s.NetNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified network namespace path") + return fmt.Errorf("cannot find specified network namespace path: %w", err) } if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), s.NetNS.Value); err != nil { return err @@ -488,12 +519,9 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio case "cgroup": options = append(options, libpod.WithPodCgroup()) case "net": - // share the netns setting with other containers in the pod only when it is not set to host - if !netnsIsHost { - options = append(options, libpod.WithPodNet()) - } + options = append(options, libpod.WithPodNet()) case "mnt": - return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level") + return erroredOptions, fmt.Errorf("mount sharing functionality not supported on pod level") case "pid": options = append(options, libpod.WithPodPID()) case "user": @@ -506,7 +534,7 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio case "none": return erroredOptions, nil default: - return erroredOptions, errors.Errorf("Invalid kernel namespace to share: %s. Options are: cgroup, ipc, net, pid, uts or none", toShare) + return erroredOptions, fmt.Errorf("invalid kernel namespace to share: %s. Options are: cgroup, ipc, net, pid, uts or none", toShare) } } return options, nil diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index dda2de6e4..bb5f2d0ec 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -3,6 +3,7 @@ package generate import ( "context" "encoding/json" + "fmt" "path" "strings" @@ -15,7 +16,6 @@ import ( "github.com/containers/podman/v4/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -117,7 +117,7 @@ func makeCommand(s *specgen.SpecGenerator, imageData *libimage.ImageData, rtc *c finalCommand = append(finalCommand, command...) if len(finalCommand) == 0 { - return nil, errors.Errorf("no command or entrypoint provided, and no CMD or ENTRYPOINT from image") + return nil, fmt.Errorf("no command or entrypoint provided, and no CMD or ENTRYPOINT from image") } if s.Init { @@ -126,7 +126,7 @@ func makeCommand(s *specgen.SpecGenerator, imageData *libimage.ImageData, rtc *c initPath = rtc.Engine.InitPath } if initPath == "" { - return nil, errors.Errorf("no path to init binary found but container requested an init") + return nil, fmt.Errorf("no path to init binary found but container requested an init") } finalCommand = append([]string{define.ContainerInitPath, "--"}, finalCommand...) } @@ -298,8 +298,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.AddAnnotation(key, val) } - switch { - case compatibleOptions.InfraResources == nil && s.ResourceLimits != nil: + if s.ResourceLimits != nil { out, err := json.Marshal(s.ResourceLimits) if err != nil { return nil, err @@ -308,43 +307,17 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt if err != nil { return nil, err } - case s.ResourceLimits != nil: // if we have predefined resource limits we need to make sure we keep the infra and container limits - originalResources, err := json.Marshal(s.ResourceLimits) - if err != nil { - return nil, err - } - infraResources, err := json.Marshal(compatibleOptions.InfraResources) - if err != nil { - return nil, err - } - err = json.Unmarshal(infraResources, s.ResourceLimits) // put infra's resource limits in the container - if err != nil { - return nil, err - } - err = json.Unmarshal(originalResources, s.ResourceLimits) // make sure we did not override anything - if err != nil { - return nil, err - } g.Config.Linux.Resources = s.ResourceLimits - default: - g.Config.Linux.Resources = compatibleOptions.InfraResources } // Devices - // set the default rule at the beginning of device configuration if !inUserNS && !s.Privileged { g.AddLinuxResourcesDevice(false, "", nil, nil, "rwm") } var userDevices []spec.LinuxDevice - if s.Privileged { - // If privileged, we need to add all the host devices to the - // spec. We do not add the user provided ones because we are - // already adding them all. - if err := addPrivilegedDevices(&g); err != nil { - return nil, err - } - } else { + + if !s.Privileged { // add default devices from containers.conf for _, device := range rtc.Containers.Devices { if err = DevicesFromPath(&g, device); err != nil { @@ -375,9 +348,9 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt for k, v := range s.WeightDevice { statT := unix.Stat_t{} if err := unix.Stat(k, &statT); err != nil { - return nil, errors.Wrapf(err, "failed to inspect '%s' in --blkio-weight-device", k) + return nil, fmt.Errorf("failed to inspect '%s' in --blkio-weight-device: %w", k, err) } - g.AddLinuxResourcesBlockIOWeightDevice((int64(unix.Major(uint64(statT.Rdev)))), (int64(unix.Minor(uint64(statT.Rdev)))), *v.Weight) // nolint: unconvert + g.AddLinuxResourcesBlockIOWeightDevice((int64(unix.Major(uint64(statT.Rdev)))), (int64(unix.Minor(uint64(statT.Rdev)))), *v.Weight) //nolint: unconvert } BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 5b7bb2b57..212d613fe 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -2,13 +2,17 @@ package generate import ( "context" + "fmt" "net" + "os" + "strconv" + "strings" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/specgen" - "github.com/pkg/errors" + "github.com/containers/podman/v4/pkg/specgenutil" "github.com/sirupsen/logrus" ) @@ -55,6 +59,7 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { if err != nil { return nil, err } + spec.Pod = pod.ID() opts = append(opts, rt.WithPod(pod)) spec.CgroupParent = pod.CgroupParent() @@ -141,30 +146,33 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { case specgen.Bridge: p.InfraContainerSpec.NetNS.NSMode = specgen.Bridge logrus.Debugf("Pod using bridge network mode") + case specgen.Private: + p.InfraContainerSpec.NetNS.NSMode = specgen.Private + logrus.Debugf("Pod will use default network mode") case specgen.Host: logrus.Debugf("Pod will use host networking") if len(p.InfraContainerSpec.PortMappings) > 0 || len(p.InfraContainerSpec.Networks) > 0 || p.InfraContainerSpec.NetNS.NSMode == specgen.NoNetwork { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") + return nil, fmt.Errorf("cannot set host network if network-related configuration is specified: %w", define.ErrInvalidArg) } p.InfraContainerSpec.NetNS.NSMode = specgen.Host case specgen.Slirp: logrus.Debugf("Pod will use slirp4netns") - if p.InfraContainerSpec.NetNS.NSMode != "host" { + if p.InfraContainerSpec.NetNS.NSMode != specgen.Host { p.InfraContainerSpec.NetworkOptions = p.NetworkOptions - p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("slirp4netns") + p.InfraContainerSpec.NetNS.NSMode = specgen.Slirp } case specgen.NoNetwork: logrus.Debugf("Pod will not use networking") if len(p.InfraContainerSpec.PortMappings) > 0 || len(p.InfraContainerSpec.Networks) > 0 || - p.InfraContainerSpec.NetNS.NSMode == "host" { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") + p.InfraContainerSpec.NetNS.NSMode == specgen.Host { + return nil, fmt.Errorf("cannot disable pod network if network-related configuration is specified: %w", define.ErrInvalidArg) } p.InfraContainerSpec.NetNS.NSMode = specgen.NoNetwork default: - return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) + return nil, fmt.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) } if len(p.InfraCommand) > 0 { @@ -207,3 +215,88 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { p.InfraContainerSpec.Image = p.InfraImage return p.InfraContainerSpec, nil } + +func PodConfigToSpec(rt *libpod.Runtime, spec *specgen.PodSpecGenerator, infraOptions *entities.ContainerCreateOptions, id string) (p *libpod.Pod, err error) { + pod, err := rt.LookupPod(id) + if err != nil { + return nil, err + } + + infraSpec := &specgen.SpecGenerator{} + if pod.HasInfraContainer() { + infraID, err := pod.InfraContainerID() + if err != nil { + return nil, err + } + _, _, err = ConfigToSpec(rt, infraSpec, infraID) + if err != nil { + return nil, err + } + + infraSpec.Hostname = "" + infraSpec.CgroupParent = "" + infraSpec.Pod = "" // remove old pod... + infraOptions.IsClone = true + infraOptions.IsInfra = true + + n := infraSpec.Name + _, err = rt.LookupContainer(n + "-clone") + if err == nil { // if we found a ctr with this name, set it so the below switch can tell + n += "-clone" + } + + switch { + case strings.Contains(n, "-clone"): + ind := strings.Index(n, "-clone") + 6 + num, err := strconv.Atoi(n[ind:]) + if num == 0 && err != nil { // clone1 is hard to get with this logic, just check for it here. + _, err = rt.LookupContainer(n + "1") + if err != nil { + infraSpec.Name = n + "1" + break + } + } else { + n = n[0:ind] + } + err = nil + count := num + for err == nil { + count++ + tempN := n + strconv.Itoa(count) + _, err = rt.LookupContainer(tempN) + } + n += strconv.Itoa(count) + infraSpec.Name = n + default: + infraSpec.Name = n + "-clone" + } + + err = specgenutil.FillOutSpecGen(infraSpec, infraOptions, []string{}) + if err != nil { + return nil, err + } + + out, err := CompleteSpec(context.Background(), rt, infraSpec) + if err != nil { + return nil, err + } + + // Print warnings + if len(out) > 0 { + for _, w := range out { + fmt.Println("Could not properly complete the spec as expected:") + fmt.Fprintf(os.Stderr, "%s\n", w) + } + } + + spec.InfraContainerSpec = infraSpec + } + + // need to reset hostname, name etc of both pod and infra + spec.Hostname = "" + + if len(spec.InfraContainerSpec.Image) > 0 { + spec.InfraImage = spec.InfraContainerSpec.Image + } + return pod, nil +} diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index bec548d3b..572f256c1 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -10,10 +10,9 @@ import ( "github.com/containers/common/libnetwork/types" "github.com/containers/podman/v4/utils" + "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/specgenutil" - "github.com/containers/podman/v4/pkg/util" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -46,7 +45,7 @@ func joinTwoPortsToRangePortIfPossible(ports *[]types.PortMapping, allHostPorts, // if both host port ranges overlap and the container port range did not match // we have to error because we cannot assign the same host port to more than one container port if previousPort.HostPort+previousPort.Range-1 > port.HostPort { - return nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", port.HostPort, port.Protocol) + return nil, fmt.Errorf("conflicting port mappings for host port %d (protocol %s)", port.HostPort, port.Protocol) } } // we could not join the ports so we append the old one to the list @@ -127,7 +126,7 @@ outer: rangePort = fmt.Sprintf("with range %d ", port.Range) } - return port, errors.Errorf("failed to find an open port to expose container port %d %son the host", port.ContainerPort, rangePort) + return port, fmt.Errorf("failed to find an open port to expose container port %d %son the host", port.ContainerPort, rangePort) } // Parse port maps to port mappings. @@ -163,7 +162,7 @@ func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][ } if port.HostIP != "" { if ip := net.ParseIP(port.HostIP); ip == nil { - return nil, errors.Errorf("invalid IP address %q in port mapping", port.HostIP) + return nil, fmt.Errorf("invalid IP address %q in port mapping", port.HostIP) } } @@ -174,14 +173,14 @@ func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][ } containerPort := port.ContainerPort if containerPort == 0 { - return nil, errors.Errorf("container port number must be non-0") + return nil, fmt.Errorf("container port number must be non-0") } hostPort := port.HostPort if uint32(portRange-1)+uint32(containerPort) > 65535 { - return nil, errors.Errorf("container port range exceeds maximum allowable port number") + return nil, fmt.Errorf("container port range exceeds maximum allowable port number") } if uint32(portRange-1)+uint32(hostPort) > 65535 { - return nil, errors.Errorf("host port range exceeds maximum allowable port number") + return nil, fmt.Errorf("host port range exceeds maximum allowable port number") } hostProtoMap, ok := portMap[port.HostIP] @@ -351,11 +350,11 @@ func createPortMappings(s *specgen.SpecGenerator, imageData *libimage.ImageData) for _, expose := range []map[uint16]string{expose, s.Expose} { for port, proto := range expose { if port == 0 { - return nil, nil, errors.Errorf("cannot expose 0 as it is not a valid port number") + return nil, nil, fmt.Errorf("cannot expose 0 as it is not a valid port number") } protocols, err := checkProtocol(proto, false) if err != nil { - return nil, nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port) + return nil, nil, fmt.Errorf("error validating protocols for exposed port %d: %w", port, err) } toExpose[port] = appendProtocolsNoDuplicates(toExpose[port], protocols) } @@ -387,11 +386,11 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) { protocols[protoUDP] = struct{}{} case protoSCTP: if !allowSCTP { - return nil, errors.Errorf("protocol SCTP is not allowed for exposed ports") + return nil, fmt.Errorf("protocol SCTP is not allowed for exposed ports") } protocols[protoSCTP] = struct{}{} default: - return nil, errors.Errorf("unrecognized protocol %q in port mapping", p) + return nil, fmt.Errorf("unrecognized protocol %q in port mapping", p) } } @@ -402,7 +401,7 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) { // This shouldn't be possible, but check anyways if len(finalProto) == 0 { - return nil, errors.Errorf("no valid protocols specified for port mapping") + return nil, fmt.Errorf("no valid protocols specified for port mapping") } return finalProto, nil @@ -415,7 +414,7 @@ func GenExposedPorts(exposedPorts map[string]struct{}) (map[uint16]string, error } toReturn, err := specgenutil.CreateExpose(expose) if err != nil { - return nil, errors.Wrapf(err, "unable to convert image EXPOSE") + return nil, fmt.Errorf("unable to convert image EXPOSE: %w", err) } return toReturn, nil } diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index ec52164ab..aacefcbac 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -1,19 +1,20 @@ package generate import ( + "fmt" "strings" "github.com/containers/common/libimage" "github.com/containers/common/pkg/apparmor" "github.com/containers/common/pkg/capabilities" "github.com/containers/common/pkg/config" + cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -31,11 +32,11 @@ func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s } else if pidConfig.IsContainer() { ctr, err := runtime.LookupContainer(pidConfig.Value) if err != nil { - return errors.Wrapf(err, "container %q not found", pidConfig.Value) + return fmt.Errorf("container %q not found: %w", pidConfig.Value, err) } secopts, err := label.DupSecOpt(ctr.ProcessLabel()) if err != nil { - return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) + return fmt.Errorf("failed to duplicate label %q : %w", ctr.ProcessLabel(), err) } labelOpts = append(labelOpts, secopts...) } @@ -45,11 +46,11 @@ func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s } else if ipcConfig.IsContainer() { ctr, err := runtime.LookupContainer(ipcConfig.Value) if err != nil { - return errors.Wrapf(err, "container %q not found", ipcConfig.Value) + return fmt.Errorf("container %q not found: %w", ipcConfig.Value, err) } secopts, err := label.DupSecOpt(ctr.ProcessLabel()) if err != nil { - return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) + return fmt.Errorf("failed to duplicate label %q : %w", ctr.ProcessLabel(), err) } labelOpts = append(labelOpts, secopts...) } @@ -62,7 +63,7 @@ func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Gen hasProfile := len(s.ApparmorProfile) > 0 if !apparmor.IsEnabled() { if hasProfile && s.ApparmorProfile != "unconfined" { - return errors.Errorf("Apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile) + return fmt.Errorf("apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile) } return nil } @@ -120,7 +121,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, // capabilities, required to run the container. var capsRequiredRequested []string for key, val := range s.Labels { - if util.StringInSlice(key, capabilities.ContainerImageLabels) { + if cutil.StringInSlice(key, capabilities.ContainerImageLabels) { capsRequiredRequested = strings.Split(val, ",") } } @@ -128,11 +129,11 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, // Pass capRequiredRequested in CapAdd field to normalize capabilities names capsRequired, err := capabilities.MergeCapabilities(nil, capsRequiredRequested, nil) if err != nil { - return errors.Wrapf(err, "capabilities requested by user or image are not valid: %q", strings.Join(capsRequired, ",")) + return fmt.Errorf("capabilities requested by user or image are not valid: %q: %w", strings.Join(capsRequired, ","), err) } // Verify all capRequired are in the capList for _, cap := range capsRequired { - if !util.StringInSlice(cap, caplist) { + if !cutil.StringInSlice(cap, caplist) { privCapsRequired = append(privCapsRequired, cap) } } @@ -160,7 +161,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, } else { mergedCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { - return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ",")) + return fmt.Errorf("capabilities requested by user are not valid: %q: %w", strings.Join(s.CapAdd, ","), err) } boundingSet, err := capabilities.BoundingSet() if err != nil { @@ -244,17 +245,17 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, for sysctlKey, sysctlVal := range s.Sysctl { if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") { - return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since IPC Namespace set to host", sysctlKey, sysctlVal) + return fmt.Errorf("sysctl %s=%s can't be set since IPC Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) } // Ignore net sysctls if --net=host if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") { - return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since Network Namespace set to host", sysctlKey, sysctlVal) + return fmt.Errorf("sysctl %s=%s can't be set since Network Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) } // Ignore uts sysctls if --uts=host if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { - return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since UTS Namespace set to host", sysctlKey, sysctlVal) + return fmt.Errorf("sysctl %s=%s can't be set since UTS Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) } g.AddLinuxSysctl(sysctlKey, sysctlVal) diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index 0a4d03780..867bb4b79 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -2,6 +2,7 @@ package generate import ( "context" + "errors" "fmt" "os" "path" @@ -16,11 +17,10 @@ import ( "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -var errDuplicateDest = errors.Errorf("duplicate mount destination") +var errDuplicateDest = errors.New("duplicate mount destination") // Produce final mounts and named volumes for a container func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *libimage.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) { @@ -63,7 +63,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } cleanDestination := filepath.Clean(m.Destination) if _, ok := unifiedMounts[cleanDestination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", cleanDestination) + return nil, nil, nil, fmt.Errorf("conflict in specified mounts - multiple mounts at %q: %w", cleanDestination, errDuplicateDest) } unifiedMounts[cleanDestination] = m } @@ -84,7 +84,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } cleanDestination := filepath.Clean(v.Dest) if _, ok := unifiedVolumes[cleanDestination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) + return nil, nil, nil, fmt.Errorf("conflict in specified volumes - multiple volumes at %q: %w", cleanDestination, errDuplicateDest) } unifiedVolumes[cleanDestination] = v } @@ -105,7 +105,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru } cleanDestination := filepath.Clean(v.Destination) if _, ok := unifiedOverlays[cleanDestination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) + return nil, nil, nil, fmt.Errorf("conflict in specified volumes - multiple volumes at %q: %w", cleanDestination, errDuplicateDest) } unifiedOverlays[cleanDestination] = v } @@ -131,7 +131,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru return nil, nil, nil, err } if _, ok := unifiedMounts[initMount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination) + return nil, nil, nil, fmt.Errorf("conflict with mount added by --init to %q: %w", initMount.Destination, errDuplicateDest) } unifiedMounts[initMount.Destination] = initMount } @@ -161,12 +161,12 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // Check for conflicts between named volumes and mounts for dest := range baseMounts { if _, ok := baseVolumes[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, errDuplicateDest) } } for dest := range baseVolumes { if _, ok := baseMounts[dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, errDuplicateDest) } } // Final step: maps to arrays @@ -175,7 +175,7 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru if mount.Type == define.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, fmt.Errorf("error getting absolute path of %s: %w", mount.Source, err) } mount.Source = absSrc } @@ -208,7 +208,7 @@ func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGe inspect, err := img.Inspect(ctx, nil) if err != nil { - return nil, nil, errors.Wrapf(err, "error inspecting image to get image volumes") + return nil, nil, fmt.Errorf("error inspecting image to get image volumes: %w", err) } for volume := range inspect.Config.Volumes { logrus.Debugf("Image has volume at %q", volume) @@ -252,16 +252,16 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s switch opt { case "z": if setZ { - return nil, nil, errors.Errorf("cannot set :z more than once in mount options") + return nil, nil, errors.New("cannot set :z more than once in mount options") } setZ = true case "ro", "rw": if setRORW { - return nil, nil, errors.Errorf("cannot set ro or rw options more than once") + return nil, nil, errors.New("cannot set ro or rw options more than once") } setRORW = true default: - return nil, nil, errors.Errorf("invalid option %q specified - volumes from another container can only use z,ro,rw options", opt) + return nil, nil, fmt.Errorf("invalid option %q specified - volumes from another container can only use z,ro,rw options", opt) } } options = splitOpts @@ -269,7 +269,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s ctr, err := runtime.LookupContainer(splitVol[0]) if err != nil { - return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0]) + return nil, nil, fmt.Errorf("error looking up container %q for volumes-from: %w", splitVol[0], err) } logrus.Debugf("Adding volumes from container %s", ctr.ID()) @@ -290,7 +290,7 @@ func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]s // and append them in if we can find them. spec := ctr.Spec() if spec == nil { - return nil, nil, errors.Errorf("retrieving container %s spec for volumes-from", ctr.ID()) + return nil, nil, fmt.Errorf("retrieving container %s spec for volumes-from", ctr.ID()) } for _, mnt := range spec.Mounts { if mnt.Type != define.TypeBind { @@ -364,16 +364,16 @@ func addContainerInitBinary(s *specgen.SpecGenerator, path string) (spec.Mount, } if path == "" { - return mount, fmt.Errorf("please specify a path to the container-init binary") + return mount, errors.New("please specify a path to the container-init binary") } if !s.PidNS.IsPrivate() { - return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)") + return mount, errors.New("cannot add init binary as PID 1 (PID namespace isn't private)") } if s.Systemd == "always" { - return mount, fmt.Errorf("cannot use container-init binary with systemd=always") + return mount, errors.New("cannot use container-init binary with systemd=always") } if _, err := os.Stat(path); os.IsNotExist(err) { - return mount, errors.Wrap(err, "container-init binary not found on the host") + return mount, fmt.Errorf("container-init binary not found on the host: %w", err) } return mount, nil } diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go index 44c7818e7..9c933d747 100644 --- a/pkg/specgen/generate/validate.go +++ b/pkg/specgen/generate/validate.go @@ -1,6 +1,9 @@ package generate import ( + "errors" + "fmt" + "io/ioutil" "os" "path/filepath" @@ -8,7 +11,6 @@ import ( "github.com/containers/common/pkg/sysinfo" "github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/utils" - "github.com/pkg/errors" ) // Verify resource limits are sanely set when running on cgroup v1. @@ -22,7 +24,7 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error } if s.ResourceLimits.Unified != nil { - return nil, errors.New("Cannot use --cgroup-conf without cgroup v2") + return nil, errors.New("cannot use --cgroup-conf without cgroup v2") } // Memory checks @@ -48,7 +50,7 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.") memory.Swappiness = nil } else if *memory.Swappiness > 100 { - return warnings, errors.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness) + return warnings, fmt.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness) } } if memory.Reservation != nil && !sysInfo.MemoryReservation { @@ -103,18 +105,18 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(cpu.Cpus) if err != nil { - return warnings, errors.Errorf("invalid value %s for cpuset cpus", cpu.Cpus) + return warnings, fmt.Errorf("invalid value %s for cpuset cpus", cpu.Cpus) } if !cpusAvailable { - return warnings, errors.Errorf("requested CPUs are not available - requested %s, available: %s", cpu.Cpus, sysInfo.Cpus) + return warnings, fmt.Errorf("requested CPUs are not available - requested %s, available: %s", cpu.Cpus, sysInfo.Cpus) } memsAvailable, err := sysInfo.IsCpusetMemsAvailable(cpu.Mems) if err != nil { - return warnings, errors.Errorf("invalid value %s for cpuset mems", cpu.Mems) + return warnings, fmt.Errorf("invalid value %s for cpuset mems", cpu.Mems) } if !memsAvailable { - return warnings, errors.Errorf("requested memory nodes are not available - requested %s, available: %s", cpu.Mems, sysInfo.Mems) + return warnings, fmt.Errorf("requested memory nodes are not available - requested %s, available: %s", cpu.Mems, sysInfo.Mems) } } @@ -166,6 +168,14 @@ func verifyContainerResourcesCgroupV2(s *specgen.SpecGenerator) ([]string, error if err != nil { return warnings, err } + + if own == "/" { + // If running under the root cgroup try to create or reuse a "probe" cgroup to read memory values + own = "podman_probe" + _ = os.MkdirAll(filepath.Join("/sys/fs/cgroup", own), 0o755) + _ = ioutil.WriteFile("/sys/fs/cgroup/cgroup.subtree_control", []byte("+memory"), 0o644) + } + memoryMax := filepath.Join("/sys/fs/cgroup", own, "memory.max") memorySwapMax := filepath.Join("/sys/fs/cgroup", own, "memory.swap.max") _, errMemoryMax := os.Stat(memoryMax) |