aboutsummaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container.go14
-rw-r--r--libpod/container_api.go122
-rw-r--r--libpod/container_exec.go4
-rw-r--r--libpod/container_internal.go56
-rw-r--r--libpod/container_internal_linux.go14
-rw-r--r--libpod/container_internal_unsupported.go5
-rw-r--r--libpod/container_log.go (renamed from libpod/container.log.go)3
-rw-r--r--libpod/container_top_linux.go2
-rw-r--r--libpod/define/config.go40
-rw-r--r--libpod/define/ctr_inspect.go (renamed from libpod/define/inspect.go)0
-rw-r--r--libpod/define/info.go102
-rw-r--r--libpod/define/pod_inspect.go97
-rw-r--r--libpod/filters/containers.go157
-rw-r--r--libpod/filters/pods.go (renamed from libpod/podfilters/pods.go)2
-rw-r--r--libpod/healthcheck.go2
-rw-r--r--libpod/info.go248
-rw-r--r--libpod/oci.go8
-rw-r--r--libpod/oci_attach_linux.go8
-rw-r--r--libpod/oci_attach_unsupported.go4
-rw-r--r--libpod/oci_conmon_linux.go116
-rw-r--r--libpod/oci_conmon_unsupported.go6
-rw-r--r--libpod/oci_missing.go21
-rw-r--r--libpod/options.go4
-rw-r--r--libpod/pod_api.go49
-rw-r--r--libpod/runtime.go63
-rw-r--r--libpod/runtime_ctr.go5
-rw-r--r--libpod/storage.go5
-rw-r--r--libpod/util.go31
28 files changed, 895 insertions, 293 deletions
diff --git a/libpod/container.go b/libpod/container.go
index c1deb95f9..5cd719ab6 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -34,15 +34,6 @@ const SystemdDefaultCgroupParent = "machine.slice"
// manager in libpod when running as rootless
const SystemdDefaultRootlessCgroupParent = "user.slice"
-// JournaldLogging is the string conmon expects to specify journald logging
-const JournaldLogging = "journald"
-
-// KubernetesLogging is the string conmon expects when specifying to use the kubernetes logging format
-const KubernetesLogging = "k8s-file"
-
-// JSONLogging is the string conmon expects when specifying to use the json logging format
-const JSONLogging = "json-file"
-
// DefaultWaitInterval is the default interval between container status checks
// while waiting.
const DefaultWaitInterval = 250 * time.Millisecond
@@ -564,6 +555,11 @@ func (c *Container) MountLabel() string {
return c.config.MountLabel
}
+// Systemd returns whether the container will be running in systemd mode
+func (c *Container) Systemd() bool {
+ return c.config.Systemd
+}
+
// User returns the user who the container is run as
func (c *Container) User() string {
return c.config.User
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 967180437..b31079b26 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -3,14 +3,16 @@ package libpod
import (
"bufio"
"context"
- "io"
"io/ioutil"
"net"
"os"
+ "strings"
+ "sync"
"time"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
+ "github.com/containers/libpod/libpod/logs"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -96,7 +98,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
// The channel will be closed automatically after the result of attach has been
// sent.
// If recursive is set, StartAndAttach will also start all containers this container depends on.
-func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
+func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -213,29 +215,10 @@ func (c *Container) Kill(signal uint) error {
return c.save()
}
-// AttachStreams contains streams that will be attached to the container
-type AttachStreams struct {
- // OutputStream will be attached to container's STDOUT
- OutputStream io.WriteCloser
- // ErrorStream will be attached to container's STDERR
- ErrorStream io.WriteCloser
- // InputStream will be attached to container's STDIN
- InputStream *bufio.Reader
- // AttachOutput is whether to attach to STDOUT
- // If false, stdout will not be attached
- AttachOutput bool
- // AttachError is whether to attach to STDERR
- // If false, stdout will not be attached
- AttachError bool
- // AttachInput is whether to attach to STDIN
- // If false, stdout will not be attached
- AttachInput bool
-}
-
// Attach attaches to a container.
// This function returns when the attach finishes. It does not hold the lock for
// the duration of its runtime, only using it at the beginning to verify state.
-func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error {
+func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize) error {
if !c.batched {
c.lock.Lock()
if err := c.syncContainer(); err != nil {
@@ -264,15 +247,28 @@ func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan re
// forwarded to the client.
// This function returns when the attach finishes. It does not hold the lock for
// the duration of its runtime, only using it at the beginning to verify state.
-func (c *Container) HTTPAttach(httpCon net.Conn, httpBuf *bufio.ReadWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool) error {
+// The streamLogs parameter indicates that all the container's logs until present
+// will be streamed at the beginning of the attach.
+// The streamAttach parameter indicates that the attach itself will be streamed
+// over the socket; if this is not set, but streamLogs is, only the logs will be
+// sent.
+// At least one of streamAttach and streamLogs must be set.
+func (c *Container) HTTPAttach(httpCon net.Conn, httpBuf *bufio.ReadWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, streamAttach, streamLogs bool) (deferredErr error) {
+ isTerminal := false
+ if c.config.Spec.Process != nil {
+ isTerminal = c.config.Spec.Process.Terminal
+ }
+ // Ensure our contract of writing errors to and closing the HTTP conn is
+ // honored.
+ defer func() {
+ hijackWriteErrorAndClose(deferredErr, c.ID(), isTerminal, httpCon, httpBuf)
+ }()
+
if !c.batched {
c.lock.Lock()
if err := c.syncContainer(); err != nil {
c.lock.Unlock()
- // Write any errors to the HTTP buffer before we close.
- hijackWriteErrorAndClose(err, c.ID(), httpCon, httpBuf)
-
return err
}
// We are NOT holding the lock for the duration of the function.
@@ -280,16 +276,80 @@ func (c *Container) HTTPAttach(httpCon net.Conn, httpBuf *bufio.ReadWriter, stre
}
if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) {
- toReturn := errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers")
-
- // Write any errors to the HTTP buffer before we close.
- hijackWriteErrorAndClose(toReturn, c.ID(), httpCon, httpBuf)
+ return errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers")
+ }
- return toReturn
+ if !streamAttach && !streamLogs {
+ return errors.Wrapf(define.ErrInvalidArg, "must specify at least one of stream or logs")
}
logrus.Infof("Performing HTTP Hijack attach to container %s", c.ID())
+ if streamLogs {
+ // Get all logs for the container
+ logChan := make(chan *logs.LogLine)
+ logOpts := new(logs.LogOptions)
+ logOpts.Tail = -1
+ logOpts.WaitGroup = new(sync.WaitGroup)
+ errChan := make(chan error)
+ go func() {
+ var err error
+ // In non-terminal mode we need to prepend with the
+ // stream header.
+ logrus.Debugf("Writing logs for container %s to HTTP attach", c.ID())
+ for logLine := range logChan {
+ if !isTerminal {
+ device := logLine.Device
+ var header []byte
+ headerLen := uint32(len(logLine.Msg))
+
+ switch strings.ToLower(device) {
+ case "stdin":
+ header = makeHTTPAttachHeader(0, headerLen)
+ case "stdout":
+ header = makeHTTPAttachHeader(1, headerLen)
+ case "stderr":
+ header = makeHTTPAttachHeader(2, headerLen)
+ default:
+ logrus.Errorf("Unknown device for log line: %s", device)
+ header = makeHTTPAttachHeader(1, headerLen)
+ }
+ _, err = httpBuf.Write(header)
+ if err != nil {
+ break
+ }
+ }
+ _, err = httpBuf.Write([]byte(logLine.Msg))
+ if err != nil {
+ break
+ }
+ _, err = httpBuf.Write([]byte("\n"))
+ if err != nil {
+ break
+ }
+ err = httpBuf.Flush()
+ if err != nil {
+ break
+ }
+ }
+ errChan <- err
+ }()
+ go func() {
+ logOpts.WaitGroup.Wait()
+ close(logChan)
+ }()
+ if err := c.ReadLog(logOpts, logChan); err != nil {
+ return err
+ }
+ logrus.Debugf("Done reading logs for container %s", c.ID())
+ if err := <-errChan; err != nil {
+ return err
+ }
+ }
+ if !streamAttach {
+ return nil
+ }
+
c.newContainerEvent(events.Attach)
return c.ociRuntime.HTTPAttach(c, httpCon, httpBuf, streams, detachKeys, cancel)
}
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index 5469462f8..c1ce8b724 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -221,7 +221,7 @@ func (c *Container) ExecStart(sessionID string) error {
// ExecStartAndAttach starts and attaches to an exec session in a container.
// TODO: Should we include detach keys in the signature to allow override?
// TODO: How do we handle AttachStdin/AttachStdout/AttachStderr?
-func (c *Container) ExecStartAndAttach(sessionID string, streams *AttachStreams) error {
+func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams) error {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -544,7 +544,7 @@ func (c *Container) ExecResize(sessionID string, newSize remotecommand.TerminalS
// Exec emulates the old Libpod exec API, providing a single call to create,
// run, and remove an exec session. Returns exit code and error. Exit code is
// not guaranteed to be set sanely if error is not nil.
-func (c *Container) Exec(config *ExecConfig, streams *AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) {
+func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan remotecommand.TerminalSize) (int, error) {
sessionID, err := c.ExecCreate(config)
if err != nil {
return -1, err
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 4e18819b8..50bd9bc25 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -19,6 +19,7 @@ import (
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
"github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/mount"
@@ -339,6 +340,29 @@ func (c *Container) syncContainer() error {
return nil
}
+func (c *Container) setupStorageMapping(dest, from *storage.IDMappingOptions) {
+ if c.config.Rootfs != "" {
+ return
+ }
+ *dest = *from
+ if dest.AutoUserNs {
+ overrides := c.getUserOverrides()
+ dest.AutoUserNsOpts.PasswdFile = overrides.ContainerEtcPasswdPath
+ dest.AutoUserNsOpts.GroupFile = overrides.ContainerEtcGroupPath
+ if c.config.User != "" {
+ initialSize := uint32(0)
+ parts := strings.Split(c.config.User, ":")
+ for _, p := range parts {
+ s, err := strconv.ParseUint(p, 10, 32)
+ if err == nil && uint32(s) > initialSize {
+ initialSize = uint32(s)
+ }
+ }
+ dest.AutoUserNsOpts.InitialSize = initialSize + 1
+ }
+ }
+}
+
// Create container root filesystem for use
func (c *Container) setupStorage(ctx context.Context) error {
span, _ := opentracing.StartSpanFromContext(ctx, "setupStorage")
@@ -398,29 +422,45 @@ func (c *Container) setupStorage(ctx context.Context) error {
options.MountOpts = newOptions
}
- if c.config.Rootfs == "" {
- options.IDMappingOptions = c.config.IDMappings
- }
+ c.setupStorageMapping(&options.IDMappingOptions, &c.config.IDMappings)
+
containerInfo, err := c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, options)
if err != nil {
return errors.Wrapf(err, "error creating container storage")
}
- if len(c.config.IDMappings.UIDMap) != 0 || len(c.config.IDMappings.GIDMap) != 0 {
- if err := os.Chown(containerInfo.RunDir, c.RootUID(), c.RootGID()); err != nil {
+ c.config.IDMappings.UIDMap = containerInfo.UIDMap
+ c.config.IDMappings.GIDMap = containerInfo.GIDMap
+
+ processLabel := containerInfo.ProcessLabel
+ switch {
+ case c.ociRuntime.SupportsKVM():
+ processLabel, err = util.SELinuxKVMLabel(processLabel)
+ if err != nil {
return err
}
-
- if err := os.Chown(containerInfo.Dir, c.RootUID(), c.RootGID()); err != nil {
+ case c.config.Systemd:
+ processLabel, err = util.SELinuxInitLabel(processLabel)
+ if err != nil {
return err
}
}
- c.config.ProcessLabel = containerInfo.ProcessLabel
+ c.config.ProcessLabel = processLabel
c.config.MountLabel = containerInfo.MountLabel
c.config.StaticDir = containerInfo.Dir
c.state.RunDir = containerInfo.RunDir
+ if len(c.config.IDMappings.UIDMap) != 0 || len(c.config.IDMappings.GIDMap) != 0 {
+ if err := os.Chown(containerInfo.RunDir, c.RootUID(), c.RootGID()); err != nil {
+ return err
+ }
+
+ if err := os.Chown(containerInfo.Dir, c.RootUID(), c.RootGID()); err != nil {
+ return err
+ }
+ }
+
// Set the default Entrypoint and Command
if containerInfo.Config != nil {
if c.config.Entrypoint == nil {
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index a3f97f2a6..c40ad45b9 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -396,6 +396,20 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
}
+ if c.config.IDMappings.AutoUserNs {
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
+ return nil, err
+ }
+ g.ClearLinuxUIDMappings()
+ for _, uidmap := range c.config.IDMappings.UIDMap {
+ g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
+ }
+ g.ClearLinuxGIDMappings()
+ for _, gidmap := range c.config.IDMappings.GIDMap {
+ g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
+ }
+ }
+
g.SetRootPath(c.state.Mountpoint)
g.AddAnnotation(annotations.Created, c.config.CreatedTime.Format(time.RFC3339Nano))
g.AddAnnotation("org.opencontainers.image.stopSignal", fmt.Sprintf("%d", c.config.StopSignal))
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index 395271b2a..2a611c2d9 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -6,6 +6,7 @@ import (
"context"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/lookup"
spec "github.com/opencontainers/runtime-spec/specs-go"
)
@@ -44,3 +45,7 @@ func (c *Container) copyOwnerAndPerms(source, dest string) error {
func (c *Container) getOCICgroupPath() (string, error) {
return "", define.ErrNotImplemented
}
+
+func (c *Container) getUserOverrides() *lookup.Overrides {
+ return nil
+}
diff --git a/libpod/container.log.go b/libpod/container_log.go
index 514edb8c8..bfa303e84 100644
--- a/libpod/container.log.go
+++ b/libpod/container_log.go
@@ -3,6 +3,7 @@ package libpod
import (
"os"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -22,7 +23,7 @@ func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChan
func (c *Container) ReadLog(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
// TODO Skip sending logs until journald logs can be read
// TODO make this not a magic string
- if c.LogDriver() == JournaldLogging {
+ if c.LogDriver() == define.JournaldLogging {
return c.readFromJournal(options, logChannel)
}
return c.readFromLogFile(options, logChannel)
diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go
index 2a35a2ae9..98a69966a 100644
--- a/libpod/container_top_linux.go
+++ b/libpod/container_top_linux.go
@@ -112,7 +112,7 @@ func (c *Container) execPS(args []string) ([]string, error) {
defer wErrPipe.Close()
defer rErrPipe.Close()
- streams := new(AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = wPipe
streams.ErrorStream = wErrPipe
streams.AttachOutput = true
diff --git a/libpod/define/config.go b/libpod/define/config.go
index 5598f97a3..17d764c65 100644
--- a/libpod/define/config.go
+++ b/libpod/define/config.go
@@ -1,5 +1,10 @@
package define
+import (
+ "bufio"
+ "io"
+)
+
var (
// DefaultInfraImage to use for infra container
DefaultInfraImage = "k8s.gcr.io/pause:3.2"
@@ -26,3 +31,38 @@ type InfoData struct {
// VolumeDriverLocal is the "local" volume driver. It is managed by libpod
// itself.
const VolumeDriverLocal = "local"
+
+const (
+ OCIManifestDir = "oci-dir"
+ OCIArchive = "oci-archive"
+ V2s2ManifestDir = "docker-dir"
+ V2s2Archive = "docker-archive"
+)
+
+// AttachStreams contains streams that will be attached to the container
+type AttachStreams struct {
+ // OutputStream will be attached to container's STDOUT
+ OutputStream io.WriteCloser
+ // ErrorStream will be attached to container's STDERR
+ ErrorStream io.WriteCloser
+ // InputStream will be attached to container's STDIN
+ InputStream *bufio.Reader
+ // AttachOutput is whether to attach to STDOUT
+ // If false, stdout will not be attached
+ AttachOutput bool
+ // AttachError is whether to attach to STDERR
+ // If false, stdout will not be attached
+ AttachError bool
+ // AttachInput is whether to attach to STDIN
+ // If false, stdout will not be attached
+ AttachInput bool
+}
+
+// JournaldLogging is the string conmon expects to specify journald logging
+const JournaldLogging = "journald"
+
+// KubernetesLogging is the string conmon expects when specifying to use the kubernetes logging format
+const KubernetesLogging = "k8s-file"
+
+// JSONLogging is the string conmon expects when specifying to use the json logging format
+const JSONLogging = "json-file"
diff --git a/libpod/define/inspect.go b/libpod/define/ctr_inspect.go
index b7cd13f82..b7cd13f82 100644
--- a/libpod/define/inspect.go
+++ b/libpod/define/ctr_inspect.go
diff --git a/libpod/define/info.go b/libpod/define/info.go
new file mode 100644
index 000000000..2516cad77
--- /dev/null
+++ b/libpod/define/info.go
@@ -0,0 +1,102 @@
+package define
+
+import "github.com/containers/storage/pkg/idtools"
+
+// Info is the overall struct that describes the host system
+// running libpod/podman
+type Info struct {
+ Host *HostInfo `json:"host"`
+ Store *StoreInfo `json:"store"`
+ Registries map[string]interface{} `json:"registries"`
+ Version Version `json:"version"`
+}
+
+//HostInfo describes the libpod host
+type HostInfo struct {
+ Arch string `json:"arch"`
+ BuildahVersion string `json:"buildahVersion"`
+ CGroupsVersion string `json:"cgroupVersion"`
+ Conmon *ConmonInfo `json:"conmon"`
+ CPUs int `json:"cpus"`
+ Distribution DistributionInfo `json:"distribution"`
+ EventLogger string `json:"eventLogger"`
+ Hostname string `json:"hostname"`
+ IDMappings IDMappings `json:"idMappings,omitempty"`
+ Kernel string `json:"kernel"`
+ MemFree int64 `json:"memFree"`
+ MemTotal int64 `json:"memTotal"`
+ OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"`
+ OS string `json:"os"`
+ Rootless bool `json:"rootless"`
+ RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"`
+ Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"`
+ SwapFree int64 `json:"swapFree"`
+ SwapTotal int64 `json:"swapTotal"`
+ Uptime string `json:"uptime"`
+}
+
+// SlirpInfo describes the slirp exectuable that
+// is being being used.
+type SlirpInfo struct {
+ Executable string `json:"executable"`
+ Package string `json:"package"`
+ Version string `json:"version"`
+}
+
+// IDMappings describe the GID and UID mappings
+type IDMappings struct {
+ GIDMap []idtools.IDMap `json:"gidmap"`
+ UIDMap []idtools.IDMap `json:"uidmap"`
+}
+
+// DistributionInfo describes the host distribution
+// for libpod
+type DistributionInfo struct {
+ Distribution string `json:"distribution"`
+ Version string `json:"version"`
+}
+
+// ConmonInfo describes the conmon executable being used
+type ConmonInfo struct {
+ Package string `json:"package"`
+ Path string `json:"path"`
+ Version string `json:"version"`
+}
+
+// OCIRuntimeInfo describes the runtime (crun or runc) being
+// used with podman
+type OCIRuntimeInfo struct {
+ Name string `json:"name"`
+ Package string `json:"package"`
+ Path string `json:"path"`
+ Version string `json:"version"`
+}
+
+// StoreInfo describes the container storage and its
+// attributes
+type StoreInfo struct {
+ ConfigFile string `json:"configFile"`
+ ContainerStore ContainerStore `json:"containerStore"`
+ GraphDriverName string `json:"graphDriverName"`
+ GraphOptions map[string]interface{} `json:"graphOptions"`
+ GraphRoot string `json:"graphRoot"`
+ GraphStatus map[string]string `json:"graphStatus"`
+ ImageStore ImageStore `json:"imageStore"`
+ RunRoot string `json:"runRoot"`
+ VolumePath string `json:"volumePath"`
+}
+
+// ImageStore describes the image store. Right now only the number
+// of images present
+type ImageStore struct {
+ Number int `json:"number"`
+}
+
+// ContainerStore describes the quantity of containers in the
+// store by status
+type ContainerStore struct {
+ Number int `json:"number"`
+ Paused int `json:"paused"`
+ Running int `json:"running"`
+ Stopped int `json:"stopped"`
+}
diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go
new file mode 100644
index 000000000..8558c149b
--- /dev/null
+++ b/libpod/define/pod_inspect.go
@@ -0,0 +1,97 @@
+package define
+
+import (
+ "net"
+ "time"
+
+ "github.com/cri-o/ocicni/pkg/ocicni"
+)
+
+// InspectPodData contains detailed information on a pod's configuration and
+// state. It is used as the output of Inspect on pods.
+type InspectPodData struct {
+ // ID is the ID of the pod.
+ ID string `json:"Id"`
+ // Name is the name of the pod.
+ Name string
+ // Namespace is the Libpod namespace the pod is placed in.
+ Namespace string `json:"Namespace,omitempty"`
+ // Created is the time when the pod was created.
+ Created time.Time
+ // Hostname is the hostname that the pod will set.
+ Hostname string
+ // Labels is a set of key-value labels that have been applied to the
+ // pod.
+ Labels map[string]string `json:"Labels,omitempty"`
+ // CreateCgroup is whether this pod will create its own CGroup to group
+ // containers under.
+ CreateCgroup bool
+ // CgroupParent is the parent of the pod's CGroup.
+ CgroupParent string `json:"CgroupParent,omitempty"`
+ // CgroupPath is the path to the pod's CGroup.
+ CgroupPath string `json:"CgroupPath,omitempty"`
+ // CreateInfra is whether this pod will create an infra container to
+ // share namespaces.
+ CreateInfra bool
+ // InfraContainerID is the ID of the pod's infra container, if one is
+ // present.
+ InfraContainerID string `json:"InfraContainerID,omitempty"`
+ // InfraConfig is the configuration of the infra container of the pod.
+ // Will only be set if CreateInfra is true.
+ InfraConfig *InspectPodInfraConfig `json:"InfraConfig,omitempty"`
+ // SharedNamespaces contains a list of namespaces that will be shared by
+ // containers within the pod. Can only be set if CreateInfra is true.
+ SharedNamespaces []string `json:"SharedNamespaces,omitempty"`
+ // NumContainers is the number of containers in the pod, including the
+ // infra container.
+ NumContainers uint
+ // Containers gives a brief summary of all containers in the pod and
+ // their current status.
+ Containers []InspectPodContainerInfo `json:"Containers,omitempty"`
+}
+
+// InspectPodInfraConfig contains the configuration of the pod's infra
+// container.
+type InspectPodInfraConfig struct {
+ // PortBindings are ports that will be forwarded to the infra container
+ // and then shared with the pod.
+ PortBindings []ocicni.PortMapping
+ // HostNetwork is whether the infra container (and thus the whole pod)
+ // will use the host's network and not create a network namespace.
+ HostNetwork bool
+ // StaticIP is a static IPv4 that will be assigned to the infra
+ // container and then used by the pod.
+ StaticIP net.IP
+ // StaticMAC is a static MAC address that will be assigned to the infra
+ // container and then used by the pod.
+ StaticMAC net.HardwareAddr
+ // NoManageResolvConf indicates that the pod will not manage resolv.conf
+ // and instead each container will handle their own.
+ NoManageResolvConf bool
+ // DNSServer is a set of DNS Servers that will be used by the infra
+ // container's resolv.conf and shared with the remainder of the pod.
+ DNSServer []string
+ // DNSSearch is a set of DNS search domains that will be used by the
+ // infra container's resolv.conf and shared with the remainder of the
+ // pod.
+ DNSSearch []string
+ // DNSOption is a set of DNS options that will be used by the infra
+ // container's resolv.conf and shared with the remainder of the pod.
+ DNSOption []string
+ // NoManageHosts indicates that the pod will not manage /etc/hosts and
+ // instead each container will handle their own.
+ NoManageHosts bool
+ // HostAdd adds a number of hosts to the infra container's resolv.conf
+ // which will be shared with the rest of the pod.
+ HostAdd []string
+}
+
+// InspectPodContainerInfo contains information on a container in a pod.
+type InspectPodContainerInfo struct {
+ // ID is the ID of the container.
+ ID string `json:"Id"`
+ // Name is the name of the container.
+ Name string
+ // State is the current status of the container.
+ State string
+}
diff --git a/libpod/filters/containers.go b/libpod/filters/containers.go
new file mode 100644
index 000000000..c13372f91
--- /dev/null
+++ b/libpod/filters/containers.go
@@ -0,0 +1,157 @@
+package lpfilters
+
+import (
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/timetype"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+// GenerateContainerFilterFuncs return ContainerFilter functions based of filter.
+func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) {
+ switch filter {
+ case "id":
+ return func(c *libpod.Container) bool {
+ return strings.Contains(c.ID(), filterValue)
+ }, nil
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(c *libpod.Container) bool {
+ for labelKey, labelValue := range c.Labels() {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "name":
+ return func(c *libpod.Container) bool {
+ match, err := regexp.MatchString(filterValue, c.Name())
+ if err != nil {
+ return false
+ }
+ return match
+ }, nil
+ case "exited":
+ exitCode, err := strconv.ParseInt(filterValue, 10, 32)
+ if err != nil {
+ return nil, errors.Wrapf(err, "exited code out of range %q", filterValue)
+ }
+ return func(c *libpod.Container) bool {
+ ec, exited, err := c.ExitCode()
+ if ec == int32(exitCode) && err == nil && exited {
+ return true
+ }
+ return false
+ }, nil
+ case "status":
+ if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) {
+ return nil, errors.Errorf("%s is not a valid status", filterValue)
+ }
+ return func(c *libpod.Container) bool {
+ status, err := c.State()
+ if err != nil {
+ return false
+ }
+ if filterValue == "stopped" {
+ filterValue = "exited"
+ }
+ state := status.String()
+ if status == define.ContainerStateConfigured {
+ state = "created"
+ } else if status == define.ContainerStateStopped {
+ state = "exited"
+ }
+ return state == filterValue
+ }, nil
+ case "ancestor":
+ // This needs to refine to match docker
+ // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant.
+ return func(c *libpod.Container) bool {
+ containerConfig := c.Config()
+ if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) {
+ return true
+ }
+ return false
+ }, nil
+ case "before":
+ ctr, err := r.LookupContainer(filterValue)
+ if err != nil {
+ return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
+ }
+ containerConfig := ctr.Config()
+ createTime := containerConfig.CreatedTime
+ return func(c *libpod.Container) bool {
+ cc := c.Config()
+ return createTime.After(cc.CreatedTime)
+ }, nil
+ case "since":
+ ctr, err := r.LookupContainer(filterValue)
+ if err != nil {
+ return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
+ }
+ containerConfig := ctr.Config()
+ createTime := containerConfig.CreatedTime
+ return func(c *libpod.Container) bool {
+ cc := c.Config()
+ return createTime.Before(cc.CreatedTime)
+ }, nil
+ case "volume":
+ //- volume=(<volume-name>|<mount-point-destination>)
+ return func(c *libpod.Container) bool {
+ containerConfig := c.Config()
+ var dest string
+ arr := strings.Split(filterValue, ":")
+ source := arr[0]
+ if len(arr) == 2 {
+ dest = arr[1]
+ }
+ for _, mount := range containerConfig.Spec.Mounts {
+ if dest != "" && (mount.Source == source && mount.Destination == dest) {
+ return true
+ }
+ if dest == "" && mount.Source == source {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "health":
+ return func(c *libpod.Container) bool {
+ hcStatus, err := c.HealthCheckStatus()
+ if err != nil {
+ return false
+ }
+ return hcStatus == filterValue
+ }, nil
+ case "until":
+ ts, err := timetype.GetTimestamp(filterValue, time.Now())
+ if err != nil {
+ return nil, err
+ }
+ seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
+ if err != nil {
+ return nil, err
+ }
+ until := time.Unix(seconds, nanoseconds)
+ return func(c *libpod.Container) bool {
+ if !until.IsZero() && c.CreatedTime().After((until)) {
+ return true
+ }
+ return false
+ }, nil
+ }
+ return nil, errors.Errorf("%s is an invalid filter", filter)
+}
diff --git a/libpod/podfilters/pods.go b/libpod/filters/pods.go
index 54fa85edc..9bf436eab 100644
--- a/libpod/podfilters/pods.go
+++ b/libpod/filters/pods.go
@@ -1,4 +1,4 @@
-package podfilters
+package lpfilters
import (
"strconv"
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index 08a613dfe..daddb6561 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -108,7 +108,7 @@ func (c *Container) runHealthCheck() (HealthCheckStatus, error) {
hcw := hcWriteCloser{
captureBuffer,
}
- streams := new(AttachStreams)
+ streams := new(define.AttachStreams)
streams.OutputStream = hcw
streams.ErrorStream = hcw
diff --git a/libpod/info.go b/libpod/info.go
index 8d411f0d4..d7ed5bb16 100644
--- a/libpod/info.go
+++ b/libpod/info.go
@@ -13,7 +13,9 @@ import (
"time"
"github.com/containers/buildah"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/cgroups"
+ registries2 "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/system"
@@ -21,14 +23,85 @@ import (
"github.com/sirupsen/logrus"
)
+// Info returns the store and host information
+func (r *Runtime) info() (*define.Info, error) {
+ info := define.Info{}
+ versionInfo, err := define.GetVersion()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting version info")
+ }
+ info.Version = versionInfo
+ // get host information
+ hostInfo, err := r.hostInfo()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting host info")
+ }
+ info.Host = hostInfo
+
+ // get store information
+ storeInfo, err := r.storeInfo()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting store info")
+ }
+ info.Store = storeInfo
+ registries := make(map[string]interface{})
+ data, err := registries2.GetRegistriesData()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting registries")
+ }
+ for _, reg := range data {
+ registries[reg.Prefix] = reg
+ }
+ regs, err := registries2.GetRegistries()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting registries")
+ }
+ if len(regs) > 0 {
+ registries["search"] = regs
+ }
+
+ info.Registries = registries
+ return &info, nil
+}
+
// top-level "host" info
-func (r *Runtime) hostInfo() (map[string]interface{}, error) {
+func (r *Runtime) hostInfo() (*define.HostInfo, error) {
// lets say OS, arch, number of cpus, amount of memory, maybe os distribution/version, hostname, kernel version, uptime
- info := map[string]interface{}{}
- info["os"] = runtime.GOOS
- info["arch"] = runtime.GOARCH
- info["cpus"] = runtime.NumCPU()
- info["rootless"] = rootless.IsRootless()
+ mi, err := system.ReadMemInfo()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading memory info")
+ }
+
+ hostDistributionInfo := r.GetHostDistributionInfo()
+
+ kv, err := readKernelVersion()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading kernel version")
+ }
+
+ host, err := os.Hostname()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting hostname")
+ }
+ info := define.HostInfo{
+ Arch: runtime.GOARCH,
+ BuildahVersion: buildah.Version,
+ CPUs: runtime.NumCPU(),
+ Distribution: hostDistributionInfo,
+ EventLogger: r.eventer.String(),
+ Hostname: host,
+ IDMappings: define.IDMappings{},
+ Kernel: kv,
+ MemFree: mi.MemFree,
+ MemTotal: mi.MemTotal,
+ OS: runtime.GOOS,
+ Rootless: rootless.IsRootless(),
+ Slirp4NetNS: define.SlirpInfo{},
+ SwapFree: mi.SwapFree,
+ SwapTotal: mi.SwapTotal,
+ }
+
+ // CGroups version
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, errors.Wrapf(err, "error reading cgroups mode")
@@ -37,17 +110,8 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if unified {
cgroupVersion = "v2"
}
- info["CgroupVersion"] = cgroupVersion
- mi, err := system.ReadMemInfo()
- if err != nil {
- return nil, errors.Wrapf(err, "error reading memory info")
- }
- // TODO this might be a place for github.com/dustin/go-humanize
- info["MemTotal"] = mi.MemTotal
- info["MemFree"] = mi.MemFree
- info["SwapTotal"] = mi.SwapTotal
- info["SwapFree"] = mi.SwapFree
- hostDistributionInfo := r.GetHostDistributionInfo()
+ info.CGroupsVersion = cgroupVersion
+
if rootless.IsRootless() {
if path, err := exec.LookPath("slirp4netns"); err == nil {
logrus.Warnf("Failed to retrieve program version for %s: %v", path, err)
@@ -55,11 +119,12 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if err != nil {
logrus.Warnf("Failed to retrieve program version for %s: %v", path, err)
}
- program := map[string]interface{}{}
- program["Executable"] = path
- program["Version"] = version
- program["Package"] = packageVersion(path)
- info["slirp4netns"] = program
+ program := define.SlirpInfo{
+ Executable: path,
+ Package: packageVersion(path),
+ Version: version,
+ }
+ info.Slirp4NetNS = program
}
uidmappings, err := rootless.ReadMappingsProc("/proc/self/uid_map")
if err != nil {
@@ -69,29 +134,19 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if err != nil {
return nil, errors.Wrapf(err, "error reading gid mappings")
}
- idmappings := make(map[string]interface{})
- idmappings["uidmap"] = uidmappings
- idmappings["gidmap"] = gidmappings
- info["IDMappings"] = idmappings
- }
- info["Distribution"] = map[string]interface{}{
- "distribution": hostDistributionInfo["Distribution"],
- "version": hostDistributionInfo["Version"],
- }
- info["BuildahVersion"] = buildah.Version
- kv, err := readKernelVersion()
- if err != nil {
- return nil, errors.Wrapf(err, "error reading kernel version")
+ idmappings := define.IDMappings{
+ GIDMap: gidmappings,
+ UIDMap: uidmappings,
+ }
+ info.IDMappings = idmappings
}
- info["kernel"] = kv
- runtimeInfo, err := r.defaultOCIRuntime.RuntimeInfo()
+ conmonInfo, ociruntimeInfo, err := r.defaultOCIRuntime.RuntimeInfo()
if err != nil {
logrus.Errorf("Error getting info on OCI runtime %s: %v", r.defaultOCIRuntime.Name(), err)
} else {
- for k, v := range runtimeInfo {
- info[k] = v
- }
+ info.Conmon = conmonInfo
+ info.OCIRuntime = ociruntimeInfo
}
up, err := readUptime()
@@ -105,6 +160,7 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
return nil, errors.Wrapf(err, "error parsing system uptime")
}
+ // TODO Isnt there a simple lib for this, something like humantime?
hoursFound := false
var timeBuffer bytes.Buffer
var hoursBuffer bytes.Buffer
@@ -121,32 +177,75 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
}
}
- info["uptime"] = timeBuffer.String()
+ info.Uptime = timeBuffer.String()
if hoursFound {
hours, err := strconv.ParseFloat(hoursBuffer.String(), 64)
if err == nil {
days := hours / 24
- info["uptime"] = fmt.Sprintf("%s (Approximately %.2f days)", info["uptime"], days)
+ info.Uptime = fmt.Sprintf("%s (Approximately %.2f days)", info.Uptime, days)
}
}
- host, err := os.Hostname()
+ return &info, nil
+}
+
+func (r *Runtime) getContainerStoreInfo() (define.ContainerStore, error) {
+ var (
+ paused, running, stopped int
+ )
+ cs := define.ContainerStore{}
+ cons, err := r.GetAllContainers()
if err != nil {
- return nil, errors.Wrapf(err, "error getting hostname")
+ return cs, err
}
- info["hostname"] = host
- info["eventlogger"] = r.eventer.String()
-
- return info, nil
+ for _, con := range cons {
+ state, err := con.State()
+ if err != nil {
+ return cs, err
+ }
+ switch state {
+ case define.ContainerStateRunning:
+ running += 1
+ case define.ContainerStatePaused:
+ paused += 1
+ default:
+ stopped += 1
+ }
+ }
+ cs.Number = len(cons)
+ cs.Paused = paused
+ cs.Stopped = stopped
+ cs.Running = running
+ return cs, nil
}
// top-level "store" info
-func (r *Runtime) storeInfo() (map[string]interface{}, error) {
+func (r *Runtime) storeInfo() (*define.StoreInfo, error) {
// lets say storage driver in use, number of images, number of containers
- info := map[string]interface{}{}
- info["GraphRoot"] = r.store.GraphRoot()
- info["RunRoot"] = r.store.RunRoot()
- info["GraphDriverName"] = r.store.GraphDriverName()
+ configFile, err := storage.DefaultConfigFile(rootless.IsRootless())
+ if err != nil {
+ return nil, err
+ }
+ images, err := r.store.Images()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting number of images")
+ }
+ conInfo, err := r.getContainerStoreInfo()
+ if err != nil {
+ return nil, err
+ }
+ imageInfo := define.ImageStore{Number: len(images)}
+
+ info := define.StoreInfo{
+ ImageStore: imageInfo,
+ ContainerStore: conInfo,
+ GraphRoot: r.store.GraphRoot(),
+ RunRoot: r.store.RunRoot(),
+ GraphDriverName: r.store.GraphDriverName(),
+ GraphOptions: nil,
+ VolumePath: r.config.Engine.VolumePath,
+ ConfigFile: configFile,
+ }
graphOptions := map[string]interface{}{}
for _, o := range r.store.GraphOptions() {
split := strings.SplitN(o, "=", 2)
@@ -164,14 +263,8 @@ func (r *Runtime) storeInfo() (map[string]interface{}, error) {
graphOptions[split[0]] = split[1]
}
}
- info["GraphOptions"] = graphOptions
- info["VolumePath"] = r.config.Engine.VolumePath
+ info.GraphOptions = graphOptions
- configFile, err := storage.DefaultConfigFile(rootless.IsRootless())
- if err != nil {
- return nil, err
- }
- info["ConfigFile"] = configFile
statusPairs, err := r.store.Status()
if err != nil {
return nil, err
@@ -180,24 +273,8 @@ func (r *Runtime) storeInfo() (map[string]interface{}, error) {
for _, pair := range statusPairs {
status[pair[0]] = pair[1]
}
- info["GraphStatus"] = status
- images, err := r.store.Images()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting number of images")
- }
- info["ImageStore"] = map[string]interface{}{
- "number": len(images),
- }
-
- containers, err := r.store.Containers()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting number of containers")
- }
- info["ContainerStore"] = map[string]interface{}{
- "number": len(containers),
- }
-
- return info, nil
+ info.GraphStatus = status
+ return &info, nil
}
func readKernelVersion() (string, error) {
@@ -225,14 +302,13 @@ func readUptime() (string, error) {
}
// GetHostDistributionInfo returns a map containing the host's distribution and version
-func (r *Runtime) GetHostDistributionInfo() map[string]string {
- dist := make(map[string]string)
-
+func (r *Runtime) GetHostDistributionInfo() define.DistributionInfo {
// Populate values in case we cannot find the values
// or the file
- dist["Distribution"] = "unknown"
- dist["Version"] = "unknown"
-
+ dist := define.DistributionInfo{
+ Distribution: "unknown",
+ Version: "unknown",
+ }
f, err := os.Open("/etc/os-release")
if err != nil {
return dist
@@ -242,10 +318,10 @@ func (r *Runtime) GetHostDistributionInfo() map[string]string {
l := bufio.NewScanner(f)
for l.Scan() {
if strings.HasPrefix(l.Text(), "ID=") {
- dist["Distribution"] = strings.TrimPrefix(l.Text(), "ID=")
+ dist.Distribution = strings.TrimPrefix(l.Text(), "ID=")
}
if strings.HasPrefix(l.Text(), "VERSION_ID=") {
- dist["Version"] = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_ID="), "\"")
+ dist.Version = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_ID="), "\"")
}
}
return dist
diff --git a/libpod/oci.go b/libpod/oci.go
index ef46cf5c3..9991c5625 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -4,6 +4,7 @@ import (
"bufio"
"net"
+ "github.com/containers/libpod/libpod/define"
"k8s.io/client-go/tools/remotecommand"
)
@@ -102,6 +103,9 @@ type OCIRuntime interface {
// SupportsNoCgroups is whether the runtime supports running containers
// without cgroups.
SupportsNoCgroups() bool
+ // SupportsKVM os whether the OCI runtime supports running containers
+ // without KVM separation
+ SupportsKVM() bool
// AttachSocketPath is the path to the socket to attach to a given
// container.
@@ -120,7 +124,7 @@ type OCIRuntime interface {
ExitFilePath(ctr *Container) (string, error)
// RuntimeInfo returns verbose information about the runtime.
- RuntimeInfo() (map[string]interface{}, error)
+ RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error)
}
// ExecOptions are options passed into ExecContainer. They control the command
@@ -141,7 +145,7 @@ type ExecOptions struct {
// the container was run as will be used.
User string
// Streams are the streams that will be attached to the container.
- Streams *AttachStreams
+ Streams *define.AttachStreams
// PreserveFDs is a number of additional file descriptors (in addition
// to 0, 1, 2) that will be passed to the executed process. The total FDs
// passed will be 3 + PreserveFDs.
diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go
index fb0a54bff..ff158c2d1 100644
--- a/libpod/oci_attach_linux.go
+++ b/libpod/oci_attach_linux.go
@@ -31,7 +31,7 @@ const (
// Attach to the given container
// Does not check if state is appropriate
// started is only required if startContainer is true
-func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
+func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
}
@@ -94,7 +94,7 @@ func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan re
// 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go
// 5. child receives on startFd, runs the runtime exec command
// attachToExec is responsible for closing startFd and attachFd
-func (c *Container) attachToExec(streams *AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error {
+func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error {
if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to")
}
@@ -189,7 +189,7 @@ func buildSocketPath(socketPath string) string {
return socketPath
}
-func setupStdioChannels(streams *AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) {
+func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) {
receiveStdoutError := make(chan error)
go func() {
receiveStdoutError <- redirectResponseToOutputStreams(streams.OutputStream, streams.ErrorStream, streams.AttachOutput, streams.AttachError, conn)
@@ -257,7 +257,7 @@ func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, writeO
return err
}
-func readStdio(streams *AttachStreams, receiveStdoutError, stdinDone chan error) error {
+func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error {
var err error
select {
case err = <-receiveStdoutError:
diff --git a/libpod/oci_attach_unsupported.go b/libpod/oci_attach_unsupported.go
index 987d2c973..3b0216e5d 100644
--- a/libpod/oci_attach_unsupported.go
+++ b/libpod/oci_attach_unsupported.go
@@ -9,10 +9,10 @@ import (
"k8s.io/client-go/tools/remotecommand"
)
-func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
+func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool, started chan bool) error {
return define.ErrNotImplemented
}
-func (c *Container) attachToExec(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error {
+func (c *Container) attachToExec(streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error {
return define.ErrNotImplemented
}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 6a0097b8e..da4b85067 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -5,7 +5,6 @@ package libpod
import (
"bufio"
"bytes"
- "encoding/binary"
"fmt"
"io"
"io/ioutil"
@@ -61,6 +60,7 @@ type ConmonOCIRuntime struct {
noPivot bool
reservePorts bool
supportsJSON bool
+ supportsKVM bool
supportsNoCgroups bool
sdNotify bool
}
@@ -71,11 +71,25 @@ type ConmonOCIRuntime struct {
// The first path that points to a valid executable will be used.
// Deliberately private. Someone should not be able to construct this outside of
// libpod.
-func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *config.Config, supportsJSON, supportsNoCgroups bool) (OCIRuntime, error) {
+func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *config.Config) (OCIRuntime, error) {
if name == "" {
return nil, errors.Wrapf(define.ErrInvalidArg, "the OCI runtime must be provided a non-empty name")
}
+ // Make lookup tables for runtime support
+ supportsJSON := make(map[string]bool, len(runtimeCfg.Engine.RuntimeSupportsJSON))
+ supportsNoCgroups := make(map[string]bool, len(runtimeCfg.Engine.RuntimeSupportsNoCgroups))
+ supportsKVM := make(map[string]bool, len(runtimeCfg.Engine.RuntimeSupportsKVM))
+ for _, r := range runtimeCfg.Engine.RuntimeSupportsJSON {
+ supportsJSON[r] = true
+ }
+ for _, r := range runtimeCfg.Engine.RuntimeSupportsNoCgroups {
+ supportsNoCgroups[r] = true
+ }
+ for _, r := range runtimeCfg.Engine.RuntimeSupportsKVM {
+ supportsKVM[r] = true
+ }
+
runtime := new(ConmonOCIRuntime)
runtime.name = name
runtime.conmonPath = conmonPath
@@ -90,8 +104,9 @@ func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtime
// TODO: probe OCI runtime for feature and enable automatically if
// available.
- runtime.supportsJSON = supportsJSON
- runtime.supportsNoCgroups = supportsNoCgroups
+ runtime.supportsJSON = supportsJSON[name]
+ runtime.supportsNoCgroups = supportsNoCgroups[name]
+ runtime.supportsKVM = supportsKVM[name]
foundPath := false
for _, path := range paths {
@@ -353,6 +368,9 @@ func (r *ConmonOCIRuntime) StartContainer(ctr *Container) error {
if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok {
env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify))
}
+ if path, ok := os.LookupEnv("PATH"); ok {
+ env = append(env, fmt.Sprintf("PATH=%s", path))
+ }
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "start", ctr.ID()); err != nil {
return err
}
@@ -477,8 +495,7 @@ func (r *ConmonOCIRuntime) UnpauseContainer(ctr *Container) error {
}
// HTTPAttach performs an attach for the HTTP API.
-// This will consume, and automatically close, the hijacked HTTP session.
-// It is not necessary to close it independently.
+// The caller must handle closing the HTTP connection after this returns.
// The cancel channel is not closed; it is up to the caller to do so after
// this function returns.
// If this is a container with a terminal, we will stream raw. If it is not, we
@@ -489,13 +506,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, httpConn net.Conn, httpBuf
isTerminal = ctr.config.Spec.Process.Terminal
}
- // Ensure that our contract of closing the HTTP connection is honored.
- defer hijackWriteErrorAndClose(deferredErr, ctr.ID(), httpConn, httpBuf)
-
if streams != nil {
- if isTerminal {
- return errors.Wrapf(define.ErrInvalidArg, "cannot specify which streams to attach as container %s has a terminal", ctr.ID())
- }
if !streams.Stdin && !streams.Stdout && !streams.Stderr {
return errors.Wrapf(define.ErrInvalidArg, "must specify at least one stream to attach to")
}
@@ -544,8 +555,16 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, httpConn net.Conn, httpBuf
go func() {
var err error
if isTerminal {
+ // Hack: return immediately if attachStdout not set to
+ // emulate Docker.
+ // Basically, when terminal is set, STDERR goes nowhere.
+ // Everything does over STDOUT.
+ // Therefore, if not attaching STDOUT - we'll never copy
+ // anything from here.
logrus.Debugf("Performing terminal HTTP attach for container %s", ctr.ID())
- err = httpAttachTerminalCopy(conn, httpBuf, ctr.ID())
+ if attachStdout {
+ err = httpAttachTerminalCopy(conn, httpBuf, ctr.ID())
+ }
} else {
logrus.Debugf("Performing non-terminal HTTP attach for container %s", ctr.ID())
err = httpAttachNonTerminalCopy(conn, httpBuf, ctr.ID(), attachStdin, attachStdout, attachStderr)
@@ -930,6 +949,13 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
if options.TCPEstablished {
args = append(args, "--tcp-established")
}
+ runtimeDir, err := util.GetRuntimeDir()
+ if err != nil {
+ return err
+ }
+ if err = os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil {
+ return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
+ }
args = append(args, ctr.ID())
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
}
@@ -939,7 +965,7 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
func (r *ConmonOCIRuntime) SupportsCheckpoint() bool {
// Check if the runtime implements checkpointing. Currently only
// runc's checkpoint/restore implementation is supported.
- cmd := exec.Command(r.path, "checkpoint", "-h")
+ cmd := exec.Command(r.path, "checkpoint", "--help")
if err := cmd.Start(); err != nil {
return false
}
@@ -961,6 +987,12 @@ func (r *ConmonOCIRuntime) SupportsNoCgroups() bool {
return r.supportsNoCgroups
}
+// SupportsKVM checks if the OCI runtime supports running containers
+// without KVM separation
+func (r *ConmonOCIRuntime) SupportsKVM() bool {
+ return r.supportsKVM
+}
+
// AttachSocketPath is the path to a single container's attach socket.
func (r *ConmonOCIRuntime) AttachSocketPath(ctr *Container) (string, error) {
if ctr == nil {
@@ -989,32 +1021,30 @@ func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) {
}
// RuntimeInfo provides information on the runtime.
-func (r *ConmonOCIRuntime) RuntimeInfo() (map[string]interface{}, error) {
+func (r *ConmonOCIRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
runtimePackage := packageVersion(r.path)
conmonPackage := packageVersion(r.conmonPath)
runtimeVersion, err := r.getOCIRuntimeVersion()
if err != nil {
- return nil, errors.Wrapf(err, "error getting version of OCI runtime %s", r.name)
+ return nil, nil, errors.Wrapf(err, "error getting version of OCI runtime %s", r.name)
}
conmonVersion, err := r.getConmonVersion()
if err != nil {
- return nil, errors.Wrapf(err, "error getting conmon version")
+ return nil, nil, errors.Wrapf(err, "error getting conmon version")
}
- info := make(map[string]interface{})
- info["Conmon"] = map[string]interface{}{
- "path": r.conmonPath,
- "package": conmonPackage,
- "version": conmonVersion,
+ conmon := define.ConmonInfo{
+ Package: conmonPackage,
+ Path: r.conmonPath,
+ Version: conmonVersion,
}
- info["OCIRuntime"] = map[string]interface{}{
- "name": r.name,
- "path": r.path,
- "package": runtimePackage,
- "version": runtimeVersion,
+ ocirt := define.OCIRuntimeInfo{
+ Name: r.name,
+ Path: r.path,
+ Package: runtimePackage,
+ Version: runtimeVersion,
}
-
- return info, nil
+ return &conmon, &ocirt, nil
}
// makeAccessible changes the path permission and each parent directory to have --x--x--x
@@ -1397,9 +1427,9 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p
var logDriver string
switch ctr.LogDriver() {
- case JournaldLogging:
- logDriver = JournaldLogging
- case JSONLogging:
+ case define.JournaldLogging:
+ logDriver = define.JournaldLogging
+ case define.JSONLogging:
fallthrough
default: //nolint-stylecheck
// No case here should happen except JSONLogging, but keep this here in case the options are extended
@@ -1409,8 +1439,8 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p
// to get here, either a user would specify `--log-driver ""`, or this came from another place in libpod
// since the former case is obscure, and the latter case isn't an error, let's silently fallthrough
fallthrough
- case KubernetesLogging:
- logDriver = fmt.Sprintf("%s:%s", KubernetesLogging, logPath)
+ case define.KubernetesLogging:
+ logDriver = fmt.Sprintf("%s:%s", define.KubernetesLogging, logPath)
}
args = append(args, "-l", logDriver)
@@ -1717,13 +1747,16 @@ func httpAttachNonTerminalCopy(container *net.UnixConn, http *bufio.ReadWriter,
for {
numR, err := container.Read(buf)
if numR > 0 {
- headerBuf := []byte{0, 0, 0, 0}
+ var headerBuf []byte
+ // Subtract 1 because we strip the first byte (used for
+ // multiplexing by Conmon).
+ headerLen := uint32(numR - 1)
// Practically speaking, we could make this buf[0] - 1,
// but we need to validate it anyways...
switch buf[0] {
case AttachPipeStdin:
- headerBuf[0] = 0
+ headerBuf = makeHTTPAttachHeader(0, headerLen)
if !stdin {
continue
}
@@ -1731,24 +1764,17 @@ func httpAttachNonTerminalCopy(container *net.UnixConn, http *bufio.ReadWriter,
if !stdout {
continue
}
- headerBuf[0] = 1
+ headerBuf = makeHTTPAttachHeader(1, headerLen)
case AttachPipeStderr:
if !stderr {
continue
}
- headerBuf[0] = 2
+ headerBuf = makeHTTPAttachHeader(2, headerLen)
default:
logrus.Errorf("Received unexpected attach type %+d, discarding %d bytes", buf[0], numR)
continue
}
- // Get big-endian length and append.
- // Subtract 1 because we strip the first byte (used for
- // multiplexing by Conmon).
- lenBuf := []byte{0, 0, 0, 0}
- binary.BigEndian.PutUint32(lenBuf, uint32(numR-1))
- headerBuf = append(headerBuf, lenBuf...)
-
numH, err2 := http.Write(headerBuf)
if err2 != nil {
if err != nil {
diff --git a/libpod/oci_conmon_unsupported.go b/libpod/oci_conmon_unsupported.go
index 395b6f6d9..309e0d417 100644
--- a/libpod/oci_conmon_unsupported.go
+++ b/libpod/oci_conmon_unsupported.go
@@ -17,7 +17,7 @@ type ConmonOCIRuntime struct {
}
// newConmonOCIRuntime is not supported on this OS.
-func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *config.Config, supportsJSON, supportsNoCgroups bool) (OCIRuntime, error) {
+func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *config.Config) (OCIRuntime, error) {
return nil, define.ErrNotImplemented
}
@@ -117,8 +117,8 @@ func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) {
}
// RuntimeInfo is not supported on this OS.
-func (r *ConmonOCIRuntime) RuntimeInfo() (map[string]interface{}, error) {
- return nil, define.ErrNotImplemented
+func (r *ConmonOCIRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
+ return nil, nil, define.ErrNotImplemented
}
// Package is not supported on this OS.
diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go
index a5d589255..172805b0d 100644
--- a/libpod/oci_missing.go
+++ b/libpod/oci_missing.go
@@ -168,6 +168,12 @@ func (r *MissingRuntime) SupportsNoCgroups() bool {
return false
}
+// SupportsKVM checks if the OCI runtime supports running containers
+// without KVM separation
+func (r *MissingRuntime) SupportsKVM() bool {
+ return false
+}
+
// AttachSocketPath does not work as there is no runtime to attach to.
// (Theoretically we could follow ExitFilePath but there is no guarantee the
// container is running and thus has an attach socket...)
@@ -195,15 +201,14 @@ func (r *MissingRuntime) ExitFilePath(ctr *Container) (string, error) {
}
// RuntimeInfo returns information on the missing runtime
-func (r *MissingRuntime) RuntimeInfo() (map[string]interface{}, error) {
- info := make(map[string]interface{})
- info["OCIRuntime"] = map[string]interface{}{
- "name": r.name,
- "path": "missing",
- "package": "missing",
- "version": "missing",
+func (r *MissingRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
+ ocirt := define.OCIRuntimeInfo{
+ Name: r.name,
+ Path: "missing",
+ Package: "missing",
+ Version: "missing",
}
- return info, nil
+ return nil, &ocirt, nil
}
// Return an error indicating the runtime is missing
diff --git a/libpod/options.go b/libpod/options.go
index dfbec364a..b4e436b63 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -985,7 +985,7 @@ func WithLogDriver(driver string) CtrCreateOption {
switch driver {
case "":
return errors.Wrapf(define.ErrInvalidArg, "log driver must be set")
- case JournaldLogging, KubernetesLogging, JSONLogging:
+ case define.JournaldLogging, define.KubernetesLogging, define.JSONLogging:
break
default:
return errors.Wrapf(define.ErrInvalidArg, "invalid log driver")
@@ -1334,7 +1334,7 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption {
}
destinations[vol.Dest] = true
- mountOpts, err := util.ProcessOptions(vol.Options, false, nil)
+ mountOpts, err := util.ProcessOptions(vol.Options, false, "")
if err != nil {
return errors.Wrapf(err, "error processing options for named volume %q mounted at %q", vol.Name, vol.Dest)
}
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index 200732652..ed4dc0727 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -431,9 +431,9 @@ func containerStatusFromContainers(allCtrs []*Container) (map[string]define.Cont
}
// Inspect returns a PodInspect struct to describe the pod
-func (p *Pod) Inspect() (*PodInspect, error) {
+func (p *Pod) Inspect() (*define.InspectPodData, error) {
var (
- podContainers []PodContainerInfo
+ ctrs []define.InspectPodContainerInfo
)
p.lock.Lock()
@@ -444,14 +444,6 @@ func (p *Pod) Inspect() (*PodInspect, error) {
containers, err := p.runtime.state.PodContainers(p)
if err != nil {
- return &PodInspect{}, err
- }
- ctrStatuses, err := containerStatusFromContainers(containers)
- if err != nil {
- return nil, err
- }
- status, err := CreatePodStatusResults(ctrStatuses)
- if err != nil {
return nil, err
}
for _, c := range containers {
@@ -462,26 +454,29 @@ func (p *Pod) Inspect() (*PodInspect, error) {
if err == nil {
containerStatus = containerState.String()
}
- pc := PodContainerInfo{
+ ctrs = append(ctrs, define.InspectPodContainerInfo{
ID: c.ID(),
+ Name: c.Name(),
State: containerStatus,
- }
- podContainers = append(podContainers, pc)
+ })
+ }
+ inspectData := define.InspectPodData{
+ ID: p.ID(),
+ Name: p.Name(),
+ Namespace: p.Namespace(),
+ Created: p.CreatedTime(),
+ Hostname: "",
+ Labels: p.Labels(),
+ CreateCgroup: false,
+ CgroupParent: p.CgroupParent(),
+ CgroupPath: p.state.CgroupPath,
+ CreateInfra: false,
+ InfraContainerID: p.state.InfraContainerID,
+ InfraConfig: nil,
+ SharedNamespaces: nil,
+ NumContainers: uint(len(containers)),
+ Containers: ctrs,
}
- infraContainerID := p.state.InfraContainerID
- config := new(PodConfig)
- if err := JSONDeepCopy(p.config, config); err != nil {
- return nil, err
- }
- inspectData := PodInspect{
- Config: config,
- State: &PodInspectState{
- CgroupPath: p.state.CgroupPath,
- InfraContainerID: infraContainerID,
- Status: status,
- },
- Containers: podContainers,
- }
return &inspectData, nil
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 422b79359..3b8f9e057 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -9,16 +9,14 @@ import (
"sync"
"syscall"
+ "github.com/containers/common/pkg/config"
is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/types"
-
- "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/lock"
"github.com/containers/libpod/pkg/cgroups"
- sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
@@ -361,25 +359,13 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
}
}
- // Make lookup tables for runtime support
- supportsJSON := make(map[string]bool)
- supportsNoCgroups := make(map[string]bool)
- for _, r := range runtime.config.Engine.RuntimeSupportsJSON {
- supportsJSON[r] = true
- }
- for _, r := range runtime.config.Engine.RuntimeSupportsNoCgroups {
- supportsNoCgroups[r] = true
- }
-
// Get us at least one working OCI runtime.
runtime.ociRuntimes = make(map[string]OCIRuntime)
// Initialize remaining OCI runtimes
for name, paths := range runtime.config.Engine.OCIRuntimes {
- json := supportsJSON[name]
- nocgroups := supportsNoCgroups[name]
- ociRuntime, err := newConmonOCIRuntime(name, paths, runtime.conmonPath, runtime.config, json, nocgroups)
+ ociRuntime, err := newConmonOCIRuntime(name, paths, runtime.conmonPath, runtime.config)
if err != nil {
// Don't fatally error.
// This will allow us to ship configs including optional
@@ -399,10 +385,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
if strings.HasPrefix(runtime.config.Engine.OCIRuntime, "/") {
name := filepath.Base(runtime.config.Engine.OCIRuntime)
- json := supportsJSON[name]
- nocgroups := supportsNoCgroups[name]
-
- ociRuntime, err := newConmonOCIRuntime(name, []string{runtime.config.Engine.OCIRuntime}, runtime.conmonPath, runtime.config, json, nocgroups)
+ ociRuntime, err := newConmonOCIRuntime(name, []string{runtime.config.Engine.OCIRuntime}, runtime.conmonPath, runtime.config)
if err != nil {
return err
}
@@ -675,40 +658,8 @@ func (r *Runtime) refresh(alivePath string) error {
}
// Info returns the store and host information
-func (r *Runtime) Info() ([]define.InfoData, error) {
- info := []define.InfoData{}
- // get host information
- hostInfo, err := r.hostInfo()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting host info")
- }
- info = append(info, define.InfoData{Type: "host", Data: hostInfo})
-
- // get store information
- storeInfo, err := r.storeInfo()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting store info")
- }
- info = append(info, define.InfoData{Type: "store", Data: storeInfo})
-
- registries := make(map[string]interface{})
- data, err := sysreg.GetRegistriesData()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting registries")
- }
- for _, reg := range data {
- registries[reg.Prefix] = reg
- }
- regs, err := sysreg.GetRegistries()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting registries")
- }
- if len(regs) > 0 {
- registries["search"] = regs
- }
-
- info = append(info, define.InfoData{Type: "registries", Data: registries})
- return info, nil
+func (r *Runtime) Info() (*define.Info, error) {
+ return r.info()
}
// generateName generates a unique name for a container or pod.
@@ -847,3 +798,7 @@ func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) error {
}
return nil
}
+
+func (r *Runtime) EnableLabeling() bool {
+ return r.config.Containers.EnableLabeling
+}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 207ac6477..3dc8d3d0f 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -321,7 +321,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
ctrNamedVolumes = append(ctrNamedVolumes, newVol)
}
- if ctr.config.LogPath == "" && ctr.config.LogDriver != JournaldLogging {
+ if ctr.config.LogPath == "" && ctr.config.LogDriver != define.JournaldLogging {
ctr.config.LogPath = filepath.Join(ctr.config.StaticDir, "ctr.log")
}
@@ -887,8 +887,9 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int
continue
}
err = r.RemoveContainer(context.Background(), ctr, false, false)
- pruneErrors[ctr.ID()] = err
if err != nil {
+ pruneErrors[ctr.ID()] = err
+ } else {
prunedContainers[ctr.ID()] = size
}
}
diff --git a/libpod/storage.go b/libpod/storage.go
index d675f4ffe..34e40f699 100644
--- a/libpod/storage.go
+++ b/libpod/storage.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod/define"
"github.com/containers/storage"
+ "github.com/containers/storage/pkg/idtools"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
@@ -35,6 +36,8 @@ type ContainerInfo struct {
Config *v1.Image
ProcessLabel string
MountLabel string
+ UIDMap []idtools.IDMap
+ GIDMap []idtools.IDMap
}
// RuntimeContainerMetadata is the structure that we encode as JSON and store
@@ -166,6 +169,8 @@ func (r *storageService) CreateContainerStorage(ctx context.Context, systemConte
logrus.Debugf("container %q has run directory %q", container.ID, containerRunDir)
return ContainerInfo{
+ UIDMap: options.UIDMap,
+ GIDMap: options.GIDMap,
Dir: containerDir,
RunDir: containerRunDir,
Config: imageConfig,
diff --git a/libpod/util.go b/libpod/util.go
index e9d234bbe..6457dac1c 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -2,6 +2,7 @@ package libpod
import (
"bufio"
+ "encoding/binary"
"fmt"
"io"
"os"
@@ -239,11 +240,22 @@ func checkDependencyContainer(depCtr, ctr *Container) error {
// hijackWriteErrorAndClose writes an error to a hijacked HTTP session and
// closes it. Intended to HTTPAttach function.
// If error is nil, it will not be written; we'll only close the connection.
-func hijackWriteErrorAndClose(toWrite error, cid string, httpCon io.Closer, httpBuf *bufio.ReadWriter) {
+func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon io.Closer, httpBuf *bufio.ReadWriter) {
if toWrite != nil {
- if _, err := httpBuf.Write([]byte(toWrite.Error())); err != nil {
- logrus.Errorf("Error writing error %q to container %s HTTP attach connection: %v", toWrite, cid, err)
- } else if err := httpBuf.Flush(); err != nil {
+ errString := []byte(fmt.Sprintf("%v\n", toWrite))
+ if !terminal {
+ // We need a header.
+ header := makeHTTPAttachHeader(2, uint32(len(errString)))
+ if _, err := httpBuf.Write(header); err != nil {
+ logrus.Errorf("Error writing header for container %s attach connection error: %v", cid, err)
+ }
+ // TODO: May want to return immediately here to avoid
+ // writing garbage to the socket?
+ }
+ if _, err := httpBuf.Write(errString); err != nil {
+ logrus.Errorf("Error writing error to container %s HTTP attach connection: %v", cid, err)
+ }
+ if err := httpBuf.Flush(); err != nil {
logrus.Errorf("Error flushing HTTP buffer for container %s HTTP attach connection: %v", cid, err)
}
}
@@ -252,3 +264,14 @@ func hijackWriteErrorAndClose(toWrite error, cid string, httpCon io.Closer, http
logrus.Errorf("Error closing container %s HTTP attach connection: %v", cid, err)
}
}
+
+// makeHTTPAttachHeader makes an 8-byte HTTP header for a buffer of the given
+// length and stream. Accepts an integer indicating which stream we are sending
+// to (STDIN = 0, STDOUT = 1, STDERR = 2).
+func makeHTTPAttachHeader(stream byte, length uint32) []byte {
+ headerBuf := []byte{stream, 0, 0, 0}
+ lenBuf := []byte{0, 0, 0, 0}
+ binary.BigEndian.PutUint32(lenBuf, length)
+ headerBuf = append(headerBuf, lenBuf...)
+ return headerBuf
+}