Browse Source

reuse ECS logConsumer to implement formatted compose log output

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 5 years ago
parent
commit
74de423cc3
5 changed files with 127 additions and 42 deletions
  1. 2 39
      ecs/logs.go
  2. 1 1
      formatter/colors.go
  3. 91 0
      formatter/logs.go
  4. 1 0
      local/backend.go
  5. 32 2
      local/compose.go

+ 2 - 39
ecs/logs.go

@@ -17,51 +17,14 @@
 package ecs
 
 import (
-	"bytes"
 	"context"
-	"fmt"
+	"github.com/docker/compose-cli/formatter"
 	"io"
-	"strconv"
-	"strings"
 )
 
 func (b *ecsAPIService) Logs(ctx context.Context, project string, w io.Writer) error {
-	consumer := logConsumer{
-		colors: map[string]colorFunc{},
-		width:  0,
-		writer: w,
-	}
+	consumer := formatter.NewLogConsumer(w)
 	err := b.aws.GetLogs(ctx, project, consumer.Log)
 	return err
 }
 
-func (l *logConsumer) Log(service, container, message string) {
-	cf, ok := l.colors[service]
-	if !ok {
-		cf = <-loop
-		l.colors[service] = cf
-		l.computeWidth()
-	}
-	prefix := fmt.Sprintf("%-"+strconv.Itoa(l.width)+"s |", service)
-
-	for _, line := range strings.Split(message, "\n") {
-		buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line))
-		l.writer.Write(buf.Bytes()) // nolint:errcheck
-	}
-}
-
-func (l *logConsumer) computeWidth() {
-	width := 0
-	for n := range l.colors {
-		if len(n) > width {
-			width = len(n)
-		}
-	}
-	l.width = width + 3
-}
-
-type logConsumer struct {
-	colors map[string]colorFunc
-	width  int
-	writer io.Writer
-}

+ 1 - 1
ecs/colors.go → formatter/colors.go

@@ -14,7 +14,7 @@
    limitations under the License.
 */
 
-package ecs
+package formatter
 
 import (
 	"fmt"

+ 91 - 0
formatter/logs.go

@@ -0,0 +1,91 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package formatter
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+)
+
+
+func NewLogConsumer(w io.Writer) LogConsumer {
+	return LogConsumer{
+		colors: map[string]colorFunc{},
+		width:  0,
+		writer: w,
+	}
+}
+
+func (l *LogConsumer) Log(service, container, message string) {
+	cf, ok := l.colors[service]
+	if !ok {
+		cf = <-loop
+		l.colors[service] = cf
+		l.computeWidth()
+	}
+	prefix := fmt.Sprintf("%-"+strconv.Itoa(l.width)+"s |", service)
+
+	for _, line := range strings.Split(message, "\n") {
+		buf := bytes.NewBufferString(fmt.Sprintf("%s %s\n", cf(prefix), line))
+		l.writer.Write(buf.Bytes()) // nolint:errcheck
+	}
+}
+
+func (l *LogConsumer) GetWriter(service, container string) io.Writer {
+	return splitBuffer{
+		service: service,
+		container: container,
+		consumer: l,
+	}
+}
+
+func (l *LogConsumer) computeWidth() {
+	width := 0
+	for n := range l.colors {
+		if len(n) > width {
+			width = len(n)
+		}
+	}
+	l.width = width + 3
+}
+
+type LogConsumer struct {
+	colors map[string]colorFunc
+	width  int
+	writer io.Writer
+}
+
+
+type splitBuffer struct {
+	service   string
+	container string
+	consumer  *LogConsumer
+}
+
+func (s splitBuffer) Write(b []byte) (n int, err error) {
+	split := bytes.Split(b, []byte{'\n'})
+	for _, line := range split {
+		if len(line) != 0 {
+			s.consumer.Log(s.service, s.container, string(line))
+		}
+	}
+	return len(b), nil
+}
+

+ 1 - 0
local/backend.go

@@ -72,3 +72,4 @@ func (s *local) ResourceService() resources.Service {
 	return nil
 }
 
+

+ 32 - 2
local/compose.go

@@ -25,6 +25,7 @@ import (
 	"github.com/compose-spec/compose-go/types"
 	"github.com/docker/compose-cli/api/compose"
 	"github.com/docker/compose-cli/api/containers"
+	"github.com/docker/compose-cli/formatter"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/filters"
@@ -43,7 +44,7 @@ import (
 
 func (s *local) Up(ctx context.Context, project *types.Project, detach bool) error {
 	for k, network := range project.Networks {
-		if !network.External.External {
+		if !network.External.External && network.Name != "" {
 			network.Name = fmt.Sprintf("%s_%s", project.Name, k)
 			project.Networks[k] = network
 		}
@@ -53,6 +54,17 @@ func (s *local) Up(ctx context.Context, project *types.Project, detach bool) err
 		}
 	}
 
+	for k, volume := range project.Volumes {
+		if !volume.External.External && volume.Name != "" {
+			volume.Name = fmt.Sprintf("%s_%s", project.Name, k)
+			project.Volumes[k] = volume
+		}
+		err := s.ensureVolume(ctx, volume)
+		if err != nil {
+			return err
+		}
+	}
+
 	for _, service := range project.Services {
 		containerConfig, hostConfig, networkingConfig, err := getContainerCreateOptions(project, service)
 		if err != nil {
@@ -104,11 +116,13 @@ func (s *local) Logs(ctx context.Context, projectName string, w io.Writer) error
 		return err
 	}
 	var wg sync.WaitGroup
+	consumer := formatter.NewLogConsumer(w)
 	for _, c := range list {
+		service := c.Labels["com.docker.compose.service"]
 		go func() {
 			s.containerService.Logs(ctx, c.ID, containers.LogsRequest{
 				Follow: true,
-				Writer: w,
+				Writer: consumer.GetWriter(service, c.ID),
 			})
 			wg.Done()
 		}()
@@ -418,3 +432,19 @@ func (s *local) connectContainerToNetwork(ctx context.Context, id string, servic
 	}
 	return nil
 }
+
+func (s *local) ensureVolume(ctx context.Context, volume types.VolumeConfig) error {
+	// TODO could identify volume by label vs name
+	_, err := s.volumeService.Inspect(ctx, volume.Name)
+	if err != nil {
+		if errdefs.IsNotFound(err) {
+			// TODO we miss support for driver_opts and labels
+			_, err := s.volumeService.Create(ctx, volume.Name, nil)
+			if err != nil {
+				return err
+			}
+		}
+		return err
+	}
+	return nil
+}