aboutsummaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state.go2
-rw-r--r--libpod/boltdb_state_linux.go1
-rw-r--r--libpod/common/common.go6
-rw-r--r--libpod/container_api.go6
-rw-r--r--libpod/container_config.go27
-rw-r--r--libpod/container_copy_linux.go1
-rw-r--r--libpod/container_exec.go56
-rw-r--r--libpod/container_inspect.go9
-rw-r--r--libpod/container_internal.go27
-rw-r--r--libpod/container_internal_linux.go49
-rw-r--r--libpod/container_linux.go1
-rw-r--r--libpod/container_log.go16
-rw-r--r--libpod/container_log_linux.go3
-rw-r--r--libpod/container_log_unsupported.go5
-rw-r--r--libpod/container_path_resolution.go1
-rw-r--r--libpod/container_stat_linux.go1
-rw-r--r--libpod/container_top_linux.go1
-rw-r--r--libpod/define/container_inspect.go6
-rw-r--r--libpod/define/containerstate.go1
-rw-r--r--libpod/define/version.go2
-rw-r--r--libpod/doc.go11
-rw-r--r--libpod/events/config.go2
-rw-r--r--libpod/events/events.go4
-rw-r--r--libpod/events/events_unsupported.go1
-rw-r--r--libpod/events/journal_linux.go1
-rw-r--r--libpod/events/journal_unsupported.go1
-rw-r--r--libpod/events/logfile.go5
-rw-r--r--libpod/kube.go10
-rw-r--r--libpod/linkmode/linkmode_dynamic.go1
-rw-r--r--libpod/linkmode/linkmode_static.go1
-rw-r--r--libpod/lock/shm/shm_lock.go1
-rw-r--r--libpod/lock/shm/shm_lock_nocgo.go1
-rw-r--r--libpod/lock/shm/shm_lock_test.go1
-rw-r--r--libpod/lock/shm_lock_manager_linux.go1
-rw-r--r--libpod/lock/shm_lock_manager_unsupported.go1
-rw-r--r--libpod/logs/log.go35
-rw-r--r--libpod/mounts_linux.go1
-rw-r--r--libpod/networking_linux.go16
-rw-r--r--libpod/networking_machine.go15
-rw-r--r--libpod/networking_slirp4netns.go151
-rw-r--r--libpod/oci_attach_linux.go3
-rw-r--r--libpod/oci_conmon_exec_linux.go7
-rw-r--r--libpod/oci_conmon_linux.go20
-rw-r--r--libpod/options.go15
-rw-r--r--libpod/pod.go9
-rw-r--r--libpod/pod_api.go4
-rw-r--r--libpod/pod_top_linux.go1
-rw-r--r--libpod/runtime.go4
-rw-r--r--libpod/runtime_ctr.go1
-rw-r--r--libpod/runtime_migrate.go1
-rw-r--r--libpod/runtime_pod_linux.go3
-rw-r--r--libpod/runtime_volume_linux.go1
-rw-r--r--libpod/stats.go23
-rw-r--r--libpod/util_linux.go1
-rw-r--r--libpod/volume.go12
55 files changed, 405 insertions, 181 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 6389431ab..9745121c7 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -366,7 +366,7 @@ func (s *BoltState) GetDBConfig() (*DBConfig, error) {
err = db.View(func(tx *bolt.Tx) error {
configBucket, err := getRuntimeConfigBucket(tx)
if err != nil {
- return nil
+ return err
}
// Some of these may be nil
diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go
index 63ce9784e..8bb10fb63 100644
--- a/libpod/boltdb_state_linux.go
+++ b/libpod/boltdb_state_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/common/common.go b/libpod/common/common.go
index 93a736af2..34cabeadc 100644
--- a/libpod/common/common.go
+++ b/libpod/common/common.go
@@ -1,16 +1,16 @@
package common
-// IsTrue determines whether the given string equals "true"
+// IsTrue determines whether the given string equals "true".
func IsTrue(str string) bool {
return str == "true"
}
-// IsFalse determines whether the given string equals "false"
+// IsFalse determines whether the given string equals "false".
func IsFalse(str string) bool {
return str == "false"
}
-// IsValidBool determines whether the given string equals "true" or "false"
+// IsValidBool determines whether the given string equals "true" or "false".
func IsValidBool(str string) bool {
return IsTrue(str) || IsFalse(str)
}
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 03b3dcc04..0b6139335 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -921,7 +921,11 @@ func (c *Container) Stat(ctx context.Context, containerPath string) (*define.Fil
if err != nil {
return nil, err
}
- defer c.unmount(false)
+ defer func() {
+ if err := c.unmount(false); err != nil {
+ logrus.Errorf("Unmounting container %s: %v", c.ID(), err)
+ }
+ }()
}
info, _, _, err := c.stat(ctx, mountPoint, containerPath)
diff --git a/libpod/container_config.go b/libpod/container_config.go
index e56f1342a..ea644764c 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v4/pkg/namespaces"
+ "github.com/containers/podman/v4/pkg/specgen"
"github.com/containers/storage"
spec "github.com/opencontainers/runtime-spec/specs-go"
)
@@ -165,6 +166,10 @@ type ContainerRootFSConfig struct {
Volatile bool `json:"volatile,omitempty"`
// Passwd allows to user to override podman's passwd/group file setup
Passwd *bool `json:"passwd,omitempty"`
+ // ChrootDirs is an additional set of directories that need to be
+ // treated as root directories. Standard bind mounts will be mounted
+ // into paths relative to these directories.
+ ChrootDirs []string `json:"chroot_directories,omitempty"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
@@ -401,13 +406,19 @@ type ContainerMiscConfig struct {
InitContainerType string `json:"init_container_type,omitempty"`
}
+// InfraInherit contains the compatible options inheritable from the infra container
type InfraInherit struct {
- InfraSecurity ContainerSecurityConfig
- InfraLabels []string `json:"labelopts,omitempty"`
- InfraVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
- InfraOverlay []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"`
- InfraImageVolumes []*ContainerImageVolume `json:"ctrImageVolumes,omitempty"`
- InfraUserVolumes []string `json:"userVolumes,omitempty"`
- InfraResources *spec.LinuxResources `json:"resources,omitempty"`
- InfraDevices []spec.LinuxDevice `json:"device_host_src,omitempty"`
+ ApparmorProfile string `json:"apparmor_profile,omitempty"`
+ CapAdd []string `json:"cap_add,omitempty"`
+ CapDrop []string `json:"cap_drop,omitempty"`
+ HostDeviceList []spec.LinuxDevice `json:"host_device_list,omitempty"`
+ ImageVolumes []*specgen.ImageVolume `json:"image_volumes,omitempty"`
+ InfraResources *spec.LinuxResources `json:"resource_limits,omitempty"`
+ Mounts []spec.Mount `json:"mounts,omitempty"`
+ NoNewPrivileges bool `json:"no_new_privileges,omitempty"`
+ OverlayVolumes []*specgen.OverlayVolume `json:"overlay_volumes,omitempty"`
+ SeccompPolicy string `json:"seccomp_policy,omitempty"`
+ SeccompProfilePath string `json:"seccomp_profile_path,omitempty"`
+ SelinuxOpts []string `json:"selinux_opts,omitempty"`
+ Volumes []*specgen.NamedVolume `json:"volumes,omitempty"`
}
diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go
index d16d635b7..38927d691 100644
--- a/libpod/container_copy_linux.go
+++ b/libpod/container_copy_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index d1c190905..140267f28 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -341,22 +341,60 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
}
lastErr = tmpErr
- exitCode, err := c.readExecExitCode(session.ID())
- if err != nil {
+ exitCode, exitCodeErr := c.readExecExitCode(session.ID())
+
+ // Lock again.
+ // Important: we must lock and sync *before* the above error is handled.
+ // We need info from the database to handle the error.
+ if !c.batched {
+ c.lock.Lock()
+ }
+ // We can't reuse the old exec session (things may have changed from
+ // other use, the container was unlocked).
+ // So re-sync and get a fresh copy.
+ // If we can't do this, no point in continuing, any attempt to save
+ // would write garbage to the DB.
+ if err := c.syncContainer(); err != nil {
+ if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) {
+ // We can't save status, but since the container has
+ // been entirely removed, we don't have to; exit cleanly
+ return lastErr
+ }
if lastErr != nil {
logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr)
}
- lastErr = err
- }
+ return errors.Wrapf(err, "error syncing container %s state to update exec session %s", c.ID(), sessionID)
+ }
+
+ // Now handle the error from readExecExitCode above.
+ if exitCodeErr != nil {
+ newSess, ok := c.state.ExecSessions[sessionID]
+ if !ok {
+ // The exec session was removed entirely, probably by
+ // the cleanup process. When it did so, it should have
+ // written an event with the exit code.
+ // Given that, there's nothing more we can do.
+ logrus.Infof("Container %s exec session %s already removed", c.ID(), session.ID())
+ return lastErr
+ }
- logrus.Debugf("Container %s exec session %s completed with exit code %d", c.ID(), session.ID(), exitCode)
+ if newSess.State == define.ExecStateStopped {
+ // Exec session already cleaned up.
+ // Exit code should be recorded, so it's OK if we were
+ // not able to read it.
+ logrus.Infof("Container %s exec session %s already cleaned up", c.ID(), session.ID())
+ return lastErr
+ }
- // Lock again
- if !c.batched {
- c.lock.Lock()
+ if lastErr != nil {
+ logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr)
+ }
+ lastErr = exitCodeErr
}
- if err := writeExecExitCode(c, session.ID(), exitCode); err != nil {
+ logrus.Debugf("Container %s exec session %s completed with exit code %d", c.ID(), session.ID(), exitCode)
+
+ if err := justWriteExecExitCode(c, session.ID(), exitCode); err != nil {
if lastErr != nil {
logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr)
}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 3df6203e3..c9d0b8a6c 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -103,8 +103,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
}
}
- namedVolumes, mounts := c.sortUserVolumes(ctrSpec)
- inspectMounts, err := c.GetInspectMounts(namedVolumes, c.config.ImageVolumes, mounts)
+ namedVolumes, mounts := c.SortUserVolumes(ctrSpec)
+ inspectMounts, err := c.GetMounts(namedVolumes, c.config.ImageVolumes, mounts)
if err != nil {
return nil, err
}
@@ -222,7 +222,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
// Get inspect-formatted mounts list.
// Only includes user-specified mounts. Only includes bind mounts and named
// volumes, not tmpfs volumes.
-func (c *Container) GetInspectMounts(namedVolumes []*ContainerNamedVolume, imageVolumes []*ContainerImageVolume, mounts []spec.Mount) ([]define.InspectMount, error) {
+func (c *Container) GetMounts(namedVolumes []*ContainerNamedVolume, imageVolumes []*ContainerImageVolume, mounts []spec.Mount) ([]define.InspectMount, error) {
inspectMounts := []define.InspectMount{}
// No mounts, return early
@@ -367,7 +367,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
// Leave empty if not explicitly overwritten by user
if len(c.config.Entrypoint) != 0 {
- ctrConfig.Entrypoint = strings.Join(c.config.Entrypoint, " ")
+ ctrConfig.Entrypoint = c.config.Entrypoint
}
if len(c.config.Labels) != 0 {
@@ -411,6 +411,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
}
ctrConfig.Passwd = c.config.Passwd
+ ctrConfig.ChrootDirs = append(ctrConfig.ChrootDirs, c.config.ChrootDirs...)
return ctrConfig
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 3c21cade8..f1f467879 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -99,15 +99,8 @@ func (c *Container) rootFsSize() (int64, error) {
// rwSize gets the size of the mutable top layer of the container.
func (c *Container) rwSize() (int64, error) {
if c.config.Rootfs != "" {
- var size int64
- err := filepath.Walk(c.config.Rootfs, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- size += info.Size()
- return nil
- })
- return size, err
+ size, err := util.SizeOfPath(c.config.Rootfs)
+ return int64(size), err
}
container, err := c.runtime.store.Container(c.ID())
@@ -1087,13 +1080,6 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
// With the spec complete, do an OCI create
if _, err = c.ociRuntime.CreateContainer(c, nil); err != nil {
- // Fedora 31 is carrying a patch to display improved error
- // messages to better handle the V2 transition. This is NOT
- // upstream in any OCI runtime.
- // TODO: Remove once runc supports cgroupsv2
- if strings.Contains(err.Error(), "this version of runc doesn't work on cgroups v2") {
- logrus.Errorf("Oci runtime %q does not support Cgroups V2: use system migrate to mitigate", c.ociRuntime.Name())
- }
return err
}
@@ -1268,7 +1254,10 @@ func (c *Container) start() error {
}
}
- if c.config.HealthCheckConfig != nil {
+ // Check if healthcheck is not nil and --no-healthcheck option is not set.
+ // If --no-healthcheck is set Test will be always set to `[NONE]` so no need
+ // to update status in such case.
+ if c.config.HealthCheckConfig != nil && !(len(c.config.HealthCheckConfig.Test) == 1 && c.config.HealthCheckConfig.Test[0] == "NONE") {
if err := c.updateHealthStatus(define.HealthCheckStarting); err != nil {
logrus.Error(err)
}
@@ -2246,9 +2235,9 @@ func (c *Container) prepareCheckpointExport() error {
return nil
}
-// sortUserVolumes sorts the volumes specified for a container
+// SortUserVolumes sorts the volumes specified for a container
// between named and normal volumes
-func (c *Container) sortUserVolumes(ctrSpec *spec.Spec) ([]*ContainerNamedVolume, []spec.Mount) {
+func (c *Container) SortUserVolumes(ctrSpec *spec.Spec) ([]*ContainerNamedVolume, []spec.Mount) {
namedUserVolumes := []*ContainerNamedVolume{}
userMounts := []spec.Mount{}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 1517a7df7..11ca169ca 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -968,6 +968,16 @@ func (c *Container) mountNotifySocket(g generate.Generator) error {
// systemd expects to have /run, /run/lock and /tmp on tmpfs
// It also expects to be able to write to /sys/fs/cgroup/systemd and /var/log/journal
func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) error {
+ var containerUUIDSet bool
+ for _, s := range c.config.Spec.Process.Env {
+ if strings.HasPrefix(s, "container_uuid=") {
+ containerUUIDSet = true
+ break
+ }
+ }
+ if !containerUUIDSet {
+ g.AddProcessEnv("container_uuid", c.ID()[:32])
+ }
options := []string{"rw", "rprivate", "nosuid", "nodev"}
for _, dest := range []string{"/run", "/run/lock"} {
if MountExists(mounts, dest) {
@@ -1811,6 +1821,17 @@ func (c *Container) getRootNetNsDepCtr() (depCtr *Container, err error) {
return depCtr, nil
}
+// Ensure standard bind mounts are mounted into all root directories (including chroot directories)
+func (c *Container) mountIntoRootDirs(mountName string, mountPath string) error {
+ c.state.BindMounts[mountName] = mountPath
+
+ for _, chrootDir := range c.config.ChrootDirs {
+ c.state.BindMounts[filepath.Join(chrootDir, mountName)] = mountPath
+ }
+
+ return nil
+}
+
// Make standard bind mounts to include in the container
func (c *Container) makeBindMounts() error {
if err := os.Chown(c.state.RunDir, c.RootUID(), c.RootGID()); err != nil {
@@ -1864,7 +1885,11 @@ func (c *Container) makeBindMounts() error {
// If it doesn't, don't copy them
resolvPath, exists := bindMounts["/etc/resolv.conf"]
if !c.config.UseImageResolvConf && exists {
- c.state.BindMounts["/etc/resolv.conf"] = resolvPath
+ err := c.mountIntoRootDirs("/etc/resolv.conf", resolvPath)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
// check if dependency container has an /etc/hosts file.
@@ -1884,7 +1909,11 @@ func (c *Container) makeBindMounts() error {
depCtr.lock.Unlock()
// finally, save it in the new container
- c.state.BindMounts["/etc/hosts"] = hostsPath
+ err := c.mountIntoRootDirs("/etc/hosts", hostsPath)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
if !hasCurrentUserMapped(c) {
@@ -1901,7 +1930,11 @@ func (c *Container) makeBindMounts() error {
if err != nil {
return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID())
}
- c.state.BindMounts["/etc/resolv.conf"] = newResolv
+ err = c.mountIntoRootDirs("/etc/resolv.conf", newResolv)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
if !c.config.UseImageHosts {
@@ -2329,7 +2362,11 @@ func (c *Container) updateHosts(path string) error {
if err != nil {
return err
}
- c.state.BindMounts["/etc/hosts"] = newHosts
+
+ if err = c.mountIntoRootDirs("/etc/hosts", newHosts); err != nil {
+ return err
+ }
+
return nil
}
@@ -2560,7 +2597,7 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, int, error) {
gid, err := strconv.ParseUint(group, 10, 32)
if err != nil {
- return "", 0, nil
+ return "", 0, nil // nolint: nilerr
}
if addedGID != 0 && addedGID == int(gid) {
@@ -2713,7 +2750,7 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// If a non numeric User, then don't generate passwd
uid, err := strconv.ParseUint(userspec, 10, 32)
if err != nil {
- return "", 0, 0, nil
+ return "", 0, 0, nil // nolint: nilerr
}
if addedUID != 0 && int(uid) == addedUID {
diff --git a/libpod/container_linux.go b/libpod/container_linux.go
index c445fb8af..8b517e69f 100644
--- a/libpod/container_linux.go
+++ b/libpod/container_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/container_log.go b/libpod/container_log.go
index 47877951d..7a9eb2dbf 100644
--- a/libpod/container_log.go
+++ b/libpod/container_log.go
@@ -9,7 +9,7 @@ import (
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/libpod/logs"
- "github.com/hpcloud/tail/watch"
+ "github.com/nxadm/tail/watch"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -23,8 +23,8 @@ func init() {
// Log is a runtime function that can read one or more container logs.
func (r *Runtime) Log(ctx context.Context, containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
- for _, ctr := range containers {
- if err := ctr.ReadLog(ctx, options, logChannel); err != nil {
+ for c, ctr := range containers {
+ if err := ctr.ReadLog(ctx, options, logChannel, int64(c)); err != nil {
return err
}
}
@@ -32,26 +32,26 @@ func (r *Runtime) Log(ctx context.Context, containers []*Container, options *log
}
// ReadLog reads a containers log based on the input options and returns log lines over a channel.
-func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
switch c.LogDriver() {
case define.PassthroughLogging:
return errors.Wrapf(define.ErrNoLogs, "this container is using the 'passthrough' log driver, cannot read logs")
case define.NoLogging:
return errors.Wrapf(define.ErrNoLogs, "this container is using the 'none' log driver, cannot read logs")
case define.JournaldLogging:
- return c.readFromJournal(ctx, options, logChannel)
+ return c.readFromJournal(ctx, options, logChannel, colorID)
case define.JSONLogging:
// TODO provide a separate implementation of this when Conmon
// has support.
fallthrough
case define.KubernetesLogging, "":
- return c.readFromLogFile(ctx, options, logChannel)
+ return c.readFromLogFile(ctx, options, logChannel, colorID)
default:
return errors.Wrapf(define.ErrInternal, "unrecognized log driver %q, cannot read logs", c.LogDriver())
}
}
-func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
t, tailLog, err := logs.GetLogFile(c.LogPath(), options)
if err != nil {
// If the log file does not exist, this is not fatal.
@@ -65,6 +65,7 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption
for _, nll := range tailLog {
nll.CID = c.ID()
nll.CName = c.Name()
+ nll.ColorID = colorID
if nll.Since(options.Since) && nll.Until(options.Until) {
logChannel <- nll
}
@@ -97,6 +98,7 @@ func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOption
}
nll.CID = c.ID()
nll.CName = c.Name()
+ nll.ColorID = colorID
if nll.Since(options.Since) && nll.Until(options.Until) {
logChannel <- nll
}
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 8ae8ff2c0..d96647e51 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -45,7 +45,7 @@ func (c *Container) initializeJournal(ctx context.Context) error {
return journal.Send("", journal.PriInfo, m)
}
-func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine, colorID int64) error {
// We need the container's events in the same journal to guarantee
// consistency, see #10323.
if options.Follow && c.runtime.config.Engine.EventsLogger != "journald" {
@@ -231,6 +231,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
}
logLine, err := logs.NewJournaldLogLine(message, options.Multi)
+ logLine.ColorID = colorID
if err != nil {
logrus.Errorf("Failed parse log line: %v", err)
return
diff --git a/libpod/container_log_unsupported.go b/libpod/container_log_unsupported.go
index f9ca26966..c84a578cc 100644
--- a/libpod/container_log_unsupported.go
+++ b/libpod/container_log_unsupported.go
@@ -1,4 +1,5 @@
-//+build !linux !systemd
+//go:build !linux || !systemd
+// +build !linux !systemd
package libpod
@@ -10,7 +11,7 @@ import (
"github.com/pkg/errors"
)
-func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine) error {
+func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine, colorID int64) error {
return errors.Wrapf(define.ErrOSNotSupported, "Journald logging only enabled with systemd on linux")
}
diff --git a/libpod/container_path_resolution.go b/libpod/container_path_resolution.go
index 7db23b783..80a3749f5 100644
--- a/libpod/container_path_resolution.go
+++ b/libpod/container_path_resolution.go
@@ -1,4 +1,3 @@
-// +linux
package libpod
import (
diff --git a/libpod/container_stat_linux.go b/libpod/container_stat_linux.go
index d90684197..84ab984e0 100644
--- a/libpod/container_stat_linux.go
+++ b/libpod/container_stat_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go
index 41300a708..9b3dbc873 100644
--- a/libpod/container_top_linux.go
+++ b/libpod/container_top_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 804b2b143..444fbff62 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -44,7 +44,7 @@ type InspectContainerConfig struct {
// Container working directory
WorkingDir string `json:"WorkingDir"`
// Container entrypoint
- Entrypoint string `json:"Entrypoint"`
+ Entrypoint []string `json:"Entrypoint"`
// On-build arguments - presently unused. More of Buildah's domain.
OnBuild *string `json:"OnBuild"`
// Container labels
@@ -75,6 +75,10 @@ type InspectContainerConfig struct {
StopTimeout uint `json:"StopTimeout"`
// Passwd determines whether or not podman can add entries to /etc/passwd and /etc/group
Passwd *bool `json:"Passwd,omitempty"`
+ // ChrootDirs is an additional set of directories that need to be
+ // treated as root directories. Standard bind mounts will be mounted
+ // into paths relative to these directories.
+ ChrootDirs []string `json:"ChrootDirs,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
diff --git a/libpod/define/containerstate.go b/libpod/define/containerstate.go
index 23ba1f451..9ad3aec08 100644
--- a/libpod/define/containerstate.go
+++ b/libpod/define/containerstate.go
@@ -138,7 +138,6 @@ type ContainerStats struct {
CPU float64
CPUNano uint64
CPUSystemNano uint64
- DataPoints int64
SystemNano uint64
MemUsage uint64
MemLimit uint64
diff --git a/libpod/define/version.go b/libpod/define/version.go
index 039b0ff27..2c17e6e92 100644
--- a/libpod/define/version.go
+++ b/libpod/define/version.go
@@ -27,6 +27,7 @@ type Version struct {
BuiltTime string
Built int64
OsArch string
+ Os string
}
// GetVersion returns a VersionOutput struct for API and podman
@@ -49,5 +50,6 @@ func GetVersion() (Version, error) {
BuiltTime: time.Unix(buildTime, 0).Format(time.ANSIC),
Built: buildTime,
OsArch: runtime.GOOS + "/" + runtime.GOARCH,
+ Os: runtime.GOOS,
}, nil
}
diff --git a/libpod/doc.go b/libpod/doc.go
new file mode 100644
index 000000000..948153181
--- /dev/null
+++ b/libpod/doc.go
@@ -0,0 +1,11 @@
+// The libpod library is not stable and we do not support use cases outside of
+// this repository. The API can change at any time even with patch releases.
+//
+// If you need a stable interface Podman provides a HTTP API which follows semver,
+// please see https://docs.podman.io/en/latest/markdown/podman-system-service.1.html
+// to start the api service and https://docs.podman.io/en/latest/_static/api.html
+// for the API reference.
+//
+// We also provide stable go bindings to talk to the api service from another go
+// program, see the pkg/bindings directory.
+package libpod
diff --git a/libpod/events/config.go b/libpod/events/config.go
index d88d7b6e3..188d15578 100644
--- a/libpod/events/config.go
+++ b/libpod/events/config.go
@@ -162,6 +162,8 @@ const (
Refresh Status = "refresh"
// Remove ...
Remove Status = "remove"
+ // Rename indicates that a container was renamed
+ Rename Status = "rename"
// Renumber indicates that lock numbers were reallocated at user
// request.
Renumber Status = "renumber"
diff --git a/libpod/events/events.go b/libpod/events/events.go
index 16dd6424e..2cdd2ab67 100644
--- a/libpod/events/events.go
+++ b/libpod/events/events.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/containers/storage/pkg/stringid"
- "github.com/hpcloud/tail"
+ "github.com/nxadm/tail"
"github.com/pkg/errors"
)
@@ -188,6 +188,8 @@ func StringToStatus(name string) (Status, error) {
return Refresh, nil
case Remove.String():
return Remove, nil
+ case Rename.String():
+ return Rename, nil
case Renumber.String():
return Renumber, nil
case Restart.String():
diff --git a/libpod/events/events_unsupported.go b/libpod/events/events_unsupported.go
index 5b32a1b4b..25c175524 100644
--- a/libpod/events/events_unsupported.go
+++ b/libpod/events/events_unsupported.go
@@ -1,3 +1,4 @@
+//go:build !linux
// +build !linux
package events
diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go
index cc63df120..866042a4c 100644
--- a/libpod/events/journal_linux.go
+++ b/libpod/events/journal_linux.go
@@ -1,3 +1,4 @@
+//go:build systemd
// +build systemd
package events
diff --git a/libpod/events/journal_unsupported.go b/libpod/events/journal_unsupported.go
index 004efdab2..6ed39792b 100644
--- a/libpod/events/journal_unsupported.go
+++ b/libpod/events/journal_unsupported.go
@@ -1,3 +1,4 @@
+//go:build !systemd
// +build !systemd
package events
diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go
index be2aaacca..76173cde9 100644
--- a/libpod/events/logfile.go
+++ b/libpod/events/logfile.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage/pkg/lockfile"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// EventLogFile is the structure for event writing to a logfile. It contains the eventer
@@ -59,7 +60,9 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
}
go func() {
time.Sleep(time.Until(untilTime))
- t.Stop()
+ if err := t.Stop(); err != nil {
+ logrus.Errorf("Stopping logger: %v", err)
+ }
}()
}
funcDone := make(chan bool)
diff --git a/libpod/kube.go b/libpod/kube.go
index d68d46415..22fbb5f9f 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -15,6 +15,10 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/env"
+ v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1"
+ "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/api/resource"
+ v12 "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1"
+ "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/util/intstr"
"github.com/containers/podman/v4/pkg/lookup"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/podman/v4/pkg/specgen"
@@ -23,10 +27,6 @@ import (
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- v1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/resource"
- v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/intstr"
)
// GenerateForKube takes a slice of libpod containers and generates
@@ -773,7 +773,7 @@ func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar,
// libpodMountsToKubeVolumeMounts converts the containers mounts to a struct kube understands
func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, map[string]string, error) {
- namedVolumes, mounts := c.sortUserVolumes(c.config.Spec)
+ namedVolumes, mounts := c.SortUserVolumes(c.config.Spec)
vms := make([]v1.VolumeMount, 0, len(mounts))
vos := make([]v1.Volume, 0, len(mounts))
annotations := make(map[string]string)
diff --git a/libpod/linkmode/linkmode_dynamic.go b/libpod/linkmode/linkmode_dynamic.go
index 6d51d60e0..f020fa53e 100644
--- a/libpod/linkmode/linkmode_dynamic.go
+++ b/libpod/linkmode/linkmode_dynamic.go
@@ -1,3 +1,4 @@
+//go:build !static
// +build !static
package linkmode
diff --git a/libpod/linkmode/linkmode_static.go b/libpod/linkmode/linkmode_static.go
index 2db083f4a..b181ad285 100644
--- a/libpod/linkmode/linkmode_static.go
+++ b/libpod/linkmode/linkmode_static.go
@@ -1,3 +1,4 @@
+//go:build static
// +build static
package linkmode
diff --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go
index fea02a619..c7f4d1bc5 100644
--- a/libpod/lock/shm/shm_lock.go
+++ b/libpod/lock/shm/shm_lock.go
@@ -1,3 +1,4 @@
+//go:build linux && cgo
// +build linux,cgo
package shm
diff --git a/libpod/lock/shm/shm_lock_nocgo.go b/libpod/lock/shm/shm_lock_nocgo.go
index 627344d9c..31fc02223 100644
--- a/libpod/lock/shm/shm_lock_nocgo.go
+++ b/libpod/lock/shm/shm_lock_nocgo.go
@@ -1,3 +1,4 @@
+//go:build linux && !cgo
// +build linux,!cgo
package shm
diff --git a/libpod/lock/shm/shm_lock_test.go b/libpod/lock/shm/shm_lock_test.go
index cb83c7c2c..8dfc849d6 100644
--- a/libpod/lock/shm/shm_lock_test.go
+++ b/libpod/lock/shm/shm_lock_test.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package shm
diff --git a/libpod/lock/shm_lock_manager_linux.go b/libpod/lock/shm_lock_manager_linux.go
index 8f3b6df7f..3076cd864 100644
--- a/libpod/lock/shm_lock_manager_linux.go
+++ b/libpod/lock/shm_lock_manager_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package lock
diff --git a/libpod/lock/shm_lock_manager_unsupported.go b/libpod/lock/shm_lock_manager_unsupported.go
index 1d6e3fcbd..d578359ab 100644
--- a/libpod/lock/shm_lock_manager_unsupported.go
+++ b/libpod/lock/shm_lock_manager_unsupported.go
@@ -1,3 +1,4 @@
+//go:build !linux
// +build !linux
package lock
diff --git a/libpod/logs/log.go b/libpod/logs/log.go
index 886911f2d..0eb3bb922 100644
--- a/libpod/logs/log.go
+++ b/libpod/logs/log.go
@@ -9,7 +9,7 @@ import (
"time"
"github.com/containers/podman/v4/libpod/logs/reversereader"
- "github.com/hpcloud/tail"
+ "github.com/nxadm/tail"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -27,6 +27,9 @@ const (
// FullLogType signifies a log line is full
FullLogType = "F"
+
+ //ANSIEscapeResetCode is a code that resets all colors and text effects
+ ANSIEscapeResetCode = "\033[0m"
)
// LogOptions is the options you can use for logs
@@ -37,6 +40,7 @@ type LogOptions struct {
Until time.Time
Tail int64
Timestamps bool
+ Colors bool
Multi bool
WaitGroup *sync.WaitGroup
UseName bool
@@ -50,6 +54,7 @@ type LogLine struct {
Msg string
CID string
CName string
+ ColorID int64
}
// GetLogFile returns an hp tail for a container given options
@@ -162,6 +167,24 @@ func getTailLog(path string, tail int) ([]*LogLine, error) {
return tailLog, nil
}
+//getColor returns a ANSI escape code for color based on the colorID
+func getColor(colorID int64) string {
+ colors := map[int64]string{
+ 0: "\033[37m", // Light Gray
+ 1: "\033[31m", // Red
+ 2: "\033[33m", // Yellow
+ 3: "\033[34m", // Blue
+ 4: "\033[35m", // Magenta
+ 5: "\033[36m", // Cyan
+ 6: "\033[32m", // Green
+ }
+ return colors[colorID%int64(len(colors))]
+}
+
+func (l *LogLine) colorize(prefix string) string {
+ return getColor(l.ColorID) + prefix + l.Msg + ANSIEscapeResetCode
+}
+
// String converts a log line to a string for output given whether a detail
// bool is specified.
func (l *LogLine) String(options *LogOptions) string {
@@ -177,10 +200,18 @@ func (l *LogLine) String(options *LogOptions) string {
out = fmt.Sprintf("%s ", cid)
}
}
+
if options.Timestamps {
out += fmt.Sprintf("%s ", l.Time.Format(LogTimeFormat))
}
- return out + l.Msg
+
+ if options.Colors {
+ out = l.colorize(out)
+ } else {
+ out += l.Msg
+ }
+
+ return out
}
// Since returns a bool as to whether a log line occurred after a given time
diff --git a/libpod/mounts_linux.go b/libpod/mounts_linux.go
index e6aa09eac..f6945b3a3 100644
--- a/libpod/mounts_linux.go
+++ b/libpod/mounts_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 7fd80927b..71e29f18f 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -497,10 +498,13 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
return nil, err
}
- // move to systemd scope to prevent systemd from killing it
- err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid)
- if err != nil {
- logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err)
+ if utils.RunsOnSystemd() {
+ // move to systemd scope to prevent systemd from killing it
+ err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid)
+ if err != nil {
+ // only log this, it is not fatal but can lead to issues when running podman inside systemd units
+ logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err)
+ }
}
// build a new resolv.conf file which uses the slirp4netns dns server address
@@ -1001,7 +1005,7 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
}
}
// do not propagate error inspecting a joined network ns
- logrus.Errorf("Error inspecting network namespace: %s of container %s: %v", networkNSPath, c.ID(), err)
+ logrus.Errorf("Inspecting network namespace: %s of container %s: %v", networkNSPath, c.ID(), err)
}
// We can't do more if the network is down.
@@ -1148,7 +1152,7 @@ func (c *Container) inspectJoinedNetworkNS(networkns string) (q types.StatusBloc
// result
func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) {
config := define.InspectBasicNetworkConfig{}
- interfaceNames := make([]string, len(result.Interfaces))
+ interfaceNames := make([]string, 0, len(result.Interfaces))
for interfaceName := range result.Interfaces {
interfaceNames = append(interfaceNames, interfaceName)
}
diff --git a/libpod/networking_machine.go b/libpod/networking_machine.go
index ca759b893..d2a6b7cfa 100644
--- a/libpod/networking_machine.go
+++ b/libpod/networking_machine.go
@@ -11,6 +11,7 @@ import (
"net/http"
"strconv"
"strings"
+ "time"
"github.com/containers/common/libnetwork/types"
"github.com/sirupsen/logrus"
@@ -36,7 +37,18 @@ func requestMachinePorts(expose bool, ports []types.PortMapping) error {
url = url + "unexpose"
}
ctx := context.Background()
- client := &http.Client{}
+ client := &http.Client{
+ Transport: &http.Transport{
+ // make sure to not set a proxy here so explicitly ignore the proxy
+ // since we want to talk directly to gvproxy
+ // https://github.com/containers/podman/issues/13628
+ Proxy: nil,
+ MaxIdleConns: 50,
+ IdleConnTimeout: 30 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
+ },
+ }
buf := new(bytes.Buffer)
for num, port := range ports {
protocols := strings.Split(port.Protocol, ",")
@@ -78,7 +90,6 @@ func requestMachinePorts(expose bool, ports []types.PortMapping) error {
}
func makeMachineRequest(ctx context.Context, client *http.Client, url string, buf io.Reader) error {
- //var buf io.ReadWriter
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, buf)
if err != nil {
return err
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index 690f0c1fa..3f2842d4c 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -13,6 +14,7 @@ import (
"path/filepath"
"strconv"
"strings"
+ "sync"
"syscall"
"time"
@@ -214,8 +216,7 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
var err error
path, err = exec.LookPath("slirp4netns")
if err != nil {
- logrus.Errorf("Could not find slirp4netns, the network namespace won't be configured: %v", err)
- return nil
+ return fmt.Errorf("could not find slirp4netns, the network namespace can't be configured: %w", err)
}
}
@@ -302,11 +303,15 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
cmd.Stdout = logFile
cmd.Stderr = logFile
- var slirpReadyChan (chan struct{})
-
+ var slirpReadyWg, netnsReadyWg *sync.WaitGroup
if netOptions.enableIPv6 {
- slirpReadyChan = make(chan struct{})
- defer close(slirpReadyChan)
+ // use two wait groups to make sure we set the sysctl before
+ // starting slirp and reset it only after slirp is ready
+ slirpReadyWg = &sync.WaitGroup{}
+ netnsReadyWg = &sync.WaitGroup{}
+ slirpReadyWg.Add(1)
+ netnsReadyWg.Add(1)
+
go func() {
err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error {
// Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds.
@@ -318,23 +323,37 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
// is ready in case users rely on this sysctl.
orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl)
if err != nil {
+ netnsReadyWg.Done()
+ // on ipv6 disabled systems the sysctl does not exists
+ // so we should not error
+ if errors.Is(err, os.ErrNotExist) {
+ return nil
+ }
return err
}
err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644)
+ netnsReadyWg.Done()
if err != nil {
return err
}
- // wait for slirp to finish setup
- <-slirpReadyChan
+
+ // wait until slirp4nets is ready before resetting this value
+ slirpReadyWg.Wait()
return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644)
})
if err != nil {
logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err)
}
}()
+
+ // wait until we set the sysctl
+ netnsReadyWg.Wait()
}
if err := cmd.Start(); err != nil {
+ if netOptions.enableIPv6 {
+ slirpReadyWg.Done()
+ }
return errors.Wrapf(err, "failed to start slirp4netns process")
}
defer func() {
@@ -344,11 +363,12 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
}
}()
- if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
- return err
+ err = waitForSync(syncR, cmd, logFile, 1*time.Second)
+ if netOptions.enableIPv6 {
+ slirpReadyWg.Done()
}
- if slirpReadyChan != nil {
- slirpReadyChan <- struct{}{}
+ if err != nil {
+ return err
}
// Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself
@@ -594,60 +614,73 @@ func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd
// for each port we want to add we need to open a connection to the slirp4netns control socket
// and send the add_hostfwd command.
- for _, i := range ctr.convertPortMappings() {
- conn, err := net.Dial("unix", apiSocket)
- if err != nil {
- return errors.Wrapf(err, "cannot open connection to %s", apiSocket)
- }
- defer func() {
- if err := conn.Close(); err != nil {
- logrus.Errorf("Unable to close connection: %q", err)
+ for _, port := range ctr.convertPortMappings() {
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ hostIP := port.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ for i := uint16(0); i < port.Range; i++ {
+ if err := openSlirp4netnsPort(apiSocket, protocol, hostIP, port.HostPort+i, port.ContainerPort+i); err != nil {
+ return err
+ }
}
- }()
- hostIP := i.HostIP
- if hostIP == "" {
- hostIP = "0.0.0.0"
- }
- apiCmd := slirp4netnsCmd{
- Execute: "add_hostfwd",
- Args: slirp4netnsCmdArg{
- Proto: i.Protocol,
- HostAddr: hostIP,
- HostPort: i.HostPort,
- GuestPort: i.ContainerPort,
- },
- }
- // create the JSON payload and send it. Mark the end of request shutting down writes
- // to the socket, as requested by slirp4netns.
- data, err := json.Marshal(&apiCmd)
- if err != nil {
- return errors.Wrapf(err, "cannot marshal JSON for slirp4netns")
- }
- if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil {
- return errors.Wrapf(err, "cannot write to control socket %s", apiSocket)
- }
- if err := conn.(*net.UnixConn).CloseWrite(); err != nil {
- return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket)
- }
- buf := make([]byte, 2048)
- readLength, err := conn.Read(buf)
- if err != nil {
- return errors.Wrapf(err, "cannot read from control socket %s", apiSocket)
- }
- // if there is no 'error' key in the received JSON data, then the operation was
- // successful.
- var y map[string]interface{}
- if err := json.Unmarshal(buf[0:readLength], &y); err != nil {
- return errors.Wrapf(err, "error parsing error status from slirp4netns")
- }
- if e, found := y["error"]; found {
- return errors.Errorf("error from slirp4netns while setting up port redirection: %v", e)
}
}
logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready")
return nil
}
+// openSlirp4netnsPort sends the slirp4netns pai quey to the given socket
+func openSlirp4netnsPort(apiSocket, proto, hostip string, hostport, guestport uint16) error {
+ conn, err := net.Dial("unix", apiSocket)
+ if err != nil {
+ return errors.Wrapf(err, "cannot open connection to %s", apiSocket)
+ }
+ defer func() {
+ if err := conn.Close(); err != nil {
+ logrus.Errorf("Unable to close slirp4netns connection: %q", err)
+ }
+ }()
+ apiCmd := slirp4netnsCmd{
+ Execute: "add_hostfwd",
+ Args: slirp4netnsCmdArg{
+ Proto: proto,
+ HostAddr: hostip,
+ HostPort: hostport,
+ GuestPort: guestport,
+ },
+ }
+ // create the JSON payload and send it. Mark the end of request shutting down writes
+ // to the socket, as requested by slirp4netns.
+ data, err := json.Marshal(&apiCmd)
+ if err != nil {
+ return errors.Wrapf(err, "cannot marshal JSON for slirp4netns")
+ }
+ if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil {
+ return errors.Wrapf(err, "cannot write to control socket %s", apiSocket)
+ }
+ if err := conn.(*net.UnixConn).CloseWrite(); err != nil {
+ return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket)
+ }
+ buf := make([]byte, 2048)
+ readLength, err := conn.Read(buf)
+ if err != nil {
+ return errors.Wrapf(err, "cannot read from control socket %s", apiSocket)
+ }
+ // if there is no 'error' key in the received JSON data, then the operation was
+ // successful.
+ var y map[string]interface{}
+ if err := json.Unmarshal(buf[0:readLength], &y); err != nil {
+ return errors.Wrapf(err, "error parsing error status from slirp4netns")
+ }
+ if e, found := y["error"]; found {
+ return errors.Errorf("from slirp4netns while setting up port redirection: %v", e)
+ }
+ return nil
+}
+
func getRootlessPortChildIP(c *Container, netStatus map[string]types.StatusBlock) string {
if c.config.NetMode.IsSlirp4netns() {
slirp4netnsIP, err := GetSlirp4netnsIP(c.slirp4netnsSubnet)
diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go
index 1ee664e81..b5eabec1f 100644
--- a/libpod/oci_attach_linux.go
+++ b/libpod/oci_attach_linux.go
@@ -1,4 +1,5 @@
-//+build linux
+//go:build linux
+// +build linux
package libpod
diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go
index c88ef2c67..1005d18da 100644
--- a/libpod/oci_conmon_exec_linux.go
+++ b/libpod/oci_conmon_exec_linux.go
@@ -758,11 +758,14 @@ func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessio
} else {
pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
}
+
+ // Always unset the inheritable capabilities similarly to what the Linux kernel does
+ // They are used only when using capabilities with uid != 0.
+ pspec.Capabilities.Inheritable = []string{}
+
if execUser.Uid == 0 {
pspec.Capabilities.Effective = pspec.Capabilities.Bounding
- pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
- pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
} else {
if user == c.config.User {
pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index cf439cd33..06ba8a03f 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -659,7 +660,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.
}
errChan <- err
}()
- if err := ctr.ReadLog(context.Background(), logOpts, logChan); err != nil {
+ if err := ctr.ReadLog(context.Background(), logOpts, logChan, 0); err != nil {
return err
}
go func() {
@@ -748,7 +749,7 @@ func openControlFile(ctr *Container, parentDir string) (*os.File, error) {
for i := 0; i < 600; i++ {
controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY|unix.O_NONBLOCK, 0)
if err == nil {
- return controlFile, err
+ return controlFile, nil
}
if !isRetryable(err) {
return nil, errors.Wrapf(err, "could not open ctl file for terminal resize for container %s", ctr.ID())
@@ -1013,7 +1014,8 @@ func (r *ConmonOCIRuntime) getLogTag(ctr *Container) (string, error) {
}
data, err := ctr.inspectLocked(false)
if err != nil {
- return "", nil
+ // FIXME: this error should probably be returned
+ return "", nil // nolint: nilerr
}
tmpl, err := template.New("container").Parse(logTag)
if err != nil {
@@ -1198,7 +1200,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
cmd.ExtraFiles = append(cmd.ExtraFiles, childSyncPipe, childStartPipe)
if r.reservePorts && !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
- ports, err := bindPorts(ctr.config.PortMappings)
+ ports, err := bindPorts(ctr.convertPortMappings())
if err != nil {
return 0, err
}
@@ -1369,7 +1371,7 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p
case define.JSONLogging:
fallthrough
//lint:ignore ST1015 the default case has to be here
- default: //nolint-stylecheck
+ default: //nolint:stylecheck
// No case here should happen except JSONLogging, but keep this here in case the options are extended
logrus.Errorf("%s logging specified but not supported. Choosing k8s-file logging instead", ctr.LogDriver())
fallthrough
@@ -1542,17 +1544,19 @@ func readConmonPipeData(runtimeName string, pipe *os.File, ociLog string) (int,
var si *syncInfo
rdr := bufio.NewReader(pipe)
b, err := rdr.ReadBytes('\n')
- if err != nil {
+ // ignore EOF here, error is returned even when data was read
+ // if it is no valid json unmarshal will fail below
+ if err != nil && !errors.Is(err, io.EOF) {
ch <- syncStruct{err: err}
}
if err := json.Unmarshal(b, &si); err != nil {
- ch <- syncStruct{err: err}
+ ch <- syncStruct{err: fmt.Errorf("conmon bytes %q: %w", string(b), err)}
return
}
ch <- syncStruct{si: si}
}()
- data := -1
+ data := -1 //nolint: wastedassign
select {
case ss := <-ch:
if ss.err != nil {
diff --git a/libpod/options.go b/libpod/options.go
index 1ee4e7322..2e5454393 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -2036,3 +2036,18 @@ func WithVolatile() CtrCreateOption {
return nil
}
}
+
+// WithChrootDirs is an additional set of directories that need to be
+// treated as root directories. Standard bind mounts will be mounted
+// into paths relative to these directories.
+func WithChrootDirs(dirs []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+
+ ctr.config.ChrootDirs = dirs
+
+ return nil
+ }
+}
diff --git a/libpod/pod.go b/libpod/pod.go
index 6273ff247..ed2d97b37 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -422,10 +422,6 @@ type PodContainerStats struct {
// GetPodStats returns the stats for each of its containers
func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerStats) (map[string]*define.ContainerStats, error) {
- var (
- ok bool
- prevStat *define.ContainerStats
- )
p.lock.Lock()
defer p.lock.Unlock()
@@ -438,10 +434,7 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerSta
}
newContainerStats := make(map[string]*define.ContainerStats)
for _, c := range containers {
- if prevStat, ok = previousContainerStats[c.ID()]; !ok {
- prevStat = &define.ContainerStats{}
- }
- newStats, err := c.GetContainerStats(prevStat)
+ newStats, err := c.GetContainerStats(previousContainerStats[c.ID()])
// If the container wasn't running, don't include it
// but also suppress the error
if err != nil && errors.Cause(err) != define.ErrCtrStateInvalid {
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index be726d8d1..48049798b 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -602,8 +602,8 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
infraConfig.PidNS = p.PidMode()
infraConfig.UserNS = p.UserNSMode()
- namedVolumes, mounts := infra.sortUserVolumes(infra.config.Spec)
- inspectMounts, err = infra.GetInspectMounts(namedVolumes, infra.config.ImageVolumes, mounts)
+ namedVolumes, mounts := infra.SortUserVolumes(infra.config.Spec)
+ inspectMounts, err = infra.GetMounts(namedVolumes, infra.config.ImageVolumes, mounts)
infraSecurity = infra.GetSecurityOptions()
if err != nil {
return nil, err
diff --git a/libpod/pod_top_linux.go b/libpod/pod_top_linux.go
index 43823a106..83a070807 100644
--- a/libpod/pod_top_linux.go
+++ b/libpod/pod_top_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/runtime.go b/libpod/runtime.go
index d19997709..07653217a 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -210,6 +210,10 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R
}
if err := shutdown.Register("libpod", func(sig os.Signal) error {
+ // For `systemctl stop podman.service` support, exit code should be 0
+ if sig == syscall.SIGTERM {
+ os.Exit(0)
+ }
os.Exit(1)
return nil
}); err != nil && errors.Cause(err) != shutdown.ErrHandlerExists {
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index fc1a688fb..8c3d283a5 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -155,6 +155,7 @@ func (r *Runtime) RenameContainer(ctx context.Context, ctr *Container, newName s
return nil, err
}
+ ctr.newContainerEvent(events.Rename)
return ctr, nil
}
diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go
index 32fdc7b5d..fccd5bdee 100644
--- a/libpod/runtime_migrate.go
+++ b/libpod/runtime_migrate.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 230491c1a..2bbccfdf6 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -6,6 +6,7 @@ package libpod
import (
"context"
"fmt"
+ "os"
"path"
"path/filepath"
"strings"
@@ -239,7 +240,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool,
// Don't try if we failed to retrieve the cgroup
if err == nil {
- if err := conmonCgroup.Update(resLimits); err != nil {
+ if err := conmonCgroup.Update(resLimits); err != nil && !os.IsNotExist(err) {
logrus.Warnf("Error updating pod %s conmon cgroup PID limit: %v", p.ID(), err)
}
}
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index c4fe3db90..3d585fa7a 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/stats.go b/libpod/stats.go
index dbb10a27e..25baa378d 100644
--- a/libpod/stats.go
+++ b/libpod/stats.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -13,7 +14,9 @@ import (
"github.com/pkg/errors"
)
-// GetContainerStats gets the running stats for a given container
+// GetContainerStats gets the running stats for a given container.
+// The previousStats is used to correctly calculate cpu percentages. You
+// should pass nil if there is no previous stat for this container.
func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) {
stats := new(define.ContainerStats)
stats.ContainerID = c.ID()
@@ -35,6 +38,14 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
return stats, define.ErrCtrStateInvalid
}
+ if previousStats == nil {
+ previousStats = &define.ContainerStats{
+ // if we have no prev stats use the container start time as prev time
+ // otherwise we cannot correctly calculate the CPU percentage
+ SystemNano: uint64(c.state.StartedTime.UnixNano()),
+ }
+ }
+
cgroupPath, err := c.cGroupPath()
if err != nil {
return nil, err
@@ -66,8 +77,8 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
stats.Duration = cgroupStats.CPU.Usage.Total
stats.UpTime = time.Duration(stats.Duration)
stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, now, previousStats.SystemNano)
- stats.AvgCPU = calculateAvgCPU(stats.CPU, previousStats.AvgCPU, previousStats.DataPoints)
- stats.DataPoints = previousStats.DataPoints + 1
+ // calc the average cpu usage for the time the container is running
+ stats.AvgCPU = calculateCPUPercent(cgroupStats, 0, now, uint64(c.state.StartedTime.UnixNano()))
stats.MemUsage = cgroupStats.Memory.Usage.Usage
stats.MemLimit = c.getMemLimit()
stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100
@@ -145,9 +156,3 @@ func calculateBlockIO(stats *cgroups.Metrics) (read uint64, write uint64) {
}
return
}
-
-// calculateAvgCPU calculates the avg CPU percentage given the previous average and the number of data points.
-func calculateAvgCPU(statsCPU float64, prevAvg float64, prevData int64) float64 {
- avgPer := ((prevAvg * float64(prevData)) + statsCPU) / (float64(prevData) + 1)
- return avgPer
-}
diff --git a/libpod/util_linux.go b/libpod/util_linux.go
index dd115c7fb..fe98056dc 100644
--- a/libpod/util_linux.go
+++ b/libpod/util_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/volume.go b/libpod/volume.go
index f79ceaa87..bffafdc15 100644
--- a/libpod/volume.go
+++ b/libpod/volume.go
@@ -1,13 +1,12 @@
package libpod
import (
- "os"
- "path/filepath"
"time"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/lock"
"github.com/containers/podman/v4/libpod/plugin"
+ "github.com/containers/podman/v4/pkg/util"
)
// Volume is a libpod named volume.
@@ -93,14 +92,7 @@ func (v *Volume) Name() string {
// Returns the size on disk of volume
func (v *Volume) Size() (uint64, error) {
- var size uint64
- err := filepath.Walk(v.config.MountPoint, func(path string, info os.FileInfo, err error) error {
- if err == nil && !info.IsDir() {
- size += (uint64)(info.Size())
- }
- return err
- })
- return size, err
+ return util.SizeOfPath(v.config.MountPoint)
}
// Driver retrieves the volume's driver.