diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 14 | ||||
-rw-r--r-- | libpod/container_api.go | 98 | ||||
-rw-r--r-- | libpod/container_internal.go | 18 | ||||
-rw-r--r-- | libpod/container_log.go (renamed from libpod/container.log.go) | 3 | ||||
-rw-r--r-- | libpod/define/config.go | 9 | ||||
-rw-r--r-- | libpod/define/ctr_inspect.go (renamed from libpod/define/inspect.go) | 0 | ||||
-rw-r--r-- | libpod/define/info.go | 1 | ||||
-rw-r--r-- | libpod/define/pod_inspect.go | 97 | ||||
-rw-r--r-- | libpod/info.go | 5 | ||||
-rw-r--r-- | libpod/networking_linux.go | 14 | ||||
-rw-r--r-- | libpod/oci.go | 3 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 76 | ||||
-rw-r--r-- | libpod/oci_conmon_unsupported.go | 2 | ||||
-rw-r--r-- | libpod/oci_missing.go | 6 | ||||
-rw-r--r-- | libpod/options.go | 2 | ||||
-rw-r--r-- | libpod/pod_api.go | 49 | ||||
-rw-r--r-- | libpod/runtime.go | 23 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 5 | ||||
-rw-r--r-- | libpod/util.go | 31 |
19 files changed, 354 insertions, 102 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 55c79fa74..b31079b26 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -6,10 +6,13 @@ import ( "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" @@ -244,15 +247,28 @@ func (c *Container) Attach(streams *define.AttachStreams, keys string, resize <- // 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. @@ -260,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_internal.go b/libpod/container_internal.go index c930017a4..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" @@ -430,7 +431,22 @@ func (c *Container) setupStorage(ctx context.Context) error { c.config.IDMappings.UIDMap = containerInfo.UIDMap c.config.IDMappings.GIDMap = containerInfo.GIDMap - c.config.ProcessLabel = containerInfo.ProcessLabel + + processLabel := containerInfo.ProcessLabel + switch { + case c.ociRuntime.SupportsKVM(): + processLabel, err = util.SELinuxKVMLabel(processLabel) + if err != nil { + return err + } + case c.config.Systemd: + processLabel, err = util.SELinuxInitLabel(processLabel) + if err != nil { + return err + } + } + + c.config.ProcessLabel = processLabel c.config.MountLabel = containerInfo.MountLabel c.config.StaticDir = containerInfo.Dir c.state.RunDir = containerInfo.RunDir 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/define/config.go b/libpod/define/config.go index 10e00062a..17d764c65 100644 --- a/libpod/define/config.go +++ b/libpod/define/config.go @@ -57,3 +57,12 @@ type AttachStreams struct { // 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 index e9809c367..2516cad77 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -8,6 +8,7 @@ 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 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/info.go b/libpod/info.go index 3cc767be6..d7ed5bb16 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -26,6 +26,11 @@ import ( // 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 { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index c3a90f481..83344ebbe 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -101,7 +101,19 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re requestedMAC = ctr.config.StaticMAC } - podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC) + // 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() + } + + podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC) results, err := r.netPlugin.SetUpPod(podNetwork) if err != nil { diff --git a/libpod/oci.go b/libpod/oci.go index 6adf42497..9991c5625 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -103,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. diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index c20e3f0b4..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 { @@ -480,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 @@ -492,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") } @@ -547,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) @@ -971,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 { @@ -1405,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 @@ -1417,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) @@ -1725,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 } @@ -1739,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 1f9d89ff6..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 } diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index 5284fb4b7..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...) diff --git a/libpod/options.go b/libpod/options.go index 65a089131..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") 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 a6032ad23..3b8f9e057 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -359,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 @@ -397,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 } @@ -813,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/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 +} |