Merge pull request #919 from docker/compose-in-go

This commit is contained in:
Nicolas De loof 2020-11-17 16:23:42 +01:00 committed by GitHub
commit bbcf445f1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1219 additions and 150 deletions

View File

@ -17,51 +17,14 @@
package ecs
import (
"bytes"
"context"
"fmt"
"io"
"strconv"
"strings"
"github.com/docker/compose-cli/formatter"
)
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
}

View File

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

92
formatter/logs.go Normal file
View File

@ -0,0 +1,92 @@
/*
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"
)
// NewLogConsumer creates a new LogConsumer
func NewLogConsumer(w io.Writer) LogConsumer {
return LogConsumer{
colors: map[string]colorFunc{},
width: 0,
writer: w,
}
}
// Log formats a log message as received from service/container
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
}
}
// GetWriter creates a io.Writer that will actually split by line and format by LogConsumer
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
}
// LogConsumer consume logs from services and format them
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
}

20
go.mod
View File

@ -6,6 +6,9 @@ go 1.15
// we need to create a new release tag for docker/distribution
replace github.com/docker/distribution => github.com/docker/distribution v0.0.0-20200708230824-53e18a9d9bfe
// unix.SYS_IOCTL has been removed from golang/sys but still in use by docker/docker until 20.x
replace golang.org/x/sys => golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6
require (
github.com/AlecAivazis/survey/v2 v2.1.1
github.com/Azure/azure-sdk-for-go v43.3.0+incompatible
@ -17,17 +20,18 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0
github.com/Azure/go-autorest/autorest/to v0.4.0
github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5
github.com/Microsoft/hcsshim v0.8.9 // indirect
github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab
github.com/Microsoft/hcsshim v0.8.10 // indirect
github.com/aws/aws-sdk-go v1.35.15
github.com/awslabs/goformation/v4 v4.15.3
github.com/awslabs/goformation/v4 v4.15.2
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
github.com/compose-spec/compose-go v0.0.0-20201116112017-777513ca88e2
github.com/containerd/console v1.0.1
github.com/containerd/console v1.0.0
github.com/containerd/containerd v1.3.5 // indirect
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect
github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8
github.com/docker/distribution v0.0.0-00010101000000-000000000000 // indirect
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/docker/go-connections v0.4.0
github.com/docker/go-units v0.4.0
@ -45,8 +49,10 @@ require (
github.com/joho/godotenv v1.3.0
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/gommon v0.3.0 // indirect
github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf
github.com/morikuni/aec v1.0.0
github.com/onsi/ginkgo v1.14.2 // indirect
github.com/onsi/gomega v1.10.2 // indirect
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/runc v0.1.1 // indirect
github.com/pkg/errors v0.9.1
@ -58,7 +64,7 @@ require (
github.com/stretchr/testify v1.6.1
github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/mod v0.3.0
golang.org/x/net v0.0.0-20201026091529-146b70c837a4
golang.org/x/net v0.0.0-20200822124328-c89045814202
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
google.golang.org/grpc v1.33.1

117
go.sum
View File

@ -1,3 +1,4 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@ -79,10 +80,10 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.9 h1:VrfodqvztU8YSOvygU+DN1BGaSGxmrNfqOv5oOuX2Bk=
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab h1:9pygWVFqbY9lPxM0peffumuVDyMuIMzNLyO9uFjJuQo=
github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/hcsshim v0.8.10 h1:k5wTrpnVU2/xv8ZuzGkbXVd3js5zJ8RnumPo5RxiIxU=
github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3HV0OhsddkmM=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@ -96,8 +97,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.35.15 h1:JdQNM8hJe+9N9xP53S54NDmX8GCaZn8CCJ4LBHfom4U=
github.com/aws/aws-sdk-go v1.35.15/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
github.com/awslabs/goformation/v4 v4.15.3 h1:zVRL+gAW5Xvyhd85KNsJpKaOuArxEyUYKK5+v13Bwrc=
github.com/awslabs/goformation/v4 v4.15.3/go.mod h1:wB5lKZf1J0MYH1Lt4B9w3opqz0uIjP7MMCAcib3QkwA=
github.com/awslabs/goformation/v4 v4.15.2 h1:sRfSdC1FnSBhsrz5G0XZZxapEtmJSlkNpnFQJf8ylfs=
github.com/awslabs/goformation/v4 v4.15.2/go.mod h1:GcJULxCJfloT+3pbqCluXftdEK2AD/UqpS3hkaaBntg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -120,36 +121,36 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/compose-spec/compose-go v0.0.0-20201116112017-777513ca88e2 h1:m6m0rnxmq3mPapGNAWqO6FLYg6GST7tJAAZqJ0pjadk=
github.com/compose-spec/compose-go v0.0.0-20201116112017-777513ca88e2/go.mod h1:cZJ8y+zQzrYfN5XnEu/kMeDEP0V2D9ojF7Rb4nrneUc=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/console v1.0.0 h1:fU3UuQapBs+zLJu82NhR11Rif1ny2zfMMAyPJzSN5tQ=
github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.5 h1:l0iDHQtFwcOUmOvdepI6BB67q7beT6sRp2JYsfHS08c=
github.com/containerd/containerd v1.3.5/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 h1:PUD50EuOMkXVcpBIA/R95d56duJR9VxhwncsFbNnxW4=
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a h1:jEIoR0aA5GogXZ8pP3DUzE+zrhaF6/1rYZy+7KkYEWM=
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a/go.mod h1:W0qIOTD7mp2He++YVq+kgfXezRYqzP1uDuMVH1bITDY=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de h1:dlfGmNcE3jDAecLqwKPMNX6nk2qh1c1Vg1/YTzpOOF4=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -165,8 +166,8 @@ github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8 h1:JRquW4uqIU+eSilDhuo9
github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20200708230824-53e18a9d9bfe h1:pni13lAFm1g4cjHU6c3n4qGvvJGZQK4VvKRKMseQ42E=
github.com/docker/distribution v0.0.0-20200708230824-53e18a9d9bfe/go.mod h1:Oqz4IonmMNc2N7GqfTL2xkhCQx0yS6nR+HrOZJnmKIk=
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible h1:G2hY8RD7jB9QaSmcb8mYEIg8QbEvVAB7se8+lXHZHfg=
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible h1:SiUATuP//KecDjpOK2tvZJgeScYAklvyjfK8JZlU6fo=
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@ -178,6 +179,7 @@ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHz
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -208,8 +210,7 @@ github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
@ -317,6 +318,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@ -343,6 +345,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@ -389,8 +393,8 @@ github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mjibson/esc v0.2.0/go.mod h1:9Hw9gxxfHulMF5OJKCyhYD7PzlSdhzXyaGEBRPH1OPs=
github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 h1:SPoLlS9qUUnXcIY4pvA4CTwYjk0Is5f4UPEkeESr53k=
github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ=
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf h1:Un6PNx5oMK6CCwO3QTUyPiK2mtZnPrpDl5UnZ64eCkw=
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@ -403,16 +407,18 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.5.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.2.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@ -422,8 +428,7 @@ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700 h1:eNUVfm/RFLIi1G7flU5/ZRTHvd4kcVuzfRnL6OFlzCI=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -472,6 +477,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
@ -485,11 +491,13 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@ -505,7 +513,7 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
@ -620,10 +628,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs=
golang.org/x/net v0.0.0-20201026091529-146b70c837a4/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -644,55 +648,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2By
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -20,7 +20,6 @@ package local
import (
"context"
"github.com/docker/docker/client"
"github.com/docker/compose-cli/api/compose"
@ -42,7 +41,7 @@ func init() {
}
func service(ctx context.Context) (backend.Service, error) {
apiClient, err := client.NewClientWithOpts(client.FromEnv)
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return nil, err
}
@ -53,22 +52,25 @@ func service(ctx context.Context) (backend.Service, error) {
}, nil
}
func (cs *containerService) ContainerService() containers.Service {
return cs
func (s *local) ContainerService() containers.Service {
return s.containerService
}
func (ms *local) ComposeService() compose.Service {
func (s *local) ComposeService() compose.Service {
return s
}
func (s *local) SecretsService() secrets.Service {
return nil
}
func (ms *local) SecretsService() secrets.Service {
func (s *local) VolumeService() volumes.Service {
return s.volumeService
}
func (s *local) ResourceService() resources.Service {
return nil
}
func (vs *volumeService) VolumeService() volumes.Service {
return vs
}
func (ms *local) ResourceService() resources.Service {
return nil
}

598
local/compose.go Normal file
View File

@ -0,0 +1,598 @@
// +build local
/*
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 local
import (
"context"
"encoding/json"
"fmt"
"golang.org/x/sync/errgroup"
"io"
"path/filepath"
"strconv"
"strings"
"sync"
"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"
"github.com/docker/compose-cli/progress"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"github.com/sanathkr/go-yaml"
)
func (s *local) Up(ctx context.Context, project *types.Project, detach bool) error {
for k, network := range project.Networks {
if !network.External.External && network.Name != "" {
network.Name = fmt.Sprintf("%s_%s", project.Name, k)
project.Networks[k] = network
}
err := s.ensureNetwork(ctx, network)
if err != nil {
return 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 {
err := s.applyPullPolicy(ctx, service)
if err != nil {
return err
}
}
err := inDependencyOrder(ctx, project, func(service types.ServiceConfig) error {
return s.ensureService(ctx, project, service)
})
return err
}
func getContainerName(c moby.Container) string {
// Names return container canonical name /foo + link aliases /linked_by/foo
for _, name := range c.Names {
if strings.LastIndex(name, "/") == 0 {
return name[1:]
}
}
return c.Names[0][1:]
}
func (s *local) applyPullPolicy(ctx context.Context, service types.ServiceConfig) error {
w := progress.ContextWriter(ctx)
// TODO build vs pull should be controlled by pull policy
// if service.Build {}
if service.Image != "" {
_, _, err := s.containerService.apiClient.ImageInspectWithRaw(ctx, service.Image)
if err != nil {
if errdefs.IsNotFound(err) {
stream, err := s.containerService.apiClient.ImagePull(ctx, service.Image, moby.ImagePullOptions{})
if err != nil {
return err
}
dec := json.NewDecoder(stream)
for {
var jm jsonmessage.JSONMessage
if err := dec.Decode(&jm); err != nil {
if err == io.EOF {
break
}
return err
}
toProgressEvent(jm, w)
}
}
}
}
return nil
}
func toProgressEvent(jm jsonmessage.JSONMessage, w progress.Writer) {
if jm.Progress != nil {
if jm.Progress.Total != 0 {
percentage := int(float64(jm.Progress.Current)/float64(jm.Progress.Total)*100) / 2
numSpaces := 50 - percentage
w.Event(progress.Event{
ID: jm.ID,
Text: jm.Status,
Status: 0,
StatusText: fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces)),
Done: jm.Status == "Pull complete",
})
} else {
if jm.Error != nil {
w.Event(progress.Event{
ID: jm.ID,
Text: jm.Status,
Status: progress.Error,
StatusText: jm.Error.Message,
Done: true,
})
} else if jm.Status == "Pull complete" || jm.Status == "Already exists" {
w.Event(progress.Event{
ID: jm.ID,
Text: jm.Status,
Status: progress.Done,
Done: true,
})
} else {
w.Event(progress.Event{
ID: jm.ID,
Text: jm.Status,
Status: progress.Working,
Done: false,
})
}
}
}
}
func (s *local) Down(ctx context.Context, projectName string) error {
list, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(projectName),
),
})
if err != nil {
return err
}
eg, ctx := errgroup.WithContext(ctx)
w := progress.ContextWriter(ctx)
for _, c := range list {
container := c
eg.Go(func() error {
w.Event(progress.Event{
ID: getContainerName(container),
Text: "Stopping",
Status: progress.Working,
Done: false,
})
err := s.containerService.Stop(ctx, container.ID, nil)
if err != nil {
return err
}
w.Event(progress.Event{
ID: getContainerName(container),
Text: "Removing",
Status: progress.Working,
Done: false,
})
err = s.containerService.Delete(ctx, container.ID, containers.DeleteRequest{})
if err != nil {
return err
}
w.Event(progress.Event{
ID: getContainerName(container),
Text: "Removed",
Status: progress.Done,
Done: true,
})
return nil
})
}
return eg.Wait()
}
func (s *local) Logs(ctx context.Context, projectName string, w io.Writer) error {
list, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(projectName),
),
})
if err != nil {
return err
}
var wg sync.WaitGroup
consumer := formatter.NewLogConsumer(w)
for _, c := range list {
service := c.Labels[serviceLabel]
containerId := c.ID
go func() {
s.containerService.Logs(ctx,containerId, containers.LogsRequest{
Follow: true,
Writer: consumer.GetWriter(service, containerId),
})
wg.Done()
}()
wg.Add(1)
}
wg.Wait()
return nil
}
func (s *local) Ps(ctx context.Context, projectName string) ([]compose.ServiceStatus, error) {
list, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(projectName),
),
})
if err != nil {
return nil, err
}
var status []compose.ServiceStatus
for _, c := range list {
// TODO group by service
status = append(status, compose.ServiceStatus{
ID: c.ID,
Name: c.Labels[serviceLabel],
Replicas: 0,
Desired: 0,
Ports: nil,
Publishers: nil,
})
}
return status, nil
}
func (s *local) List(ctx context.Context, projectName string) ([]compose.Stack, error) {
_, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{All: true})
if err != nil {
return nil, err
}
var stacks []compose.Stack
// TODO rebuild stacks based on containers
return stacks, nil
}
func (s *local) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
switch format {
case "json":
return json.MarshalIndent(project, "", " ")
case "yaml":
return yaml.Marshal(project)
default:
return nil, fmt.Errorf("unsupported format %q", format)
}
}
func getContainerCreateOptions(p *types.Project, s types.ServiceConfig, number int, inherit *moby.Container) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
hash, err := jsonHash(s)
if err != nil {
return nil, nil, nil, err
}
labels := map[string]string{
projectLabel: p.Name,
serviceLabel: s.Name,
configHashLabel: hash,
containerNumberLabel: strconv.Itoa(number),
}
var (
runCmd strslice.StrSlice
entrypoint strslice.StrSlice
)
if len(s.Command) > 0 {
runCmd = strslice.StrSlice(s.Command)
}
if len(s.Entrypoint) > 0 {
entrypoint = strslice.StrSlice(s.Entrypoint)
}
image := s.Image
if s.Image == "" {
image = fmt.Sprintf("%s_%s", p.Name, s.Name)
}
var (
tty = s.Tty
stdinOpen = s.StdinOpen
attachStdin = false
)
containerConfig := container.Config{
Hostname: s.Hostname,
Domainname: s.DomainName,
User: s.User,
ExposedPorts: buildContainerPorts(s),
Tty: tty,
OpenStdin: stdinOpen,
StdinOnce: true,
AttachStdin: attachStdin,
AttachStderr: true,
AttachStdout: true,
Cmd: runCmd,
Image: image,
WorkingDir: s.WorkingDir,
Entrypoint: entrypoint,
NetworkDisabled: s.NetworkMode == "disabled",
MacAddress: s.MacAddress,
Labels: labels,
StopSignal: s.StopSignal,
// Env: s.Environment, FIXME conversion
// Healthcheck: s.HealthCheck, FIXME conversion
// Volumes: // FIXME unclear to me the overlap with HostConfig.Mounts
// StopTimeout: s.StopGracePeriod FIXME conversion
}
mountOptions, err := buildContainerMountOptions(p, s, inherit)
if err != nil {
return nil, nil, nil, err
}
bindings, err := buildContainerBindingOptions(s)
if err != nil {
return nil, nil, nil, err
}
networkMode := getNetworkMode(p, s)
hostConfig := container.HostConfig{
Mounts: mountOptions,
CapAdd: strslice.StrSlice(s.CapAdd),
CapDrop: strslice.StrSlice(s.CapDrop),
NetworkMode: networkMode,
Init: s.Init,
ReadonlyRootfs: s.ReadOnly,
// ShmSize: , TODO
Sysctls: s.Sysctls,
PortBindings: bindings,
}
networkConfig := buildDefaultNetworkConfig(s, networkMode)
return &containerConfig, &hostConfig, networkConfig, nil
}
func buildContainerPorts(s types.ServiceConfig) nat.PortSet {
ports := nat.PortSet{}
for _, p := range s.Ports {
p := nat.Port(fmt.Sprintf("%d/%s", p.Target, p.Protocol))
ports[p] = struct{}{}
}
return ports
}
func buildContainerBindingOptions(s types.ServiceConfig) (nat.PortMap, error) {
bindings := nat.PortMap{}
for _, port := range s.Ports {
p := nat.Port(fmt.Sprintf("%d/%s", port.Target, port.Protocol))
bind := []nat.PortBinding{}
binding := nat.PortBinding{}
if port.Published > 0 {
binding.HostPort = fmt.Sprint(port.Published)
}
bind = append(bind, binding)
bindings[p] = bind
}
return bindings, nil
}
func buildContainerMountOptions(p *types.Project, s types.ServiceConfig, inherit *moby.Container) ([]mount.Mount, error) {
mounts := []mount.Mount{}
var inherited []string
if inherit != nil {
for _, m := range inherit.Mounts {
if m.Type == "tmpfs" {
continue
}
src := m.Source
if m.Type == "volume" {
src = m.Name
}
mounts = append(mounts, mount.Mount{
Type: m.Type,
Source: src,
Target: m.Destination,
ReadOnly: !m.RW,
})
inherited = append(inherited, m.Destination)
}
}
for _, v := range s.Volumes {
if contains(inherited, v.Target) {
continue
}
source := v.Source
if v.Type == "bind" && !filepath.IsAbs(source) {
// FIXME handle ~/
source = filepath.Join(p.WorkingDir, source)
}
mounts = append(mounts, mount.Mount{
Type: mount.Type(v.Type),
Source: source,
Target: v.Target,
ReadOnly: v.ReadOnly,
Consistency: mount.Consistency(v.Consistency),
BindOptions: buildBindOption(v.Bind),
VolumeOptions: buildVolumeOptions(v.Volume),
TmpfsOptions: buildTmpfsOptions(v.Tmpfs),
})
}
return mounts, nil
}
func buildBindOption(bind *types.ServiceVolumeBind) *mount.BindOptions {
if bind == nil {
return nil
}
return &mount.BindOptions{
Propagation: mount.Propagation(bind.Propagation),
// NonRecursive: false, FIXME missing from model ?
}
}
func buildVolumeOptions(vol *types.ServiceVolumeVolume) *mount.VolumeOptions {
if vol == nil {
return nil
}
return &mount.VolumeOptions{
NoCopy: vol.NoCopy,
// Labels: , // FIXME missing from model ?
// DriverConfig: , // FIXME missing from model ?
}
}
func buildTmpfsOptions(tmpfs *types.ServiceVolumeTmpfs) *mount.TmpfsOptions {
if tmpfs == nil {
return nil
}
return &mount.TmpfsOptions{
SizeBytes: tmpfs.Size,
// Mode: , // FIXME missing from model ?
}
}
func buildDefaultNetworkConfig(s types.ServiceConfig, networkMode container.NetworkMode) *network.NetworkingConfig {
config := map[string]*network.EndpointSettings{}
net := string(networkMode)
config[net] = &network.EndpointSettings{
Aliases: getAliases(s, s.Networks[net]),
}
return &network.NetworkingConfig{
EndpointsConfig: config,
}
}
func getAliases(s types.ServiceConfig, c *types.ServiceNetworkConfig) []string {
aliases := []string{s.Name}
if c != nil {
aliases = append(aliases, c.Aliases...)
}
return aliases
}
func getNetworkMode(p *types.Project, service types.ServiceConfig) container.NetworkMode {
mode := service.NetworkMode
if mode == "" {
if len(p.Networks) > 0 {
for name := range getNetworksForService(service) {
return container.NetworkMode(p.Networks[name].Name)
}
}
return container.NetworkMode("none")
}
/// FIXME incomplete implementation
if strings.HasPrefix(mode, "service:") {
panic("Not yet implemented")
}
if strings.HasPrefix(mode, "container:") {
panic("Not yet implemented")
}
return container.NetworkMode(mode)
}
func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig {
if len(s.Networks) > 0 {
return s.Networks
}
return map[string]*types.ServiceNetworkConfig{"default": nil}
}
func (s *local) ensureNetwork(ctx context.Context, n types.NetworkConfig) error {
_, err := s.containerService.apiClient.NetworkInspect(ctx, n.Name, moby.NetworkInspectOptions{})
if err != nil {
if errdefs.IsNotFound(err) {
createOpts := moby.NetworkCreate{
// TODO NameSpace Labels
Labels: n.Labels,
Driver: n.Driver,
Options: n.DriverOpts,
Internal: n.Internal,
Attachable: n.Attachable,
}
if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
createOpts.IPAM = &network.IPAM{}
}
if n.Ipam.Driver != "" {
createOpts.IPAM.Driver = n.Ipam.Driver
}
for _, ipamConfig := range n.Ipam.Config {
config := network.IPAMConfig{
Subnet: ipamConfig.Subnet,
}
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
}
w := progress.ContextWriter(ctx)
w.Event(progress.Event{
ID: fmt.Sprintf("Network %q", n.Name),
Status: progress.Working,
StatusText: "Create",
Done: false,
})
if _, err := s.containerService.apiClient.NetworkCreate(context.Background(), n.Name, createOpts); err != nil {
return errors.Wrapf(err, "failed to create network %s", n.Name)
}
w.Event(progress.Event{
ID: fmt.Sprintf("Network %q", n.Name),
Status: progress.Done,
StatusText: "Created",
Done: true,
})
return nil
} else {
return err
}
}
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) {
w := progress.ContextWriter(ctx)
w.Event(progress.Event{
ID: fmt.Sprintf("Volume %q", volume.Name),
Status: progress.Working,
StatusText: "Create",
Done: false,
})
// TODO we miss support for driver_opts and labels
_, err := s.volumeService.Create(ctx, volume.Name, nil)
w.Event(progress.Event{
ID: fmt.Sprintf("Volume %q", volume.Name),
Status: progress.Done,
StatusText: "Created",
Done: true,
})
if err != nil {
return err
}
}
return err
}
return nil
}

View File

@ -28,6 +28,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/pkg/stringid"
@ -134,13 +135,21 @@ func (cs *containerService) Run(ctx context.Context, r containers.ContainerConfi
},
}
created, err := cs.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, r.ID)
id, err := cs.create(ctx, containerConfig, hostConfig, nil, r.ID)
if err != nil {
return err
}
return cs.apiClient.ContainerStart(ctx, id, types.ContainerStartOptions{})
}
func (cs *containerService) create(ctx context.Context, containerConfig *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, name string) (string, error) {
created, err := cs.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, networkingConfig, name)
if err != nil {
if client.IsErrNotFound(err) {
io, err := cs.apiClient.ImagePull(ctx, r.Image, types.ImagePullOptions{})
io, err := cs.apiClient.ImagePull(ctx, containerConfig.Image, types.ImagePullOptions{})
if err != nil {
return err
return "", err
}
scanner := bufio.NewScanner(io)
@ -149,21 +158,20 @@ func (cs *containerService) Run(ctx context.Context, r containers.ContainerConfi
}
if err = scanner.Err(); err != nil {
return err
return "", err
}
if err = io.Close(); err != nil {
return err
return "", err
}
created, err = cs.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, nil, r.ID)
created, err = cs.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, networkingConfig, name)
if err != nil {
return err
return "", err
}
} else {
return err
return "", err
}
}
return cs.apiClient.ContainerStart(ctx, created.ID, types.ContainerStartOptions{})
return created.ID, nil
}
func (cs *containerService) Start(ctx context.Context, containerID string) error {

241
local/convergence.go Normal file
View File

@ -0,0 +1,241 @@
// +build local
/*
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 local
import (
"context"
"fmt"
"github.com/compose-spec/compose-go/types"
"github.com/docker/compose-cli/api/containers"
"github.com/docker/compose-cli/progress"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"golang.org/x/sync/errgroup"
"strconv"
)
func (s *local) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
actual, err := s.containerService.apiClient.ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
filters.Arg("label", fmt.Sprintf("%s=%s", projectLabel, project.Name)),
filters.Arg("label", fmt.Sprintf("%s=%s", serviceLabel, service.Name)),
),
})
if err != nil {
return err
}
scale := getScale(service)
eg, ctx := errgroup.WithContext(ctx)
if len(actual) < scale {
next, err := nextContainerNumber(actual)
if err != nil {
return err
}
missing := scale - len(actual)
for i := 0; i < missing; i++ {
number := next + i
name := fmt.Sprintf("%s_%s_%d", project.Name, service.Name, number)
eg.Go(func() error {
return s.createContainer(ctx, project, service, name, number)
})
}
}
if len(actual) > scale {
for i := scale; i < len(actual); i++ {
container := actual[i]
eg.Go(func() error {
err := s.containerService.Stop(ctx, container.ID, nil)
if err != nil {
return err
}
return s.containerService.Delete(ctx, container.ID, containers.DeleteRequest{})
})
}
actual = actual[:scale]
}
expected, err := jsonHash(service)
if err != nil {
return err
}
for _, container := range actual {
container := container
diverged := container.Labels[configHashLabel] != expected
if diverged {
eg.Go(func() error {
return s.recreateContainer(ctx, project, service, container)
})
continue
}
if container.State == "running" {
// already running, skip
continue
}
eg.Go(func() error {
return s.restartContainer(ctx, service, container)
})
}
return eg.Wait()
}
func nextContainerNumber(containers []moby.Container) (int, error) {
max := 0
for _, c := range containers {
n, err := strconv.Atoi(c.Labels[containerNumberLabel])
if err != nil {
return 0, err
}
if n > max {
max = n
}
}
return max + 1, nil
}
func getScale(config types.ServiceConfig) int {
if config.Deploy != nil && config.Deploy.Replicas != nil {
return int(*config.Deploy.Replicas)
}
if config.Scale != 0 {
return config.Scale
}
return 1
}
func (s *local) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int) error {
w := progress.ContextWriter(ctx)
w.Event(progress.Event{
ID: fmt.Sprintf("Service %q", service.Name),
Status: progress.Working,
StatusText: "Create",
Done: false,
})
err := s.runContainer(ctx, project, service, name, number, nil)
if err != nil {
return err
}
w.Event(progress.Event{
ID: fmt.Sprintf("Service %q", service.Name),
Status: progress.Done,
StatusText: "Created",
Done: true,
})
return nil
}
func (s *local) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, container moby.Container) error {
w := progress.ContextWriter(ctx)
w.Event(progress.Event{
ID: fmt.Sprintf("Service %q", service.Name),
Status: progress.Working,
StatusText: "Recreate",
Done: false,
})
err := s.containerService.Stop(ctx, container.ID, nil)
if err != nil {
return err
}
name := getContainerName(container)
tmpName := fmt.Sprintf("%s_%s", container.ID[:12], name)
err = s.containerService.apiClient.ContainerRename(ctx, container.ID, tmpName)
if err != nil {
return err
}
number, err := strconv.Atoi(container.Labels[containerNumberLabel])
if err != nil {
return err
}
err = s.runContainer(ctx, project, service, name, number, &container)
if err != nil {
return err
}
err = s.containerService.Delete(ctx, container.ID, containers.DeleteRequest{})
if err != nil {
return err
}
w.Event(progress.Event{
ID: fmt.Sprintf("Service %q", service.Name),
Status: progress.Done,
StatusText: "Recreated",
Done: true,
})
return nil
}
func (s *local) restartContainer(ctx context.Context, service types.ServiceConfig, container moby.Container) error {
w := progress.ContextWriter(ctx)
w.Event(progress.Event{
ID: fmt.Sprintf("Service %q", service.Name),
Status: progress.Working,
StatusText: "Restart",
Done: false,
})
err := s.containerService.Start(ctx, container.ID)
if err != nil {
return err
}
w.Event(progress.Event{
ID: fmt.Sprintf("Service %q", service.Name),
Status: progress.Done,
StatusText: "Restarted",
Done: true,
})
return nil
}
func (s *local) runContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, name string, number int, container *moby.Container) error {
containerConfig, hostConfig, networkingConfig, err := getContainerCreateOptions(project, service, number, container)
if err != nil {
return err
}
id, err := s.containerService.create(ctx, containerConfig, hostConfig, networkingConfig, name)
if err != nil {
return err
}
for net := range service.Networks {
name := fmt.Sprintf("%s_%s", project.Name, net)
err = s.connectContainerToNetwork(ctx, id, service.Name, name)
if err != nil {
return err
}
}
err = s.containerService.apiClient.ContainerStart(ctx, id, moby.ContainerStartOptions{})
if err != nil {
return err
}
return nil
}
func (s *local) connectContainerToNetwork(ctx context.Context, id string, service string, n string) error {
err := s.containerService.apiClient.NetworkConnect(ctx, n, id, &network.EndpointSettings{
Aliases: []string{service},
})
if err != nil {
return err
}
return nil
}

60
local/dependencies.go Normal file
View File

@ -0,0 +1,60 @@
// +build local
/*
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 local
import (
"context"
"github.com/compose-spec/compose-go/types"
"golang.org/x/sync/errgroup"
)
func inDependencyOrder(ctx context.Context, project *types.Project, fn func(types.ServiceConfig) error) error {
eg, ctx := errgroup.WithContext(ctx)
var (
scheduled []string
ready []string
)
results := make(chan string)
for len(ready) < len(project.Services) {
for _, service := range project.Services {
if contains(scheduled, service.Name) {
continue
}
if containsAll(ready, service.GetDependencies()) {
service := service
scheduled = append(scheduled, service.Name)
eg.Go(func() error {
err := fn(service)
if err != nil {
close(results)
return err
}
results <- service.Name
return nil
})
}
}
result, ok := <-results
if !ok {
break
}
ready = append(ready, result)
}
return eg.Wait()
}

View File

@ -0,0 +1,57 @@
// +build local
/*
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 local
import (
"context"
"gotest.tools/v3/assert"
"testing"
"github.com/compose-spec/compose-go/types"
)
func TestInDependencyOrder(t *testing.T) {
order := make(chan string)
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "test1",
DependsOn: map[string]types.ServiceDependency{
"test2": {},
},
},
{
Name: "test2",
DependsOn: map[string]types.ServiceDependency{
"test3": {},
},
},
{
Name: "test3",
},
},
}
go inDependencyOrder(context.TODO(), &project, func(config types.ServiceConfig) error {
order <- config.Name
return nil
})
assert.Equal(t, <- order, "test3")
assert.Equal(t, <- order, "test2")
assert.Equal(t, <- order, "test1")
}

35
local/labels.go Normal file
View File

@ -0,0 +1,35 @@
// +build local
/*
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 local
import (
"fmt"
"github.com/docker/docker/api/types/filters"
)
const (
projectLabel = "com.docker.compose.project"
serviceLabel = "com.docker.compose.service"
configHashLabel = "com.docker.compose.config-hash"
containerNumberLabel = "com.docker.compose.container-number"
)
func projectFilter(projectName string) filters.KeyValuePair {
return filters.Arg("label", fmt.Sprintf("%s=%s", projectLabel, projectName))
}

50
local/util.go Normal file
View File

@ -0,0 +1,50 @@
// +build local
/*
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 local
import (
"encoding/json"
"github.com/opencontainers/go-digest"
)
func jsonHash(o interface{}) (string, error) {
bytes, err := json.Marshal(o)
if err != nil {
return "", nil
}
return digest.SHA256.FromBytes(bytes).String(), nil
}
func contains(slice []string, item string) bool {
for _, v := range slice {
if v == item {
return true
}
}
return false
}
func containsAll(slice []string, items []string) bool {
for _, i := range items {
if !contains(slice, i) {
return false
}
}
return true
}