summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state_internal.go9
-rw-r--r--libpod/container.go13
-rw-r--r--libpod/container_api.go2
-rw-r--r--libpod/container_config.go2
-rw-r--r--libpod/container_inspect.go17
-rw-r--r--libpod/container_internal.go23
-rw-r--r--libpod/container_internal_linux.go100
-rw-r--r--libpod/container_validate.go10
-rw-r--r--libpod/define/errors.go9
-rw-r--r--libpod/define/podstate.go7
-rw-r--r--libpod/events.go6
-rw-r--r--libpod/events/config.go12
-rw-r--r--libpod/events/events.go9
-rw-r--r--libpod/events/filters.go20
-rw-r--r--libpod/events/journal_linux.go71
-rw-r--r--libpod/healthcheck.go2
-rw-r--r--libpod/image/utils.go3
-rw-r--r--libpod/kube.go2
-rw-r--r--libpod/networking_linux.go12
-rw-r--r--libpod/oci_conmon_linux.go2
-rw-r--r--libpod/options.go21
-rw-r--r--libpod/pod_api.go2
-rw-r--r--libpod/pod_status.go8
-rw-r--r--libpod/rootless_cni_linux.go16
-rw-r--r--libpod/runtime_migrate.go2
-rw-r--r--libpod/runtime_pod_infra_linux.go1
-rw-r--r--libpod/runtime_volume.go12
-rw-r--r--libpod/util.go8
28 files changed, 299 insertions, 102 deletions
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index d5c755a52..a48de3092 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -3,6 +3,7 @@ package libpod
import (
"bytes"
"os"
+ "path/filepath"
"runtime"
"strings"
@@ -108,25 +109,25 @@ func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error {
},
{
"libpod root directory (staticdir)",
- rt.config.Engine.StaticDir,
+ filepath.Clean(rt.config.Engine.StaticDir),
staticDirKey,
"",
},
{
"libpod temporary files directory (tmpdir)",
- rt.config.Engine.TmpDir,
+ filepath.Clean(rt.config.Engine.TmpDir),
tmpDirKey,
"",
},
{
"storage temporary directory (runroot)",
- rt.StorageConfig().RunRoot,
+ filepath.Clean(rt.StorageConfig().RunRoot),
runRootKey,
storeOpts.RunRoot,
},
{
"storage graph root directory (graphroot)",
- rt.StorageConfig().GraphRoot,
+ filepath.Clean(rt.StorageConfig().GraphRoot),
graphRootKey,
storeOpts.GraphRoot,
},
diff --git a/libpod/container.go b/libpod/container.go
index 01419500e..ea5a6e09c 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -235,6 +235,19 @@ type ContainerOverlayVolume struct {
Source string `json:"source,omitempty"`
}
+// ContainerImageVolume is a volume based on a container image. The container
+// image is first mounted on the host and is then bind-mounted into the
+// container.
+type ContainerImageVolume struct {
+ // Source is the source of the image volume. The image can be referred
+ // to by name and by ID.
+ Source string `json:"source"`
+ // Dest is the absolute path of the mount in the container.
+ Dest string `json:"dest"`
+ // ReadWrite sets the volume writable.
+ ReadWrite bool `json:"rw"`
+}
+
// Config accessors
// Unlocked
diff --git a/libpod/container_api.go b/libpod/container_api.go
index aef37dd59..a9808a30e 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -249,7 +249,7 @@ func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-
// attaching, and I really do not want to do that right now.
// Send a SIGWINCH after attach succeeds so that most programs will
// redraw the screen for the new attach session.
- attachRdy := make(chan bool)
+ attachRdy := make(chan bool, 1)
if c.config.Spec.Process != nil && c.config.Spec.Process.Terminal {
go func() {
<-attachRdy
diff --git a/libpod/container_config.go b/libpod/container_config.go
index fb1ba373b..d73fbb42f 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -134,6 +134,8 @@ type ContainerRootFSConfig struct {
NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
// OverlayVolumes lists the overlay volumes to mount into the container.
OverlayVolumes []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"`
+ // ImageVolumes lists the image volumes to mount into the container.
+ ImageVolumes []*ContainerImageVolume `json:"imageVolumes,omitempty"`
// CreateWorkingDir indicates that Libpod should create the container's
// working directory if it does not exist. Some OCI runtimes do this by
// default, but others do not.
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index b8bce1272..f78d74ef7 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -90,7 +90,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
}
namedVolumes, mounts := c.sortUserVolumes(ctrSpec)
- inspectMounts, err := c.getInspectMounts(namedVolumes, mounts)
+ inspectMounts, err := c.getInspectMounts(namedVolumes, c.config.ImageVolumes, mounts)
if err != nil {
return nil, err
}
@@ -192,7 +192,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
// 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, mounts []spec.Mount) ([]define.InspectMount, error) {
+func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, imageVolumes []*ContainerImageVolume, mounts []spec.Mount) ([]define.InspectMount, error) {
inspectMounts := []define.InspectMount{}
// No mounts, return early
@@ -219,6 +219,17 @@ func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, mount
inspectMounts = append(inspectMounts, mountStruct)
}
+
+ for _, volume := range imageVolumes {
+ mountStruct := define.InspectMount{}
+ mountStruct.Type = "image"
+ mountStruct.Destination = volume.Dest
+ mountStruct.Source = volume.Source
+ mountStruct.RW = volume.ReadWrite
+
+ inspectMounts = append(inspectMounts, mountStruct)
+ }
+
for _, mount := range mounts {
// It's a mount.
// Is it a tmpfs? If so, discard.
@@ -259,7 +270,7 @@ func parseMountOptionsForInspect(options []string, mount *define.InspectMount) {
isRW = false
case "rw":
// Do nothing, silently discard
- case "shared", "slave", "private", "rshared", "rslave", "rprivate":
+ case "shared", "slave", "private", "rshared", "rslave", "rprivate", "unbindable", "runbindable":
mountProp = opt
case "z", "Z":
zZ = opt
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 4ae571de6..0aeaae43d 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -578,10 +578,10 @@ func (c *Container) refresh() error {
if len(c.config.IDMappings.UIDMap) != 0 || len(c.config.IDMappings.GIDMap) != 0 {
info, err := os.Stat(c.runtime.config.Engine.TmpDir)
if err != nil {
- return errors.Wrapf(err, "cannot stat `%s`", c.runtime.config.Engine.TmpDir)
+ return err
}
if err := os.Chmod(c.runtime.config.Engine.TmpDir, info.Mode()|0111); err != nil {
- return errors.Wrapf(err, "cannot chmod `%s`", c.runtime.config.Engine.TmpDir)
+ return err
}
root := filepath.Join(c.runtime.config.Engine.TmpDir, "containers-root", c.ID())
if err := os.MkdirAll(root, 0755); err != nil {
@@ -1734,6 +1734,25 @@ func (c *Container) cleanup(ctx context.Context) error {
}
}
+ // Unmount image volumes
+ for _, v := range c.config.ImageVolumes {
+ img, err := c.runtime.ImageRuntime().NewFromLocal(v.Source)
+ if err != nil {
+ if lastError == nil {
+ lastError = err
+ continue
+ }
+ logrus.Errorf("error unmounting image volume %q:%q :%v", v.Source, v.Dest, err)
+ }
+ if err := img.Unmount(false); err != nil {
+ if lastError == nil {
+ lastError = err
+ continue
+ }
+ logrus.Errorf("error unmounting image volume %q:%q :%v", v.Source, v.Dest, err)
+ }
+ }
+
return lastError
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index a1b4334fb..83d5c20cb 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -39,6 +39,7 @@ import (
"github.com/containers/storage/pkg/idtools"
securejoin "github.com/cyphar/filepath-securejoin"
runcuser "github.com/opencontainers/runc/libcontainer/user"
+ "github.com/opencontainers/runtime-spec/specs-go"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
@@ -308,7 +309,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
fallthrough
case "Z":
if err := label.Relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil {
- return nil, errors.Wrapf(err, "relabel failed %q", m.Source)
+ return nil, err
}
default:
@@ -343,7 +344,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
Type: "bind",
Source: srcPath,
Destination: dstPath,
- Options: []string{"bind", "private"},
+ Options: []string{"bind", "rprivate"},
}
if c.IsReadOnly() && dstPath != "/dev/shm" {
newMount.Options = append(newMount.Options, "ro", "nosuid", "noexec", "nodev")
@@ -359,11 +360,40 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
for _, overlayVol := range c.config.OverlayVolumes {
contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID())
if err != nil {
- return nil, errors.Wrapf(err, "failed to create TempDir in the %s directory", c.config.StaticDir)
+ return nil, err
}
overlayMount, err := overlay.Mount(contentDir, overlayVol.Source, overlayVol.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
if err != nil {
- return nil, errors.Wrapf(err, "creating overlay failed %q", overlayVol.Source)
+ return nil, errors.Wrapf(err, "mounting overlay failed %q", overlayVol.Source)
+ }
+ g.AddMount(overlayMount)
+ }
+
+ // Add image volumes as overlay mounts
+ for _, volume := range c.config.ImageVolumes {
+ // Mount the specified image.
+ img, err := c.runtime.ImageRuntime().NewFromLocal(volume.Source)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error creating image volume %q:%q", volume.Source, volume.Dest)
+ }
+ mountPoint, err := img.Mount(nil, "")
+ if err != nil {
+ return nil, errors.Wrapf(err, "error mounting image volume %q:%q", volume.Source, volume.Dest)
+ }
+
+ contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID())
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to create TempDir in the %s directory", c.config.StaticDir)
+ }
+
+ var overlayMount specs.Mount
+ if volume.ReadWrite {
+ overlayMount, err = overlay.Mount(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
+ } else {
+ overlayMount, err = overlay.MountReadOnly(contentDir, mountPoint, volume.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions())
+ }
+ if err != nil {
+ return nil, errors.Wrapf(err, "creating overlay mount for image %q failed", volume.Source)
}
g.AddMount(overlayMount)
}
@@ -668,11 +698,31 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro
}
g.AddMount(systemdMnt)
} else {
+ mountOptions := []string{"bind", "rprivate"}
+
+ var statfs unix.Statfs_t
+ if err := unix.Statfs("/sys/fs/cgroup/systemd", &statfs); err != nil {
+ mountOptions = append(mountOptions, "nodev", "noexec", "nosuid")
+ } else {
+ if statfs.Flags&unix.MS_NODEV == unix.MS_NODEV {
+ mountOptions = append(mountOptions, "nodev")
+ }
+ if statfs.Flags&unix.MS_NOEXEC == unix.MS_NOEXEC {
+ mountOptions = append(mountOptions, "noexec")
+ }
+ if statfs.Flags&unix.MS_NOSUID == unix.MS_NOSUID {
+ mountOptions = append(mountOptions, "nosuid")
+ }
+ if statfs.Flags&unix.MS_RDONLY == unix.MS_RDONLY {
+ mountOptions = append(mountOptions, "ro")
+ }
+ }
+
systemdMnt := spec.Mount{
Destination: "/sys/fs/cgroup/systemd",
Type: "bind",
Source: "/sys/fs/cgroup/systemd",
- Options: []string{"bind", "nodev", "noexec", "nosuid", "rprivate"},
+ Options: mountOptions,
}
g.AddMount(systemdMnt)
g.AddLinuxMaskedPaths("/sys/fs/cgroup/systemd/release_agent")
@@ -781,7 +831,7 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error {
return errors.Wrapf(err, "error creating delete files list file %q", deleteFilesList)
}
if err := ioutil.WriteFile(deleteFilesList, formatJSON, 0600); err != nil {
- return errors.Wrapf(err, "error creating delete files list file %q", deleteFilesList)
+ return errors.Wrap(err, "error creating delete files list file")
}
includeFiles = append(includeFiles, "deleted.files")
@@ -805,7 +855,7 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error {
defer outFile.Close()
if err := os.Chmod(dest, 0600); err != nil {
- return errors.Wrapf(err, "cannot chmod %q", dest)
+ return err
}
_, err = io.Copy(outFile, input)
@@ -1029,7 +1079,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if n.Sandbox != "" {
MAC, err = net.ParseMAC(n.Mac)
if err != nil {
- return errors.Wrapf(err, "failed to parse MAC %v", n.Mac)
+ return err
}
break
}
@@ -1133,14 +1183,14 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return errors.Wrapf(err, "failed to read deleted files file")
}
if err := json.Unmarshal(deletedFilesJSON, &deletedFiles); err != nil {
- return errors.Wrapf(err, "failed to read deleted files file %s", deletedFilesPath)
+ return errors.Wrapf(err, "failed to unmarshal deleted files file %s", deletedFilesPath)
}
for _, deleteFile := range deletedFiles {
// Using RemoveAll as deletedFiles, which is generated from 'podman diff'
// lists completely deleted directories as a single entry: 'D /root'.
err = os.RemoveAll(filepath.Join(c.state.Mountpoint, deleteFile))
if err != nil {
- return errors.Wrapf(err, "failed to delete file %s from container %s during restore", deletedFilesPath, c.ID())
+ return errors.Wrapf(err, "failed to delete files from container %s during restore", c.ID())
}
}
}
@@ -1179,7 +1229,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// 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 {
- return errors.Wrapf(err, "cannot chown run directory %s", c.state.RunDir)
+ return errors.Wrap(err, "cannot chown run directory")
}
if c.state.BindMounts == nil {
@@ -1197,13 +1247,13 @@ func (c *Container) makeBindMounts() error {
if c.config.NetNsCtr == "" {
if resolvePath, ok := c.state.BindMounts["/etc/resolv.conf"]; ok {
if err := os.Remove(resolvePath); err != nil && !os.IsNotExist(err) {
- return errors.Wrapf(err, "error removing container %s resolv.conf", c.ID())
+ return errors.Wrapf(err, "container %s", c.ID())
}
delete(c.state.BindMounts, "/etc/resolv.conf")
}
if hostsPath, ok := c.state.BindMounts["/etc/hosts"]; ok {
if err := os.Remove(hostsPath); err != nil && !os.IsNotExist(err) {
- return errors.Wrapf(err, "error removing container %s hosts", c.ID())
+ return errors.Wrapf(err, "container %s", c.ID())
}
delete(c.state.BindMounts, "/etc/hosts")
}
@@ -1403,7 +1453,7 @@ func (c *Container) generateResolvConf() (string, error) {
if err == nil {
resolvConf = definedPath
} else if !os.IsNotExist(err) {
- return "", errors.Wrapf(err, "failed to stat %s", definedPath)
+ return "", err
}
}
break
@@ -1425,7 +1475,7 @@ func (c *Container) generateResolvConf() (string, error) {
contents, err := ioutil.ReadFile(resolvPath)
// resolv.conf doesn't have to exists
if err != nil && !os.IsNotExist(err) {
- return "", errors.Wrapf(err, "unable to read %s", resolvPath)
+ return "", err
}
// Ensure that the container's /etc/resolv.conf is compatible with its
@@ -1494,7 +1544,7 @@ func (c *Container) generateResolvConf() (string, error) {
destPath := filepath.Join(c.state.RunDir, "resolv.conf")
if err := os.Remove(destPath); err != nil && !os.IsNotExist(err) {
- return "", errors.Wrapf(err, "error removing resolv.conf for container %s", c.ID())
+ return "", errors.Wrapf(err, "container %s", c.ID())
}
// Build resolv.conf
@@ -1514,7 +1564,7 @@ func (c *Container) generateResolvConf() (string, error) {
func (c *Container) generateHosts(path string) (string, error) {
orig, err := ioutil.ReadFile(path)
if err != nil {
- return "", errors.Wrapf(err, "unable to read %s", path)
+ return "", err
}
hosts := string(orig)
hosts += c.getHosts()
@@ -1917,7 +1967,7 @@ func (c *Container) generatePasswdAndGroup() (string, string, error) {
}
orig, err := ioutil.ReadFile(originPasswdFile)
if err != nil && !os.IsNotExist(err) {
- return "", "", errors.Wrapf(err, "unable to read passwd file %s", originPasswdFile)
+ return "", "", err
}
passwdFile, err := c.writeStringToStaticDir("passwd", string(orig)+passwdEntry)
if err != nil {
@@ -1936,7 +1986,7 @@ func (c *Container) generatePasswdAndGroup() (string, string, error) {
f, err := os.OpenFile(containerPasswd, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
- return "", "", errors.Wrapf(err, "error opening container %s /etc/passwd", c.ID())
+ return "", "", errors.Wrapf(err, "container %s", c.ID())
}
defer f.Close()
@@ -1963,7 +2013,7 @@ func (c *Container) generatePasswdAndGroup() (string, string, error) {
}
orig, err := ioutil.ReadFile(originGroupFile)
if err != nil && !os.IsNotExist(err) {
- return "", "", errors.Wrapf(err, "unable to read group file %s", originGroupFile)
+ return "", "", err
}
groupFile, err := c.writeStringToStaticDir("group", string(orig)+groupEntry)
if err != nil {
@@ -1982,7 +2032,7 @@ func (c *Container) generatePasswdAndGroup() (string, string, error) {
f, err := os.OpenFile(containerGroup, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
- return "", "", errors.Wrapf(err, "error opening container %s /etc/group", c.ID())
+ return "", "", errors.Wrapf(err, "container %s", c.ID())
}
defer f.Close()
@@ -2003,13 +2053,13 @@ func (c *Container) copyOwnerAndPerms(source, dest string) error {
if os.IsNotExist(err) {
return nil
}
- return errors.Wrapf(err, "cannot stat `%s`", dest)
+ return err
}
if err := os.Chmod(dest, info.Mode()); err != nil {
- return errors.Wrapf(err, "cannot chmod `%s`", dest)
+ return err
}
if err := os.Chown(dest, int(info.Sys().(*syscall.Stat_t).Uid), int(info.Sys().(*syscall.Stat_t).Gid)); err != nil {
- return errors.Wrapf(err, "cannot chown `%s`", dest)
+ return err
}
return nil
}
@@ -2100,7 +2150,7 @@ func (c *Container) checkFileExistsInRootfs(file string) (bool, error) {
if os.IsNotExist(err) {
return false, nil
}
- return false, errors.Wrapf(err, "error accessing container %s file %q", c.ID(), file)
+ return false, errors.Wrapf(err, "container %s", c.ID())
}
if stat.IsDir() {
return false, nil
diff --git a/libpod/container_validate.go b/libpod/container_validate.go
index ee3c8583c..fa809436e 100644
--- a/libpod/container_validate.go
+++ b/libpod/container_validate.go
@@ -88,7 +88,7 @@ func (c *Container) validate() error {
return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if using image's /etc/hosts")
}
- // Check named volume and overlay volumes destination conflits
+ // Check named volume, overlay volume and image volume destination conflits
destinations := make(map[string]bool)
for _, vol := range c.config.NamedVolumes {
// Don't check if they already exist.
@@ -106,6 +106,14 @@ func (c *Container) validate() error {
}
destinations[vol.Dest] = true
}
+ for _, vol := range c.config.ImageVolumes {
+ // Don't check if they already exist.
+ // If they don't we will automatically create them.
+ if _, ok := destinations[vol.Dest]; ok {
+ return errors.Wrapf(define.ErrInvalidArg, "two volumes found with destination %s", vol.Dest)
+ }
+ destinations[vol.Dest] = true
+ }
// Check that networks and network aliases match up.
ctrNets := make(map[string]bool)
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 36a919cf6..27c5febf4 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -14,6 +14,9 @@ var (
// ErrNoSuchImage indicates the requested image does not exist
ErrNoSuchImage = errors.New("no such image")
+ // ErrMultipleImages found multiple name and tag matches
+ ErrMultipleImages = errors.New("found multiple name and tag matches")
+
// ErrNoSuchTag indicates the requested image tag does not exist
ErrNoSuchTag = errors.New("no such tag")
@@ -148,15 +151,15 @@ var (
// ErrOCIRuntimePermissionDenied indicates the OCI runtime attempted to invoke a command that returned
// a permission denied error
- ErrOCIRuntimePermissionDenied = errors.New("OCI runtime permission denied error")
+ ErrOCIRuntimePermissionDenied = errors.New("OCI permission denied")
// ErrOCIRuntimeNotFound indicates the OCI runtime attempted to invoke a command
// that was not found
- ErrOCIRuntimeNotFound = errors.New("OCI runtime command not found error")
+ ErrOCIRuntimeNotFound = errors.New("OCI not found")
// ErrOCIRuntimeUnavailable indicates that the OCI runtime associated to a container
// could not be found in the configuration
- ErrOCIRuntimeUnavailable = errors.New("OCI runtime not available in the current configuration")
+ ErrOCIRuntimeUnavailable = errors.New("OCI unavailable")
// ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH)
// is out of date for the current podman version
diff --git a/libpod/define/podstate.go b/libpod/define/podstate.go
index 2b59aabfb..e02671972 100644
--- a/libpod/define/podstate.go
+++ b/libpod/define/podstate.go
@@ -10,9 +10,12 @@ const (
PodStateExited = "Exited"
// PodStatePaused indicates the pod has been paused
PodStatePaused = "Paused"
- // PodStateRunning indicates that one or more of the containers in
- // the pod is running
+ // PodStateRunning indicates that all of the containers in the pod are
+ // running.
PodStateRunning = "Running"
+ // PodStateDegraded indicates that at least one, but not all, of the
+ // containers in the pod are running.
+ PodStateDegraded = "Degraded"
// PodStateStopped indicates all of the containers belonging to the pod
// are stopped.
PodStateStopped = "Stopped"
diff --git a/libpod/events.go b/libpod/events.go
index b519fe324..95317eb01 100644
--- a/libpod/events.go
+++ b/libpod/events.go
@@ -26,6 +26,12 @@ func (c *Container) newContainerEvent(status events.Status) {
e.Name = c.Name()
e.Image = c.config.RootfsImageName
e.Type = events.Container
+
+ e.Details = events.Details{
+ ID: e.ID,
+ Attributes: c.Labels(),
+ }
+
if err := c.runtime.eventer.Write(e); err != nil {
logrus.Errorf("unable to write pod event: %q", err)
}
diff --git a/libpod/events/config.go b/libpod/events/config.go
index bb35c03c0..2ec3111fe 100644
--- a/libpod/events/config.go
+++ b/libpod/events/config.go
@@ -36,6 +36,18 @@ type Event struct {
Time time.Time
// Type of event that occurred
Type Type
+
+ Details
+}
+
+// Details describes specifics about certain events, specifically around
+// container events
+type Details struct {
+ // ID is the event ID
+ ID string
+ // Attributes can be used to describe specifics about the event
+ // in the case of a container event, labels for example
+ Attributes map[string]string
}
// EventerOptions describe options that need to be passed to create
diff --git a/libpod/events/events.go b/libpod/events/events.go
index 722c9595e..42939d64c 100644
--- a/libpod/events/events.go
+++ b/libpod/events/events.go
@@ -69,7 +69,14 @@ func (e *Event) ToHumanReadable() string {
var humanFormat string
switch e.Type {
case Container, Pod:
- humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s)", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name)
+ humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name)
+ // check if the container has labels and add it to the output
+ if len(e.Attributes) > 0 {
+ for k, v := range e.Attributes {
+ humanFormat += fmt.Sprintf(", %s=%s", k, v)
+ }
+ }
+ humanFormat += ")"
case Image:
humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, e.ID, e.Name)
case System:
diff --git a/libpod/events/filters.go b/libpod/events/filters.go
index 171e2237f..62891d32c 100644
--- a/libpod/events/filters.go
+++ b/libpod/events/filters.go
@@ -55,6 +55,24 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error
return func(e *Event) bool {
return string(e.Type) == filterValue
}, nil
+
+ case "LABEL":
+ return func(e *Event) bool {
+ var found bool
+ // iterate labels and see if we match a key and value
+ for eventKey, eventValue := range e.Attributes {
+ filterValueSplit := strings.SplitN(filterValue, "=", 2)
+ // if the filter isn't right, just return false
+ if len(filterValueSplit) < 2 {
+ return false
+ }
+ if eventKey == filterValueSplit[0] && eventValue == filterValueSplit[1] {
+ found = true
+ break
+ }
+ }
+ return found
+ }, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)
}
@@ -74,7 +92,7 @@ func generateEventUntilOption(timeUntil time.Time) func(e *Event) bool {
func parseFilter(filter string) (string, string, error) {
filterSplit := strings.SplitN(filter, "=", 2)
- if len(filterSplit) == 1 {
+ if len(filterSplit) != 2 {
return "", "", errors.Errorf("%s is an invalid filter", filter)
}
return filterSplit[0], filterSplit[1], nil
diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go
index dc55dbc77..5e3be8009 100644
--- a/libpod/events/journal_linux.go
+++ b/libpod/events/journal_linux.go
@@ -4,6 +4,7 @@ package events
import (
"context"
+ "encoding/json"
"strconv"
"time"
@@ -46,6 +47,15 @@ func (e EventJournalD) Write(ee Event) error {
if ee.ContainerExitCode != 0 {
m["PODMAN_EXIT_CODE"] = strconv.Itoa(ee.ContainerExitCode)
}
+ // If we have container labels, we need to convert them to a string so they
+ // can be recorded with the event
+ if len(ee.Details.Attributes) > 0 {
+ b, err := json.Marshal(ee.Details.Attributes)
+ if err != nil {
+ return err
+ }
+ m["PODMAN_LABELS"] = string(b)
+ }
case Volume:
m["PODMAN_NAME"] = ee.Name
}
@@ -59,35 +69,39 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
if err != nil {
return errors.Wrapf(err, "failed to generate event options")
}
- j, err := sdjournal.NewJournal() //nolint
+ j, err := sdjournal.NewJournal()
if err != nil {
return err
}
- // TODO AddMatch and Seek seem to conflict
- // Issue filed upstream -> https://github.com/coreos/go-systemd/issues/315
- // Leaving commented code in case upstream fixes things
- //podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} //nolint
- //if err := j.AddMatch(podmanJournal.String()); err != nil {
- // return errors.Wrap(err, "failed to add filter for event log")
- //}
+
+ // match only podman journal entries
+ podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"}
+ if err := j.AddMatch(podmanJournal.String()); err != nil {
+ return errors.Wrap(err, "failed to add journal filter for event log")
+ }
+
if len(options.Since) == 0 && len(options.Until) == 0 && options.Stream {
if err := j.SeekTail(); err != nil {
return errors.Wrap(err, "failed to seek end of journal")
}
- } else {
- podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} //nolint
- if err := j.AddMatch(podmanJournal.String()); err != nil {
- return errors.Wrap(err, "failed to add filter for event log")
+ // After SeekTail calling Next moves to a random entry.
+ // To prevent this we have to call Previous first.
+ // see: https://bugs.freedesktop.org/show_bug.cgi?id=64614
+ if _, err := j.Previous(); err != nil {
+ return errors.Wrap(err, "failed to move journal cursor to previous entry")
}
}
+
// the api requires a next|prev before getting a cursor
if _, err := j.Next(); err != nil {
- return err
+ return errors.Wrap(err, "failed to move journal cursor to next entry")
}
+
prevCursor, err := j.GetCursor()
if err != nil {
- return err
+ return errors.Wrap(err, "failed to get journal cursor")
}
+
for {
select {
case <-ctx.Done():
@@ -96,30 +110,26 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
default:
// fallthrough
}
+
if _, err := j.Next(); err != nil {
- return err
+ return errors.Wrap(err, "failed to move journal cursor to next entry")
}
newCursor, err := j.GetCursor()
if err != nil {
- return err
+ return errors.Wrap(err, "failed to get journal cursor")
}
if prevCursor == newCursor {
if len(options.Until) > 0 || !options.Stream {
break
}
- _ = j.Wait(sdjournal.IndefiniteWait) //nolint
+ _ = j.Wait(sdjournal.IndefiniteWait)
continue
}
prevCursor = newCursor
+
entry, err := j.GetEntry()
if err != nil {
- return err
- }
- // TODO this keeps us from feeding the podman event parser with
- // with regular journal content; it can be removed if the above
- // problem with AddMatch is resolved.
- if entry.Fields["PODMAN_EVENT"] == "" {
- continue
+ return errors.Wrap(err, "failed to read journal entry")
}
newEvent, err := newEventFromJournalEntry(entry)
if err != nil {
@@ -174,6 +184,19 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { /
newEvent.ContainerExitCode = intCode
}
}
+
+ // we need to check for the presence of labels recorded to a container event
+ if stringLabels, ok := entry.Fields["PODMAN_LABELS"]; ok && len(stringLabels) > 0 {
+ labels := make(map[string]string, 0)
+ if err := json.Unmarshal([]byte(stringLabels), &labels); err != nil {
+ return nil, err
+ }
+
+ // if we have labels, add them to the event
+ if len(labels) > 0 {
+ newEvent.Details = Details{Attributes: labels}
+ }
+ }
case Image:
newEvent.ID = entry.Fields["PODMAN_ID"]
}
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index bd55b852e..f77075893 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -223,7 +223,7 @@ func (c *Container) GetHealthCheckLog() (define.HealthCheckResults, error) {
}
b, err := ioutil.ReadFile(c.healthCheckLogPath())
if err != nil {
- return healthCheck, errors.Wrapf(err, "failed to read health check log file %s", c.healthCheckLogPath())
+ return healthCheck, errors.Wrap(err, "failed to read health check log file")
}
if err := json.Unmarshal(b, &healthCheck); err != nil {
return healthCheck, errors.Wrapf(err, "failed to unmarshal existing healthcheck results in %s", c.healthCheckLogPath())
diff --git a/libpod/image/utils.go b/libpod/image/utils.go
index 2538f429b..7429a7f10 100644
--- a/libpod/image/utils.go
+++ b/libpod/image/utils.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/types"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/storage"
"github.com/pkg/errors"
)
@@ -42,7 +43,7 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er
if len(results) == 0 {
return &storage.Image{}, errors.Errorf("unable to find a name and tag match for %s in repotags", searchName)
} else if len(results) > 1 {
- return &storage.Image{}, errors.Errorf("found multiple name and tag matches for %s in repotags", searchName)
+ return &storage.Image{}, errors.Wrapf(define.ErrMultipleImages, searchName)
}
return results[0], nil
}
diff --git a/libpod/kube.go b/libpod/kube.go
index cd5064c84..067e7827d 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -327,7 +327,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) {
period := *c.config.Spec.Linux.Resources.CPU.Period
if quota > 0 && period > 0 {
- cpuLimitMilli := int64(1000 * float64(quota) / float64(period))
+ cpuLimitMilli := int64(1000 * util.PeriodAndQuotaToCores(period, quota))
// Kubernetes: precision finer than 1m is not allowed
if cpuLimitMilli >= 1 {
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 9ff6e40b7..28dca8dd8 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -102,17 +102,7 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
requestedMAC = ctr.config.StaticMAC
}
- // If we are in a pod use the pod name for the network, otherwise the container name
- var podName string
- if ctr.PodID() != "" {
- pod, err := r.GetPod(ctr.PodID())
- if err == nil {
- podName = pod.Name()
- }
- }
- if podName == "" {
- podName = ctr.Name()
- }
+ podName := getCNIPodName(ctr)
podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 94630e57b..89d64537d 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -120,7 +120,7 @@ func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtime
if os.IsNotExist(err) {
continue
}
- return nil, errors.Wrapf(err, "cannot stat OCI runtime %s path %q", name, path)
+ return nil, errors.Wrapf(err, "cannot stat OCI runtime %s path", name)
}
if !stat.Mode().IsRegular() {
continue
diff --git a/libpod/options.go b/libpod/options.go
index da2fc983a..0f55f34a3 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1296,7 +1296,7 @@ func WithRootFS(rootfs string) CtrCreateOption {
return define.ErrCtrFinalized
}
if _, err := os.Stat(rootfs); err != nil {
- return errors.Wrapf(err, "error checking path %q", rootfs)
+ return err
}
ctr.config.Rootfs = rootfs
return nil
@@ -1439,6 +1439,25 @@ func WithOverlayVolumes(volumes []*ContainerOverlayVolume) CtrCreateOption {
}
}
+// WithImageVolumes adds the given image volumes to the container.
+func WithImageVolumes(volumes []*ContainerImageVolume) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+
+ for _, vol := range volumes {
+ ctr.config.ImageVolumes = append(ctr.config.ImageVolumes, &ContainerImageVolume{
+ Dest: vol.Dest,
+ Source: vol.Source,
+ ReadWrite: vol.ReadWrite,
+ })
+ }
+
+ return nil
+ }
+}
+
// WithHealthCheck adds the healthcheck to the container config
func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption {
return func(ctr *Container) error {
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index f2ddba9c9..87ac5c07a 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -506,7 +506,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
})
ctrStatuses[c.ID()] = c.state.State
}
- podState, err := CreatePodStatusResults(ctrStatuses)
+ podState, err := createPodStatusResults(ctrStatuses)
if err != nil {
return nil, err
}
diff --git a/libpod/pod_status.go b/libpod/pod_status.go
index f4ccf308a..668d45ec7 100644
--- a/libpod/pod_status.go
+++ b/libpod/pod_status.go
@@ -10,10 +10,10 @@ func (p *Pod) GetPodStatus() (string, error) {
if err != nil {
return define.PodStateErrored, err
}
- return CreatePodStatusResults(ctrStatuses)
+ return createPodStatusResults(ctrStatuses)
}
-func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) {
+func createPodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) {
ctrNum := len(ctrStatuses)
if ctrNum == 0 {
return define.PodStateCreated, nil
@@ -43,8 +43,10 @@ func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (stri
}
switch {
- case statuses[define.PodStateRunning] > 0:
+ case statuses[define.PodStateRunning] == ctrNum:
return define.PodStateRunning, nil
+ case statuses[define.PodStateRunning] > 0:
+ return define.PodStateDegraded, nil
case statuses[define.PodStatePaused] == ctrNum:
return define.PodStatePaused, nil
case statuses[define.PodStateStopped] == ctrNum:
diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go
index 21e43ebd0..3d4ff6e86 100644
--- a/libpod/rootless_cni_linux.go
+++ b/libpod/rootless_cni_linux.go
@@ -53,7 +53,7 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.
if err != nil {
return nil, nil, err
}
- k8sPodName := getPodOrContainerName(c) // passed to CNI as K8S_POD_NAME
+ k8sPodName := getCNIPodName(c) // passed to CNI as K8S_POD_NAME
cniResults := make([]*cnitypes.Result, len(c.config.Networks))
for i, nw := range c.config.Networks {
cniRes, err := rootlessCNIInfraCallAlloc(infra, c.ID(), nw, k8sPodName)
@@ -115,12 +115,16 @@ func getRootlessCNIInfraLock(r *Runtime) (lockfile.Locker, error) {
return lockfile.GetLockfile(fname)
}
-func getPodOrContainerName(c *Container) string {
- pod, err := c.runtime.GetPod(c.PodID())
- if err != nil || pod.config.Name == "" {
- return c.Name()
+// getCNIPodName return the pod name (hostname) used by CNI and the dnsname plugin.
+// If we are in the pod network namespace use the pod name otherwise the container name
+func getCNIPodName(c *Container) string {
+ if c.config.NetMode.IsPod() || c.IsInfra() {
+ pod, err := c.runtime.GetPod(c.PodID())
+ if err == nil {
+ return pod.Name()
+ }
}
- return pod.config.Name
+ return c.Name()
}
func rootlessCNIInfraCallAlloc(infra *Container, id, nw, k8sPodName string) (*cnitypes.Result, error) {
diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go
index 3dc38f442..1ad32fe9c 100644
--- a/libpod/runtime_migrate.go
+++ b/libpod/runtime_migrate.go
@@ -29,7 +29,7 @@ func stopPauseProcess() error {
if os.IsNotExist(err) {
return nil
}
- return errors.Wrapf(err, "cannot read pause process pid file %s", pausePidPath)
+ return errors.Wrap(err, "cannot read pause process pid file")
}
pausePid, err := strconv.Atoi(string(data))
if err != nil {
diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go
index 7f58e86d8..76419587a 100644
--- a/libpod/runtime_pod_infra_linux.go
+++ b/libpod/runtime_pod_infra_linux.go
@@ -131,6 +131,7 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm
logrus.Debugf("Using %q as infra container entrypoint", entryCmd)
+ g.RemoveMount("/dev/shm")
if isRootless {
g.RemoveMount("/dev/pts")
devPts := spec.Mount{
diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go
index e4e6d87e6..055a243c0 100644
--- a/libpod/runtime_volume.go
+++ b/libpod/runtime_volume.go
@@ -86,8 +86,8 @@ func (r *Runtime) HasVolume(name string) (bool, error) {
// Volumes retrieves all volumes
// Filters can be provided which will determine which volumes are included in the
-// output. Multiple filters are handled by ANDing their output, so only volumes
-// matching all filters are returned
+// output. If multiple filters are used, a volume will be returned if
+// any of the filters are matched
func (r *Runtime) Volumes(filters ...VolumeFilter) ([]*Volume, error) {
r.lock.RLock()
defer r.lock.RUnlock()
@@ -101,11 +101,15 @@ func (r *Runtime) Volumes(filters ...VolumeFilter) ([]*Volume, error) {
return nil, err
}
+ if len(filters) == 0 {
+ return vols, nil
+ }
+
volsFiltered := make([]*Volume, 0, len(vols))
for _, vol := range vols {
- include := true
+ include := false
for _, filter := range filters {
- include = include && filter(vol)
+ include = include || filter(vol)
}
if include {
diff --git a/libpod/util.go b/libpod/util.go
index 585b07aca..c26039c50 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -74,7 +74,7 @@ func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, e
return false, nil
}
if !os.IsNotExist(err) {
- return false, errors.Wrapf(err, "checking file %s", path)
+ return false, err
}
case <-time.After(25 * time.Millisecond):
// Check periodically for the file existence. It is needed
@@ -86,7 +86,7 @@ func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, e
return false, nil
}
if !os.IsNotExist(err) {
- return false, errors.Wrapf(err, "checking file %s", path)
+ return false, err
}
case <-timeoutChan:
return false, errors.Wrapf(define.ErrInternal, "timed out waiting for file %s", path)
@@ -184,11 +184,11 @@ func DefaultSeccompPath() (string, error) {
return config.SeccompOverridePath, nil
}
if !os.IsNotExist(err) {
- return "", errors.Wrapf(err, "can't check if %q exists", config.SeccompOverridePath)
+ return "", err
}
if _, err := os.Stat(config.SeccompDefaultPath); err != nil {
if !os.IsNotExist(err) {
- return "", errors.Wrapf(err, "can't check if %q exists", config.SeccompDefaultPath)
+ return "", err
}
return "", nil
}