func (s *ServerTestSuite) TestServerDurability(c *C) { srv := testServer(c) c.Assert(srv.Start(), IsNil) defer srv.Shutdown() cl := testClient(c, srv) conn, err := net.Dial("tcp", srv.SyslogAddr().String()) c.Assert(err, IsNil) defer conn.Close() zero := 0 rc, err := cl.GetLog("app-A", &logagg.LogOpts{Follow: true, Lines: &zero}) c.Assert(err, IsNil) for _, msg := range appAMessages { conn.Write(rfc6587.Bytes(msg)) } var got client.Message dec := json.NewDecoder(rc) for _, want := range appAMessages { c.Assert(dec.Decode(&got), IsNil) c.Assert(got.HostID, Equals, string(want.Hostname)) c.Assert(got.Stream, Equals, utils.StreamType(want)) c.Assert(got.Timestamp.Equal(want.Timestamp), Equals, true) procType, jobID := splitProcID(want.ProcID) c.Assert(got.ProcessType, Equals, string(procType)) c.Assert(got.JobID, Equals, string(jobID)) } }
func filterStreamType(streams ...logagg.StreamType) filterFunc { lookup := make(map[logagg.StreamType]struct{}, len(streams)) for _, stream := range streams { lookup[stream] = struct{}{} } return func(m *rfc5424.Message) bool { _, ok := lookup[utils.StreamType(m)] return ok } }
func NewMessageFromSyslog(m *rfc5424.Message) client.Message { processType, jobID := splitProcID(m.ProcID) return client.Message{ HostID: string(m.Hostname), JobID: string(jobID), Msg: string(m.Msg), ProcessType: string(processType), // TODO(bgentry): source is always "app" for now, could be router in future Source: "app", Stream: utils.StreamType(m), Timestamp: m.Timestamp, } }
func (l *LibcontainerBackend) Attach(req *AttachRequest) (err error) { client, err := l.getContainer(req.Job.Job.ID) if err != nil { if req.Job.Job.Config.TTY || req.Stdin != nil { return host.ErrJobNotRunning } // if the container has exited and logging was disabled, return EOF if req.Job.Job.Config.DisableLog { if req.Attached != nil { req.Attached <- struct{}{} } return io.EOF } } defer func() { if client != nil && (req.Job.Job.Config.TTY || req.Stream) && err == io.EOF { <-client.done job := l.State.GetJob(req.Job.Job.ID) if job.Status == host.StatusDone || job.Status == host.StatusCrashed { err = ExitError(*job.ExitStatus) return } err = errors.New(*job.Error) } }() if req.Job.Job.Config.TTY { pty, err := client.GetPtyMaster() if err != nil { return err } defer pty.Close() if err := term.SetWinsize(pty.Fd(), &term.Winsize{Height: req.Height, Width: req.Width}); err != nil { return err } if req.Attached != nil { req.Attached <- struct{}{} } done := make(chan struct{}, 2) if req.Stdin != nil { go func() { io.Copy(pty, req.Stdin) done <- struct{}{} }() } if req.Stdout != nil { go func() { io.Copy(req.Stdout, pty) done <- struct{}{} }() } <-done l.Logger.Info("one side of the TTY went away, stopping job", "fn", "attach", "job.id", req.Job.Job.ID) client.Stop() return io.EOF } if req.Stdin != nil { stdinPipe, err := client.GetStdin() if err != nil { return err } go func() { io.Copy(stdinPipe, req.Stdin) stdinPipe.Close() }() } if req.Job.Job.Config.DisableLog { stdout, stderr, initLog, err := client.GetStreams() if err != nil { return err } defer stdout.Close() defer stderr.Close() defer initLog.Close() if req.Attached != nil { req.Attached <- struct{}{} } var wg sync.WaitGroup cp := func(w io.Writer, r io.Reader) { if w == nil { w = ioutil.Discard } wg.Add(1) go func() { io.Copy(w, r) wg.Done() }() } cp(req.InitLog, initLog) cp(req.Stdout, stdout) cp(req.Stderr, stderr) wg.Wait() return io.EOF } if req.Attached != nil { req.Attached <- struct{}{} } ch := make(chan *rfc5424.Message) stream, err := l.LogMux.StreamLog(req.Job.Job.Metadata["flynn-controller.app"], req.Job.Job.ID, req.Logs, req.Stream, ch) if err != nil { return err } defer stream.Close() for msg := range ch { var w io.Writer switch logutils.StreamType(msg) { case logagg.StreamTypeStdout: w = req.Stdout case logagg.StreamTypeStderr: w = req.Stderr case logagg.StreamTypeInit: w = req.InitLog } if w == nil { continue } if _, err := w.Write(append(msg.Msg, '\n')); err != nil { return nil } } return io.EOF }