diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal.go | 4 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 17 | ||||
-rw-r--r-- | libpod/container_internal_unsupported.go | 4 | ||||
-rw-r--r-- | libpod/container_log.go | 208 | ||||
-rw-r--r-- | libpod/events.go | 2 | ||||
-rw-r--r-- | libpod/networking_linux.go | 5 | ||||
-rw-r--r-- | libpod/oci.go | 7 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 2 |
8 files changed, 246 insertions, 3 deletions
diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 872802016..ac2d65342 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1429,5 +1429,9 @@ func (c *Container) copyWithTarFromImage(src, dest string) error { } a := archive.NewDefaultArchiver() source := filepath.Join(mountpoint, src) + + if err = c.copyOwnerAndPerms(source, dest); err != nil { + return err + } return a.CopyWithTar(source, dest) } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index a7b4aed9f..2a7808bdf 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -982,3 +982,20 @@ func (c *Container) generatePasswd() (string, error) { } return passwdFile, nil } + +func (c *Container) copyOwnerAndPerms(source, dest string) error { + info, err := os.Stat(source) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return errors.Wrapf(err, "cannot stat `%s`", dest) + } + if err := os.Chmod(dest, info.Mode()); err != nil { + return errors.Wrapf(err, "cannot chmod `%s`", dest) + } + if err := os.Chown(dest, int(info.Sys().(*syscall.Stat_t).Uid), int(info.Sys().(*syscall.Stat_t).Gid)); err != nil { + return errors.Wrapf(err, "cannot chown `%s`", dest) + } + return nil +} diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go index 4af0cd56c..f707b350c 100644 --- a/libpod/container_internal_unsupported.go +++ b/libpod/container_internal_unsupported.go @@ -35,3 +35,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) error { return ErrNotImplemented } + +func (c *Container) copyOwnerAndPerms(source, dest string) error { + return nil +} diff --git a/libpod/container_log.go b/libpod/container_log.go new file mode 100644 index 000000000..7964e4022 --- /dev/null +++ b/libpod/container_log.go @@ -0,0 +1,208 @@ +package libpod + +import ( + "fmt" + "io/ioutil" + "strings" + "sync" + "time" + + "github.com/hpcloud/tail" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + // logTimeFormat is the time format used in the log. + // It is a modified version of RFC3339Nano that guarantees trailing + // zeroes are not trimmed, taken from + // https://github.com/golang/go/issues/19635 + logTimeFormat = "2006-01-02T15:04:05.000000000Z07:00" +) + +// LogOptions is the options you can use for logs +type LogOptions struct { + Details bool + Follow bool + Since time.Time + Tail uint64 + Timestamps bool + Multi bool + WaitGroup *sync.WaitGroup +} + +// LogLine describes the information for each line of a log +type LogLine struct { + Device string + ParseLogType string + Time time.Time + Msg string + CID string +} + +// Log is a runtime function that can read one or more container logs. +func (r *Runtime) Log(containers []*Container, options *LogOptions, logChannel chan *LogLine) error { + for _, ctr := range containers { + if err := ctr.ReadLog(options, logChannel); err != nil { + return err + } + } + return nil +} + +// ReadLog reads a containers log based on the input options and returns loglines over a channel +func (c *Container) ReadLog(options *LogOptions, logChannel chan *LogLine) error { + t, tailLog, err := getLogFile(c.LogPath(), options) + if err != nil { + return errors.Wrapf(err, "unable to read log file %s for %s ", c.ID(), c.LogPath()) + } + options.WaitGroup.Add(1) + if len(tailLog) > 0 { + for _, nll := range tailLog { + nll.CID = c.ID() + if nll.Since(options.Since) { + logChannel <- nll + } + } + } + + go func() { + var partial string + for line := range t.Lines { + nll, err := newLogLine(line.Text) + if err != nil { + logrus.Error(err) + continue + } + if nll.Partial() { + partial = partial + nll.Msg + continue + } else if !nll.Partial() && len(partial) > 1 { + nll.Msg = partial + partial = "" + } + nll.CID = c.ID() + if nll.Since(options.Since) { + logChannel <- nll + } + } + options.WaitGroup.Done() + }() + return nil +} + +// getLogFile returns an hp tail for a container given options +func getLogFile(path string, options *LogOptions) (*tail.Tail, []*LogLine, error) { + var ( + whence int + err error + logTail []*LogLine + ) + // whence 0=origin, 2=end + if options.Tail > 0 { + whence = 2 + logTail, err = getTailLog(path, int(options.Tail)) + if err != nil { + return nil, nil, err + } + } + seek := tail.SeekInfo{ + Offset: 0, + Whence: whence, + } + + t, err := tail.TailFile(path, tail.Config{Poll: true, Follow: options.Follow, Location: &seek, Logger: tail.DiscardingLogger}) + return t, logTail, err +} + +func getTailLog(path string, tail int) ([]*LogLine, error) { + var ( + tailLog []*LogLine + nlls []*LogLine + tailCounter int + partial string + ) + content, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + splitContent := strings.Split(string(content), "\n") + // We read the content in reverse and add each nll until we have the same + // number of F type messages as the desired tail + for i := len(splitContent) - 1; i >= 0; i-- { + if len(splitContent[i]) == 0 { + continue + } + nll, err := newLogLine(splitContent[i]) + if err != nil { + return nil, err + } + nlls = append(nlls, nll) + if !nll.Partial() { + tailCounter = tailCounter + 1 + } + if tailCounter == tail { + break + } + } + // Now we iterate the results and assemble partial messages to become full messages + for _, nll := range nlls { + if nll.Partial() { + partial = partial + nll.Msg + } else { + nll.Msg = nll.Msg + partial + tailLog = append(tailLog, nll) + partial = "" + } + } + return tailLog, nil +} + +// String converts a logline to a string for output given whether a detail +// bool is specified. +func (l *LogLine) String(options *LogOptions) string { + var out string + if options.Multi { + cid := l.CID + if len(cid) > 12 { + cid = cid[:12] + } + out = fmt.Sprintf("%s ", cid) + } + if options.Timestamps { + out = out + fmt.Sprintf("%s ", l.Time.Format(logTimeFormat)) + } + return out + l.Msg +} + +// Since returns a bool as to whether a log line occurred after a given time +func (l *LogLine) Since(since time.Time) bool { + return l.Time.After(since) +} + +// newLogLine creates a logLine struct from a container log string +func newLogLine(line string) (*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(time.RFC3339Nano, splitLine[0]) + if err != nil { + return nil, errors.Wrapf(err, "unable to convert time %s from container log", splitLine[0]) + } + l := LogLine{ + Time: logTime, + Device: splitLine[1], + ParseLogType: splitLine[2], + Msg: strings.Join(splitLine[3:], " "), + } + return &l, nil +} + +// Partial returns a bool if the log line is a partial log type +func (l *LogLine) Partial() bool { + if l.ParseLogType == "P" { + return true + } + return false +} diff --git a/libpod/events.go b/libpod/events.go index f09529a05..139600982 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -92,5 +92,5 @@ func (r *Runtime) getTail(fromStart, stream bool) (*tail.Tail, error) { seek.Whence = 0 reopen = false } - return tail.TailFile(r.config.EventsLogFilePath, tail.Config{ReOpen: reopen, Follow: stream, Location: &seek}) + return tail.TailFile(r.config.EventsLogFilePath, tail.Config{ReOpen: reopen, Follow: stream, Location: &seek, Logger: tail.DiscardingLogger}) } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index d8b0cffcb..2450bd6b1 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -215,9 +215,12 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { if pid != cmd.Process.Pid { continue } - if status.Exited() || status.Signaled() { + if status.Exited() { return errors.New("slirp4netns failed") } + if status.Signaled() { + return errors.New("slirp4netns killed by signal") + } continue } return errors.Wrapf(err, "failed to read from slirp4netns sync pipe") diff --git a/libpod/oci.go b/libpod/oci.go index 30360d289..69cff6d3c 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -183,6 +183,7 @@ func waitPidsStop(pids []int, timeout time.Duration) error { func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { var files []*os.File + notifySCTP := false for _, i := range ports { switch i.Protocol { case "udp": @@ -218,6 +219,12 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { } files = append(files, f) break + case "sctp": + if !notifySCTP { + notifySCTP = true + logrus.Warnf("port reservation for SCTP is not supported") + } + break default: return nil, fmt.Errorf("unknown protocol %s", i.Protocol) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index c6f119913..3b74a65dd 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -171,7 +171,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options .. }() if rootless.IsRootless() && ctr.config.ConmonPidFile == "" { - ctr.config.ConmonPidFile = filepath.Join(ctr.state.RunDir, "conmon.pid") + ctr.config.ConmonPidFile = filepath.Join(ctr.config.StaticDir, "conmon.pid") } // Go through the volume mounts and check for named volumes |