summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_log_linux.go38
-rw-r--r--libpod/define/errors.go3
-rw-r--r--libpod/define/info.go51
-rw-r--r--libpod/logs/log.go30
-rw-r--r--libpod/networking_linux.go22
5 files changed, 95 insertions, 49 deletions
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 4a541b6e7..ec4fa9724 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -8,12 +8,12 @@ import (
"fmt"
"io"
"math"
+ "strings"
"time"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/logs"
journal "github.com/coreos/go-systemd/v22/sdjournal"
- "github.com/hpcloud/tail/watch"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -89,21 +89,19 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
}
}()
go func() {
- for {
- state, err := c.State()
- if err != nil {
- until <- time.Time{}
- logrus.Error(err)
- break
- }
- time.Sleep(watch.POLL_DURATION)
- if state != define.ContainerStateRunning && state != define.ContainerStatePaused {
- until <- time.Time{}
- break
- }
- }
+ // FIXME (#10323): we are facing a terrible
+ // race condition here. At the time the
+ // container dies and `c.Wait()` has returned,
+ // we may not have received all journald logs.
+ // So far there is no other way than waiting
+ // for a second. Ultimately, `r.Follow` is
+ // racy and we may have to implement our custom
+ // logic here.
+ c.Wait(ctx)
+ time.Sleep(time.Second)
+ until <- time.Time{}
}()
- follower := FollowBuffer{logChannel}
+ follower := journaldFollowBuffer{logChannel, options.Multi}
err := r.Follow(until, follower)
if err != nil {
logrus.Debugf(err.Error())
@@ -124,7 +122,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
// because we are reusing bytes, we need to make
// sure the old data doesn't get into the new line
bytestr := string(bytes[:ec])
- logLine, err2 := logs.NewLogLine(bytestr)
+ logLine, err2 := logs.NewJournaldLogLine(bytestr, options.Multi)
if err2 != nil {
logrus.Error(err2)
continue
@@ -210,16 +208,18 @@ func formatterMessage(entry *journal.JournalEntry) (string, error) {
if !ok {
return "", fmt.Errorf("no MESSAGE field present in journal entry")
}
+ msg = strings.TrimSuffix(msg, "\n")
return msg, nil
}
-type FollowBuffer struct {
+type journaldFollowBuffer struct {
logChannel chan *logs.LogLine
+ withID bool
}
-func (f FollowBuffer) Write(p []byte) (int, error) {
+func (f journaldFollowBuffer) Write(p []byte) (int, error) {
bytestr := string(p)
- logLine, err := logs.NewLogLine(bytestr)
+ logLine, err := logs.NewJournaldLogLine(bytestr, f.withID)
if err != nil {
return -1, err
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 64c652eec..81bf5f69c 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -179,6 +179,9 @@ var (
// ErrNoNetwork indicates that a container has no net namespace, like network=none
ErrNoNetwork = errors.New("container has no network namespace")
+ // ErrNetworkModeInvalid indicates that a container has the wrong network mode for an operation
+ ErrNetworkModeInvalid = errors.New("invalid network mode")
+
// ErrSetSecurityAttribute indicates that a request to set a container's security attribute
// was not possible.
ErrSetSecurityAttribute = fmt.Errorf("%w: unable to assign security attribute", ErrOCIRuntime)
diff --git a/libpod/define/info.go b/libpod/define/info.go
index 87935be2d..c9d6877c0 100644
--- a/libpod/define/info.go
+++ b/libpod/define/info.go
@@ -21,31 +21,34 @@ type SecurityInfo struct {
SELinuxEnabled bool `json:"selinuxEnabled"`
}
-//HostInfo describes the libpod host
+// HostInfo describes the libpod host
type HostInfo struct {
- Arch string `json:"arch"`
- BuildahVersion string `json:"buildahVersion"`
- CgroupManager string `json:"cgroupManager"`
- 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"`
- RemoteSocket *RemoteSocket `json:"remoteSocket,omitempty"`
- RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"`
- Security SecurityInfo `json:"security"`
- Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"`
- SwapFree int64 `json:"swapFree"`
- SwapTotal int64 `json:"swapTotal"`
- Uptime string `json:"uptime"`
- Linkmode string `json:"linkmode"`
+ Arch string `json:"arch"`
+ BuildahVersion string `json:"buildahVersion"`
+ CgroupManager string `json:"cgroupManager"`
+ 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"`
+ // RemoteSocket returns the UNIX domain socket the Podman service is listening on
+ RemoteSocket *RemoteSocket `json:"remoteSocket,omitempty"`
+ RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"`
+ // ServiceIsRemote is true when the podman/libpod service is remote to the client
+ ServiceIsRemote bool `json:"serviceIsRemote"`
+ Security SecurityInfo `json:"security"`
+ Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"`
+ SwapFree int64 `json:"swapFree"`
+ SwapTotal int64 `json:"swapTotal"`
+ Uptime string `json:"uptime"`
+ Linkmode string `json:"linkmode"`
}
// RemoteSocket describes information about the API socket
diff --git a/libpod/logs/log.go b/libpod/logs/log.go
index bba52408d..308053b47 100644
--- a/libpod/logs/log.go
+++ b/libpod/logs/log.go
@@ -206,6 +206,36 @@ func NewLogLine(line string) (*LogLine, error) {
return &l, nil
}
+// NewJournaldLogLine creates a LogLine from the specified line from journald.
+// Note that if withID is set, the first item of the message is considerred to
+// be the container ID and set as such.
+func NewJournaldLogLine(line string, withID bool) (*LogLine, error) {
+ splitLine := strings.Split(line, " ")
+ if len(splitLine) < 4 {
+ return nil, errors.Errorf("'%s' is not a valid container log line", line)
+ }
+ logTime, err := time.Parse(LogTimeFormat, splitLine[0])
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to convert time %s from container log", splitLine[0])
+ }
+ var msg, id string
+ if withID {
+ id = splitLine[3]
+ msg = strings.Join(splitLine[4:], " ")
+ } else {
+ msg = strings.Join(splitLine[3:], " ")
+ // NO ID
+ }
+ l := LogLine{
+ Time: logTime,
+ Device: splitLine[1],
+ ParseLogType: splitLine[2],
+ Msg: msg,
+ CID: id,
+ }
+ return &l, nil
+}
+
// Partial returns a bool if the log line is a partial log type
func (l *LogLine) Partial() bool {
return l.ParseLogType == PartialLogType
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 1e763dac5..0e8a4f768 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -23,6 +23,7 @@ import (
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/libpod/network"
"github.com/containers/podman/v3/pkg/errorhandling"
+ "github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/netns"
"github.com/containers/podman/v3/pkg/resolvconf"
"github.com/containers/podman/v3/pkg/rootless"
@@ -758,6 +759,15 @@ func getContainerNetNS(ctr *Container) (string, error) {
return "", nil
}
+// isBridgeNetMode checks if the given network mode is bridge.
+// It returns nil when it is set to bridge and an error otherwise.
+func isBridgeNetMode(n namespaces.NetworkMode) error {
+ if !n.IsBridge() {
+ return errors.Wrapf(define.ErrNetworkModeInvalid, "%q is not supported", n)
+ }
+ return nil
+}
+
// Reload only works with containers with a configured network.
// It will tear down, and then reconfigure, the network of the container.
// This is mainly used when a reload of firewall rules wipes out existing
@@ -771,8 +781,8 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, er
if ctr.state.NetNS == nil {
return nil, errors.Wrapf(define.ErrCtrStateInvalid, "container %s network is not configured, refusing to reload", ctr.ID())
}
- if rootless.IsRootless() || ctr.config.NetMode.IsSlirp4netns() {
- return nil, errors.Wrapf(define.ErrRootless, "network reload only supported for root containers")
+ if err := isBridgeNetMode(ctr.config.NetMode); err != nil {
+ return nil, err
}
logrus.Infof("Going to reload container %s network", ctr.ID())
@@ -1026,8 +1036,8 @@ func (w *logrusDebugWriter) Write(p []byte) (int, error) {
// NetworkDisconnect removes a container from the network
func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) error {
// only the bridge mode supports cni networks
- if !c.config.NetMode.IsBridge() {
- return errors.Errorf("network mode %q is not supported", c.config.NetMode)
+ if err := isBridgeNetMode(c.config.NetMode); err != nil {
+ return err
}
networks, err := c.networksByNameIndex()
@@ -1087,8 +1097,8 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
// ConnectNetwork connects a container to a given network
func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) error {
// only the bridge mode supports cni networks
- if !c.config.NetMode.IsBridge() {
- return errors.Errorf("network mode %q is not supported", c.config.NetMode)
+ if err := isBridgeNetMode(c.config.NetMode); err != nil {
+ return err
}
networks, err := c.networksByNameIndex()