Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

service: simplify Logs() & improve docs #888

Merged
merged 4 commits into from
Apr 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions service/dependency.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package service

import (
"io"

"github.com/docker/docker/pkg/stdcopy"
"github.com/mesg-foundation/core/container"
"github.com/sirupsen/logrus"
)

// Dependency represents a Docker container and it holds instructions about
// how it should run.
type Dependency struct {
Expand Down Expand Up @@ -35,22 +27,3 @@ type Dependency struct {
// Env is a slice of environment variables in key=value format.
Env []string `hash:"name:8"`
}

// Logs gives the dependency logs. rstd stands for standard logs and rerr stands for
// error logs.
func (d *Dependency) Logs(c container.Container, serviceNamespace []string) (rstd, rerr io.ReadCloser, err error) {
var reader io.ReadCloser
reader, err = c.ServiceLogs(d.namespace(serviceNamespace))
if err != nil {
return nil, nil, err
}
sr, sw := io.Pipe()
er, ew := io.Pipe()
go func() {
if _, err := stdcopy.StdCopy(sw, ew, reader); err != nil {
reader.Close()
logrus.Errorln(err)
}
}()
return sr, er, nil
}
74 changes: 0 additions & 74 deletions service/dependency_test.go
Original file line number Diff line number Diff line change
@@ -1,75 +1 @@
package service

import (
"io"
"sync"
"testing"

"github.com/docker/docker/pkg/stdcopy"
"github.com/mesg-foundation/core/container"
"github.com/mesg-foundation/core/container/mocks"
"github.com/stretchr/testify/require"
)

func TestDependencyLogs(t *testing.T) {
testDependencyLogs(t, func(s *Service, c container.Container, dependencyKey string) (rstd, rerr io.ReadCloser,
err error) {
dep, err := s.getDependency(dependencyKey)
require.NoError(t, err)
return dep.Logs(c, s.namespace())
})
}

func testDependencyLogs(t *testing.T, do func(s *Service, c container.Container, dependencyKey string) (rstd, rerr io.ReadCloser,
err error)) {
var (
dependencyKey = "1"
stdData = []byte{1, 2}
errData = []byte{3, 4}
)

rp, wp := io.Pipe()
wstd := stdcopy.NewStdWriter(wp, stdcopy.Stdout)
werr := stdcopy.NewStdWriter(wp, stdcopy.Stderr)

go wstd.Write(stdData)
go werr.Write(errData)

var (
s = &Service{
Hash: "1",
Dependencies: []*Dependency{
{Key: dependencyKey},
},
}
mc = &mocks.Container{}
)

d, _ := s.getDependency(dependencyKey)
mc.On("ServiceLogs", d.namespace(s.namespace())).Once().Return(rp, nil)

rstd, rerr, err := do(s, mc, dependencyKey)
require.NoError(t, err)

var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
buf := make([]byte, 2)
_, err := rstd.Read(buf)
require.NoError(t, err)
require.Equal(t, stdData, buf)
}()

go func() {
defer wg.Done()
buf := make([]byte, 2)
_, err = rerr.Read(buf)
require.NoError(t, err)
require.Equal(t, errData, buf)
}()

wg.Wait()
mc.AssertExpectations(t)
}
42 changes: 24 additions & 18 deletions service/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package service
import (
"io"

"github.com/docker/docker/pkg/stdcopy"
"github.com/mesg-foundation/core/container"
"github.com/mesg-foundation/core/x/xstrings"
"github.com/sirupsen/logrus"
)

// Log holds log streams of dependency.
Expand All @@ -14,36 +16,40 @@ type Log struct {
Error io.ReadCloser
}

// Logs gives service's logs and applies dependencies filter to filter logs.
// if dependencies has a length of zero all dependency logs will be provided.
// Logs gives service's logs streams. when dependencies filter is not provided, it'll give
// logs for all dependencies otherwise it'll only give logs for specified dependencies.
// note that, service itself is also a dependency defined with special "service" key.
// in order to get service's own logs, "service" key must be included to dependencies filter.
func (s *Service) Logs(c container.Container, dependencies ...string) ([]*Log, error) {
var (
logs []*Log
isNoFilter = len(dependencies) == 0
)
addLog := func(dep *Dependency, name string) error {
if isNoFilter || xstrings.SliceContains(dependencies, name) {
rstd, rerr, err := dep.Logs(c, s.namespace())
for _, d := range append(s.Dependencies, s.Configuration) {
// Service.Configuration can be nil so, here is a check for it.
if d == nil {
continue
}
if isNoFilter || xstrings.SliceContains(dependencies, d.Key) {
var r io.ReadCloser
r, err := c.ServiceLogs(d.namespace(s.namespace()))
if err != nil {
return err
return nil, err
}
rstd, sw := io.Pipe()
rerr, ew := io.Pipe()
go func(dstout, dsterr io.Writer, r io.ReadCloser) {
if _, err := stdcopy.StdCopy(dstout, dsterr, r); err != nil {
r.Close()
logrus.Errorln(err)
}
}(sw, ew, r)
logs = append(logs, &Log{
Dependency: name,
Dependency: d.Key,
Standard: rstd,
Error: rerr,
})
}
return nil
}
if s.Configuration != nil {
if err := addLog(s.Configuration, MainServiceKey); err != nil {
return nil, err
}
}
for _, dep := range s.Dependencies {
if err := addLog(dep, dep.Key); err != nil {
return nil, err
}
}
return logs, nil
}
61 changes: 54 additions & 7 deletions service/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,65 @@ package service

import (
"io"
"sync"
"testing"

"github.com/mesg-foundation/core/container"
"github.com/docker/docker/pkg/stdcopy"
"github.com/mesg-foundation/core/container/mocks"
"github.com/stretchr/testify/require"
)

func TestServiceLogs(t *testing.T) {
testDependencyLogs(t, func(s *Service, c container.Container, dependencyKey string) (rstd, rerr io.ReadCloser,
err error) {
l, err := s.Logs(c, dependencyKey)
var (
dependencyKey = "1"
stdData = []byte{1, 2}
errData = []byte{3, 4}
)

rp, wp := io.Pipe()
wstd := stdcopy.NewStdWriter(wp, stdcopy.Stdout)
werr := stdcopy.NewStdWriter(wp, stdcopy.Stderr)

go wstd.Write(stdData)
go werr.Write(errData)

var (
s = &Service{
Hash: "1",
Dependencies: []*Dependency{
{Key: dependencyKey},
},
}
mc = &mocks.Container{}
)

d, _ := s.getDependency(dependencyKey)
mc.On("ServiceLogs", d.namespace(s.namespace())).Once().Return(rp, nil)

l, err := s.Logs(mc, dependencyKey)
require.NoError(t, err)
require.Len(t, l, 1)
rstd, rerr := l[0].Standard, l[0].Error

var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
buf := make([]byte, 2)
_, err := rstd.Read(buf)
require.NoError(t, err)
require.Len(t, l, 1)
return l[0].Standard, l[0].Error, nil
})
require.Equal(t, stdData, buf)
}()

go func() {
defer wg.Done()
buf := make([]byte, 2)
_, err = rerr.Read(buf)
require.NoError(t, err)
require.Equal(t, errData, buf)
}()

wg.Wait()
mc.AssertExpectations(t)
}