From eae2c7f5ad0ce25022f97d7d68a0fad198bce01d Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Fri, 16 Aug 2019 20:53:28 -0400 Subject: [PATCH 1/4] allow filter.label to work with a service running in docker swarm. still listens to log events from individual containers --- glide.lock | 115 ++++++++++++++++++++++++++++++++++++++---------- glide.yaml | 1 + router/pump.go | 38 ++++++++++++++-- router/types.go | 7 ++- 4 files changed, 132 insertions(+), 29 deletions(-) diff --git a/glide.lock b/glide.lock index 97ee4fa9..d64bdae0 100644 --- a/glide.lock +++ b/glide.lock @@ -1,48 +1,117 @@ -hash: 83a577e65396190336bd5117580f29dbf983a3e2fdc5732a111ae56f229ed978 -updated: 2017-11-07T22:39:32.638917215-06:00 +hash: 28bbe4dbb607479fc1f84400e0d165ee8ecb96015fe493d1deb320df0c6d51ad +updated: 2019-08-16T20:52:00.354722402-04:00 imports: +- name: github.com/Azure/go-ansiterm + version: d6e3b3328b783f23731bc4d058875b0371ff8109 + subpackages: + - winterm +- name: github.com/containerd/containerd + version: 074b75907b1f6a753b26d24de93b3eeddb5d0d22 + subpackages: + - errdefs +- name: github.com/containerd/continuity + version: f2a389ac0a02ce21c09edd7344677a601970f41c + subpackages: + - pathdriver +- name: github.com/docker/distribution + version: 1fb7fffdb26687adf375a4433a58a0d66a13fede + subpackages: + - registry/api/errcode - name: github.com/docker/docker - version: ad969f1aa782478725a7f338cf963fa82f484609 + version: f18ad28874fe4bdc5dc61db366bb818797153909 subpackages: - - opts - - pkg/archive + - api/types/blkiodev + - api/types/container + - api/types/filters + - api/types/mount + - api/types/network + - api/types/registry + - api/types/strslice + - api/types/swarm + - api/types/swarm/runtime + - api/types/versions + - errdefs - pkg/fileutils - pkg/homedir - pkg/idtools - pkg/ioutils - pkg/longpath + - pkg/mount - pkg/pools - - pkg/promise - pkg/stdcopy - pkg/system -- name: github.com/docker/engine-api - version: 98348ad6f9c89bb10f31ac32cd1b12cbadd292b6 +- name: github.com/docker/go-connections + version: fd1b1942c4d55f7f210a8387e612dc6ffee78ff6 subpackages: - - types/filters - - types/versions + - nat - name: github.com/docker/go-units - version: f2d77a61e3c169b43402a0a1e84f06daf29b8190 + version: 519db1ee28dcc9fd2474ae59fca29a810482bfb1 - name: github.com/fsouza/go-dockerclient - version: 1a3d0cfd7814bbfe44ada7617654948c99891749 -- name: github.com/gorilla/context - version: aed02d124ae4a0e94fea4541c8effd05bf0c8296 + version: 308dc73c17d7c26a1d0f2ff0057637cc500d7c06 + subpackages: + - internal/archive + - internal/jsonmessage + - internal/term +- name: github.com/gogo/protobuf + version: 28a6bbf47e48e0b2220b2a244750b660c83d4942 + subpackages: + - proto +- name: github.com/golang/protobuf + version: 4c88cc3f1a34ffade77b79abc53335d1e511f25b + subpackages: + - proto + - ptypes + - ptypes/any + - ptypes/duration + - ptypes/timestamp - name: github.com/gorilla/mux - version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e -- name: github.com/hashicorp/go-cleanhttp - version: ad28ea4487f05916463e2423a55166280e8254b5 + version: e67b3c02c7195c052acff13261f0c9fd1ba53011 +- name: github.com/ijc/Gotty + version: a8b993ba6abdb0e0c12b0125c603323a71c7790c +- name: github.com/konsorten/go-windows-terminal-sequences + version: f55edac94c9bbba5d6182a4be46d86a2c9b5b50e +- name: github.com/Microsoft/go-winio + version: 6c72808b55902eae4c5943626030429ff20f3b63 + subpackages: + - pkg/guid +- name: github.com/Microsoft/hcsshim + version: f3a709278302553a13f3076369160103d274276c + subpackages: + - osversion +- name: github.com/opencontainers/go-digest + version: ac19fd6e7483ff933754af248d80be865e543d22 +- name: github.com/opencontainers/image-spec + version: da296dcb1e473a9b4e2d148941d7faa9ac8fea3f + subpackages: + - specs-go + - specs-go/v1 - name: github.com/opencontainers/runc - version: 9d7831e41d3ef428b67685eeb27f2b4a22a92391 + version: 2e94378464ae22b92e1335c200edb37ebc94a1b7 subpackages: - libcontainer/user -- name: github.com/Sirupsen/logrus - version: f3cfb454f4c209e6668c95216c4744b8fddb2356 +- name: github.com/pkg/errors + version: 27936f6d90f9c8e1145f11ed52ffffbfdb9e0af7 +- name: github.com/sirupsen/logrus + version: de736cf91b921d56253b4010270681d33fdf7cb5 - name: golang.org/x/net - version: f841c39de738b1d0df95b5a7187744f0e03d8112 + version: 74dc4d7220e7acc4e100824340f3e66577424772 subpackages: - - context - websocket - name: golang.org/x/sys - version: a408501be4d17ee978c04a618e7a1b22af058c0e + version: fde4db37ae7ad8191b03d30d27f258b5291ae4e3 subpackages: - unix + - windows +- name: google.golang.org/genproto + version: fa694d86fc64c7654a660f8908de4e879866748d + subpackages: + - googleapis/rpc/status +- name: google.golang.org/grpc + version: 030824531b6588b2f7fb381c9efa96c560693b83 + subpackages: + - codes + - connectivity + - grpclog + - internal + - status testImports: [] diff --git a/glide.yaml b/glide.yaml index d5370a9c..3f6ad4fd 100644 --- a/glide.yaml +++ b/glide.yaml @@ -2,6 +2,7 @@ package: github.com/gliderlabs/logspout excludeDirs: - custom import: +- package: github.com/docker/docker/api/types/swarm - package: github.com/fsouza/go-dockerclient - package: github.com/gorilla/mux - package: golang.org/x/net diff --git a/router/pump.go b/router/pump.go index 94e34786..b5ec4d55 100644 --- a/router/pump.go +++ b/router/pump.go @@ -6,10 +6,12 @@ import ( "io" "log" "os" + "strconv" "strings" "sync" "time" + "github.com/docker/docker/api/types/swarm" "github.com/fsouza/go-dockerclient" ) @@ -80,6 +82,7 @@ func logDriverSupported(container *docker.Container) bool { } func ignoreContainer(container *docker.Container) bool { + debug("Checking ignore container") for _, kv := range container.Config.Env { kvp := strings.SplitN(kv, "=", 2) if len(kvp) == 2 && kvp[0] == "LOGSPOUT" && strings.ToLower(kvp[1]) == "ignore" { @@ -190,6 +193,18 @@ func (p *LogsPump) Run() error { func (p *LogsPump) pumpLogs(event *docker.APIEvents, backlog bool, inactivityTimeout time.Duration) { id := normalID(event.ID) container, err := p.client.InspectContainer(id) + serviceID := container.Config.Labels["com.docker.swarm.service.id"] + debug("Service ID: " + serviceID) + var service *swarm.Service + if serviceID != "" { + svc, err := p.client.InspectService(serviceID) + if err != nil { + debug("Error getting service", err) + } else { + service = svc + } + } + debug("service is nil: " + strconv.FormatBool(service == nil)) assert(err, "pump") if ignoreContainerTTY(container) { debug("pump.pumpLogs():", id, "ignored: tty enabled") @@ -227,7 +242,7 @@ func (p *LogsPump) pumpLogs(event *docker.APIEvents, backlog bool, inactivityTim } outrd, outwr := io.Pipe() errrd, errwr := io.Pipe() - p.pumps[id] = newContainerPump(container, outrd, errrd) + p.pumps[id] = newContainerPump(container, service, outrd, errrd) p.mu.Unlock() p.update(event) go func() { @@ -305,10 +320,18 @@ func (p *LogsPump) RoutingFrom(id string) bool { func (p *LogsPump) Route(route *Route, logstream chan *Message) { p.mu.Lock() for _, pump := range p.pumps { + var serviceLabels map[string]string + if pump.service != nil { + serviceLabels = pump.service.Spec.Labels + for k, v := range serviceLabels { + debug(k + "=" + v) + } + } if route.MatchContainer( normalID(pump.container.ID), normalName(pump.container.Name), - pump.container.Config.Labels) { + pump.container.Config.Labels, + serviceLabels) { pump.add(logstream, route) defer pump.remove(logstream) @@ -328,10 +351,15 @@ func (p *LogsPump) Route(route *Route, logstream chan *Message) { case event := <-updates: switch event.Status { case "start", "restart": + var serviceLabels map[string]string + if event.pump.service != nil { + serviceLabels = event.pump.service.Spec.Labels + } if route.MatchContainer( normalID(event.pump.container.ID), normalName(event.pump.container.Name), - event.pump.container.Config.Labels) { + event.pump.container.Config.Labels, + serviceLabels) { event.pump.add(logstream, route) defer event.pump.remove(logstream) @@ -352,12 +380,14 @@ func (p *LogsPump) Route(route *Route, logstream chan *Message) { type containerPump struct { sync.Mutex container *docker.Container + service *swarm.Service logstreams map[chan *Message]*Route } -func newContainerPump(container *docker.Container, stdout, stderr io.Reader) *containerPump { +func newContainerPump(container *docker.Container, service *swarm.Service, stdout, stderr io.Reader) *containerPump { cp := &containerPump{ container: container, + service: service, logstreams: make(map[chan *Message]*Route), } pump := func(source string, input io.Reader) { diff --git a/router/types.go b/router/types.go index b17ec4c1..1ef5d18b 100644 --- a/router/types.go +++ b/router/types.go @@ -117,7 +117,7 @@ func (r *Route) MultiContainer() bool { } // MatchContainer returns whether the Route is responsible for a given container -func (r *Route) MatchContainer(id, name string, labels map[string]string) bool { +func (r *Route) MatchContainer(id, name string, containerLabels map[string]string, serviceLabels map[string]string) bool { if r.matchAll() { return true } @@ -128,12 +128,15 @@ func (r *Route) MatchContainer(id, name string, labels map[string]string) bool { if err != nil || (r.FilterName != "" && !match) { return false } + for k, v := range serviceLabels { + containerLabels[k] = v + } for _, label := range r.FilterLabels { labelParts := strings.SplitN(label, ":", 2) if len(labelParts) > 1 { labelKey := labelParts[0] labelValue := labelParts[1] - labelMatch, labelErr := path.Match(labelValue, labels[labelKey]) + labelMatch, labelErr := path.Match(labelValue, containerLabels[labelKey]) if labelErr != nil || (labelValue != "" && !labelMatch) { return false } From 1263b667e925901cc2c0805d948585a38774bf83 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Fri, 16 Aug 2019 21:30:05 -0400 Subject: [PATCH 2/4] check for errors before using container object. add support for service labels to ignorecontainer function --- router/pump.go | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/router/pump.go b/router/pump.go index b5ec4d55..27338adc 100644 --- a/router/pump.go +++ b/router/pump.go @@ -6,7 +6,6 @@ import ( "io" "log" "os" - "strconv" "strings" "sync" "time" @@ -81,8 +80,7 @@ func logDriverSupported(container *docker.Container) bool { } } -func ignoreContainer(container *docker.Container) bool { - debug("Checking ignore container") +func ignoreContainer(container *docker.Container, serviceLabels map[string]string) bool { for _, kv := range container.Config.Env { kvp := strings.SplitN(kv, "=", 2) if len(kvp) == 2 && kvp[0] == "LOGSPOUT" && strings.ToLower(kvp[1]) == "ignore" { @@ -99,7 +97,11 @@ func ignoreContainer(container *docker.Container) bool { excludeLabel = excludeLabelArr[0] } - if value, ok := container.Config.Labels[excludeLabel]; ok { + for k, v := range container.Config.Labels { + serviceLabels[k] = v + } + + if value, ok := serviceLabels[excludeLabel]; ok { return len(excludeLabel) > 0 && strings.ToLower(value) == strings.ToLower(excludeValue) } return false @@ -193,24 +195,27 @@ func (p *LogsPump) Run() error { func (p *LogsPump) pumpLogs(event *docker.APIEvents, backlog bool, inactivityTimeout time.Duration) { id := normalID(event.ID) container, err := p.client.InspectContainer(id) - serviceID := container.Config.Labels["com.docker.swarm.service.id"] - debug("Service ID: " + serviceID) var service *swarm.Service - if serviceID != "" { - svc, err := p.client.InspectService(serviceID) - if err != nil { - debug("Error getting service", err) - } else { - service = svc + serviceLabels := make(map[string]string) + if err == nil { + serviceID := container.Config.Labels["com.docker.swarm.service.id"] + debug("Service ID: " + serviceID) + if serviceID != "" { + svc, err := p.client.InspectService(serviceID) + if err != nil { + debug("Error getting service", err) + } else { + service = svc + serviceLabels = service.Spec.Labels + } } } - debug("service is nil: " + strconv.FormatBool(service == nil)) assert(err, "pump") if ignoreContainerTTY(container) { debug("pump.pumpLogs():", id, "ignored: tty enabled") return } - if ignoreContainer(container) { + if ignoreContainer(container, serviceLabels) { debug("pump.pumpLogs():", id, "ignored: environ ignore") return } @@ -323,9 +328,6 @@ func (p *LogsPump) Route(route *Route, logstream chan *Message) { var serviceLabels map[string]string if pump.service != nil { serviceLabels = pump.service.Spec.Labels - for k, v := range serviceLabels { - debug(k + "=" + v) - } } if route.MatchContainer( normalID(pump.container.ID), From 6254ba4bb030b3f281ef70f475ccc53a1c5b9b08 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Sat, 17 Aug 2019 21:59:34 -0400 Subject: [PATCH 3/4] strip binary --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 7aed13cb..90b58ef7 100755 --- a/build.sh +++ b/build.sh @@ -6,7 +6,7 @@ cp -r /src /go/src/github.com/gliderlabs/logspout cd /go/src/github.com/gliderlabs/logspout export GOPATH=/go go get github.com/Masterminds/glide && $GOPATH/bin/glide install -go build -ldflags "-X main.Version=$1" -o /bin/logspout +go build -ldflags "-s -w -X main.Version=$1" -o /bin/logspout apk del go git mercurial build-base rm -rf /go /var/cache/apk/* /root/.glide From 2a5969bfc007be209d2655b61ca0da83faa05fc8 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Sat, 17 Aug 2019 22:58:45 -0400 Subject: [PATCH 4/4] update tests --- router/pump_test.go | 48 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/router/pump_test.go b/router/pump_test.go index e05f9f99..10ea03de 100644 --- a/router/pump_test.go +++ b/router/pump_test.go @@ -10,6 +10,7 @@ import ( "os" "testing" + swarm "github.com/docker/docker/api/types/swarm" docker "github.com/fsouza/go-dockerclient" ) @@ -67,11 +68,25 @@ func TestPumpIgnoreContainer(t *testing.T) { {&docker.Config{Labels: map[string]string{"exclude": "false"}}, false}, } + serviceLabels := []struct { + in map[string]string + out bool + }{ + {map[string]string{"exclude": "true"}, true}, + {map[string]string{"exclude": "false"}, false}, + } + for _, conf := range containers { - if actual := ignoreContainer(&docker.Container{Config: conf.in}); actual != conf.out { + if actual := ignoreContainer(&docker.Container{Config: conf.in}, make(map[string]string)); actual != conf.out { t.Errorf("expected %v got %v", conf.out, actual) } } + + for _, serviceLabel := range serviceLabels { + if actual := ignoreContainer(&docker.Container{Config: &docker.Config{Labels: make(map[string]string)}}, serviceLabel.in); actual != serviceLabel.out { + t.Errorf("expected %v got %v", serviceLabel.out, actual) + } + } } func TestPumpIgnoreContainerCustomLabels(t *testing.T) { @@ -85,11 +100,25 @@ func TestPumpIgnoreContainerCustomLabels(t *testing.T) { {&docker.Config{Labels: map[string]string{"app": "demo-app"}}, false}, } + serviceLabels := []struct { + in map[string]string + out bool + }{ + {map[string]string{"k8s-app": "canal"}, true}, + {map[string]string{"app": "demo-app"}, false}, + } + for _, conf := range containers { - if actual := ignoreContainer(&docker.Container{Config: conf.in}); actual != conf.out { + if actual := ignoreContainer(&docker.Container{Config: conf.in}, make(map[string]string)); actual != conf.out { t.Errorf("expected %v got %v", conf.out, actual) } } + + for _, serviceLabel := range serviceLabels { + if actual := ignoreContainer(&docker.Container{Config: &docker.Config{Labels: make(map[string]string)}}, serviceLabel.in); actual != serviceLabel.out { + t.Errorf("expected %v got %v", serviceLabel.out, actual) + } + } } func TestPumpIgnoreContainerAllowTTYDefault(t *testing.T) { @@ -139,6 +168,9 @@ func TestPumpContainerRename(t *testing.T) { ID: "8dfafdbc3a40", Name: "bar", } + service := &swarm.Service{ + ID: "83nf93ndin2j", + } client := newTestClient(&FakeRoundTripper{message: container, status: http.StatusOK}) p := &LogsPump{ client: &client, @@ -153,7 +185,7 @@ func TestPumpContainerRename(t *testing.T) { Name: "foo", Config: config, } - p.pumps["8dfafdbc3a40"] = newContainerPump(container, os.Stdout, os.Stderr) + p.pumps["8dfafdbc3a40"] = newContainerPump(container, service, os.Stdout, os.Stderr) if name := p.pumps["8dfafdbc3a40"].container.Name; name != "foo" { t.Errorf("containerPump should have name: 'foo' got name: '%s'", name) } @@ -171,7 +203,10 @@ func TestPumpNewContainerPump(t *testing.T) { ID: "8dfafdbc3a40", Config: config, } - pump := newContainerPump(container, os.Stdout, os.Stderr) + service := &swarm.Service{ + ID: "83nf93ndin2j", + } + pump := newContainerPump(container, service, os.Stdout, os.Stderr) if pump == nil { t.Error("pump nil") return @@ -186,7 +221,10 @@ func TestPumpContainerPump(t *testing.T) { ID: "8dfafdbc3a40", Config: config, } - pump := newContainerPump(container, os.Stdout, os.Stderr) + service := &swarm.Service{ + ID: "83nf93ndin2j", + } + pump := newContainerPump(container, service, os.Stdout, os.Stderr) logstream, route := make(chan *Message), &Route{} go func() { for msg := range logstream {