diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_log_linux.go | 38 | ||||
-rw-r--r-- | libpod/define/errors.go | 3 | ||||
-rw-r--r-- | libpod/define/info.go | 51 | ||||
-rw-r--r-- | libpod/logs/log.go | 30 | ||||
-rw-r--r-- | libpod/networking_linux.go | 22 |
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() |