Merge pull request #327 from docker/compose-go

Use compose-go's Project and ProjectOptions
This commit is contained in:
Guillaume Tardif 2020-07-02 16:43:34 +02:00 committed by GitHub
commit 3c83e01e9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 101 additions and 352 deletions

View File

@ -26,6 +26,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
"github.com/Azure/go-autorest/autorest/to"
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -167,28 +168,26 @@ func (cs *aciContainerService) Run(ctx context.Context, r containers.ContainerCo
return err
}
project := compose.Project{
project := types.Project{
Name: r.ID,
Config: types.Config{
Services: []types.ServiceConfig{
{
Name: singleContainerName,
Image: r.Image,
Ports: ports,
Labels: r.Labels,
Volumes: serviceConfigVolumes,
Deploy: &types.DeployConfig{
Resources: types.Resources{
Limits: &types.Resource{
NanoCPUs: fmt.Sprintf("%f", r.CPULimit),
MemoryBytes: types.UnitBytes(r.MemLimit.Value()),
},
Services: []types.ServiceConfig{
{
Name: singleContainerName,
Image: r.Image,
Ports: ports,
Labels: r.Labels,
Volumes: serviceConfigVolumes,
Deploy: &types.DeployConfig{
Resources: types.Resources{
Limits: &types.Resource{
NanoCPUs: fmt.Sprintf("%f", r.CPULimit),
MemoryBytes: types.UnitBytes(r.MemLimit.Value()),
},
},
},
},
Volumes: projectVolumes,
},
Volumes: projectVolumes,
}
logrus.Debugf("Running container %q with name %q\n", r.Image, r.ID)
@ -301,8 +300,8 @@ type aciComposeService struct {
ctx store.AciContext
}
func (cs *aciComposeService) Up(ctx context.Context, opts compose.ProjectOptions) error {
project, err := compose.ProjectFromOptions(&opts)
func (cs *aciComposeService) Up(ctx context.Context, opts cli.ProjectOptions) error {
project, err := cli.ProjectFromOptions(&opts)
if err != nil {
return err
}
@ -315,8 +314,8 @@ func (cs *aciComposeService) Up(ctx context.Context, opts compose.ProjectOptions
return createOrUpdateACIContainers(ctx, cs.ctx, groupDefinition)
}
func (cs *aciComposeService) Down(ctx context.Context, opts compose.ProjectOptions) error {
project, err := compose.ProjectFromOptions(&opts)
func (cs *aciComposeService) Down(ctx context.Context, opts cli.ProjectOptions) error {
project, err := cli.ProjectFromOptions(&opts)
if err != nil {
return err
}

View File

@ -29,7 +29,6 @@ import (
"github.com/Azure/go-autorest/autorest/to"
"github.com/compose-spec/compose-go/types"
"github.com/docker/api/compose"
"github.com/docker/api/containers"
"github.com/docker/api/context/store"
)
@ -47,7 +46,7 @@ const (
)
// ToContainerGroup converts a compose project into a ACI container group
func ToContainerGroup(aciContext store.AciContext, p compose.Project) (containerinstance.ContainerGroup, error) {
func ToContainerGroup(aciContext store.AciContext, p types.Project) (containerinstance.ContainerGroup, error) {
project := projectAciHelper(p)
containerGroupName := strings.ToLower(project.Name)
volumesCache, volumesSlice, err := project.getAciFileVolumes()
@ -154,7 +153,7 @@ func getDNSSidecar(containers []containerinstance.Container) containerinstance.C
return dnsSideCar
}
type projectAciHelper compose.Project
type projectAciHelper types.Project
func (p projectAciHelper) getAciSecretVolumes() ([]containerinstance.Volume, error) {
var secretVolumes []containerinstance.Volume

View File

@ -23,7 +23,6 @@ import (
"github.com/Azure/go-autorest/autorest/to"
"github.com/compose-spec/compose-go/types"
"github.com/docker/api/compose"
"github.com/docker/api/containers"
"github.com/docker/api/context/store"
@ -46,7 +45,7 @@ func (suite *ConvertTestSuite) BeforeTest(suiteName, testName string) {
}
func (suite *ConvertTestSuite) TestProjectName() {
project := compose.Project{
project := types.Project{
Name: "TEST",
}
containerGroup, err := ToContainerGroup(suite.ctx, project)
@ -110,18 +109,15 @@ func (suite *ConvertTestSuite) TestContainerGroupToContainer() {
}
func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerWithDnsSideCarSide() {
project := compose.Project{
Name: "",
Config: types.Config{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
},
{
Name: "service2",
Image: "image2",
},
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
},
{
Name: "service2",
Image: "image2",
},
},
}
@ -142,14 +138,11 @@ func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerWithDnsSideCa
}
func (suite *ConvertTestSuite) TestComposeSingleContainerGroupToContainerNoDnsSideCarSide() {
project := compose.Project{
Name: "",
Config: types.Config{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
},
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
},
},
}
@ -163,28 +156,25 @@ func (suite *ConvertTestSuite) TestComposeSingleContainerGroupToContainerNoDnsSi
}
func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerMultiplePorts() {
project := compose.Project{
Name: "",
Config: types.Config{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Ports: []types.ServicePortConfig{
{
Published: 80,
Target: 80,
},
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Ports: []types.ServicePortConfig{
{
Published: 80,
Target: 80,
},
},
{
Name: "service2",
Image: "image2",
Ports: []types.ServicePortConfig{
{
Published: 8080,
Target: 8080,
},
},
{
Name: "service2",
Image: "image2",
Ports: []types.ServicePortConfig{
{
Published: 8080,
Target: 8080,
},
},
},
@ -215,19 +205,16 @@ func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerMultiplePorts
func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerResourceLimits() {
_0_1Gb := 0.1 * 1024 * 1024 * 1024
project := compose.Project{
Name: "",
Config: types.Config{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Deploy: &types.DeployConfig{
Resources: types.Resources{
Limits: &types.Resource{
NanoCPUs: "0.1",
MemoryBytes: types.UnitBytes(_0_1Gb),
},
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Deploy: &types.DeployConfig{
Resources: types.Resources{
Limits: &types.Resource{
NanoCPUs: "0.1",
MemoryBytes: types.UnitBytes(_0_1Gb),
},
},
},
@ -245,19 +232,16 @@ func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerResourceLimit
}
func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerResourceLimitsDefaults() {
project := compose.Project{
Name: "",
Config: types.Config{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Deploy: &types.DeployConfig{
Resources: types.Resources{
Limits: &types.Resource{
NanoCPUs: "",
MemoryBytes: 0,
},
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Deploy: &types.DeployConfig{
Resources: types.Resources{
Limits: &types.Resource{
NanoCPUs: "",
MemoryBytes: 0,
},
},
},

View File

@ -23,11 +23,10 @@ import (
"github.com/Azure/azure-sdk-for-go/profiles/latest/containerinstance/mgmt/containerinstance"
"github.com/Azure/go-autorest/autorest/to"
compose "github.com/compose-spec/compose-go/types"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/types"
"github.com/docker/api/compose"
)
// Specific username from ACR docs : https://github.com/Azure/acr/blob/master/docs/AAD-OAuth.md#getting-credentials-programatically

View File

@ -24,8 +24,6 @@ import (
"github.com/compose-spec/compose-go/types"
cliconfigtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/api/compose"
"github.com/Azure/azure-sdk-for-go/profiles/latest/containerinstance/mgmt/containerinstance"
. "github.com/onsi/gomega"
@ -173,7 +171,7 @@ func (suite *RegistryConvertTestSuite) TestHubAndSeveralACRRegistries() {
}))
}
func composeServices(images ...string) compose.Project {
func composeServices(images ...string) types.Project {
var services []types.ServiceConfig
for index, name := range images {
service := types.ServiceConfig{
@ -182,10 +180,8 @@ func composeServices(images ...string) compose.Project {
}
services = append(services, service)
}
return compose.Project{
Config: types.Config{
Services: services,
},
return types.Project{
Services: services,
}
}

View File

@ -20,14 +20,14 @@ import (
"context"
"errors"
"github.com/compose-spec/compose-go/cli"
"github.com/spf13/cobra"
"github.com/docker/api/client"
"github.com/docker/api/compose"
)
func downCommand() *cobra.Command {
opts := compose.ProjectOptions{}
opts := cli.ProjectOptions{}
downCmd := &cobra.Command{
Use: "down",
RunE: func(cmd *cobra.Command, args []string) error {
@ -35,13 +35,13 @@ func downCommand() *cobra.Command {
},
}
downCmd.Flags().StringVarP(&opts.Name, "project-name", "p", "", "Project name")
downCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
downCmd.Flags().StringVar(&opts.WorkingDir, "workdir", ".", "Work dir")
downCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
return downCmd
}
func runDown(ctx context.Context, opts compose.ProjectOptions) error {
func runDown(ctx context.Context, opts cli.ProjectOptions) error {
c, err := client.New(ctx)
if err != nil {
return err

View File

@ -20,15 +20,15 @@ import (
"context"
"errors"
"github.com/compose-spec/compose-go/cli"
"github.com/spf13/cobra"
"github.com/docker/api/client"
"github.com/docker/api/compose"
"github.com/docker/api/progress"
)
func upCommand() *cobra.Command {
opts := compose.ProjectOptions{}
opts := cli.ProjectOptions{}
upCmd := &cobra.Command{
Use: "up",
RunE: func(cmd *cobra.Command, args []string) error {
@ -36,7 +36,7 @@ func upCommand() *cobra.Command {
},
}
upCmd.Flags().StringVarP(&opts.Name, "project-name", "p", "", "Project name")
upCmd.Flags().StringVar(&opts.WorkDir, "workdir", ".", "Work dir")
upCmd.Flags().StringVar(&opts.WorkingDir, "workdir", ".", "Work dir")
upCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
upCmd.Flags().StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")
upCmd.Flags().BoolP("detach", "d", true, " Detached mode: Run containers in the background")
@ -44,7 +44,7 @@ func upCommand() *cobra.Command {
return upCmd
}
func runUp(ctx context.Context, opts compose.ProjectOptions) error {
func runUp(ctx context.Context, opts cli.ProjectOptions) error {
c, err := client.New(ctx)
if err != nil {
return err

View File

@ -18,12 +18,14 @@ package compose
import (
"context"
"github.com/compose-spec/compose-go/cli"
)
// Service manages a compose project
type Service interface {
// Up executes the equivalent to a `compose up`
Up(ctx context.Context, opts ProjectOptions) error
Up(ctx context.Context, opts cli.ProjectOptions) error
// Down executes the equivalent to a `compose down`
Down(ctx context.Context, opts ProjectOptions) error
Down(ctx context.Context, opts cli.ProjectOptions) error
}

View File

@ -1,172 +0,0 @@
/*
Copyright 2020 Docker, Inc.
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 compose
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"github.com/sirupsen/logrus"
)
var supportedFilenames = []string{
"compose.yml",
"compose.yaml",
"docker-compose.yml",
"docker-compose.yaml",
}
// ProjectOptions configures a compose project
type ProjectOptions struct {
Name string
WorkDir string
ConfigPaths []string
Environment []string
}
// Project represents a compose project with a name
type Project struct {
types.Config
projectDir string
Name string `yaml:"-" json:"-"`
}
// ProjectFromOptions load a compose project based on given options
func ProjectFromOptions(options *ProjectOptions) (*Project, error) {
configPath, err := getConfigPathFromOptions(options)
if err != nil {
return nil, err
}
configs, err := parseConfigs(configPath)
if err != nil {
return nil, err
}
name := options.Name
if name == "" {
r := regexp.MustCompile(`[^a-z0-9\\-_]+`)
absPath, err := filepath.Abs(options.WorkDir)
if err != nil {
return nil, err
}
name = r.ReplaceAllString(strings.ToLower(filepath.Base(absPath)), "")
}
return newProject(types.ConfigDetails{
WorkingDir: options.WorkDir,
ConfigFiles: configs,
Environment: getAsEqualsMap(options.Environment),
}, name)
}
func newProject(config types.ConfigDetails, name string) (*Project, error) {
model, err := loader.Load(config)
if err != nil {
return nil, err
}
p := Project{
Config: *model,
projectDir: config.WorkingDir,
Name: name,
}
return &p, nil
}
func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) {
var paths []string
pwd := options.WorkDir
if len(options.ConfigPaths) != 0 {
for _, f := range options.ConfigPaths {
if f == "-" {
paths = append(paths, f)
continue
}
if !filepath.IsAbs(f) {
f = filepath.Join(pwd, f)
}
if _, err := os.Stat(f); err != nil {
return nil, err
}
paths = append(paths, f)
}
return paths, nil
}
for {
var candidates []string
for _, n := range supportedFilenames {
f := filepath.Join(pwd, n)
if _, err := os.Stat(f); err == nil {
candidates = append(candidates, f)
}
}
if len(candidates) > 0 {
winner := candidates[0]
if len(candidates) > 1 {
logrus.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", "))
logrus.Warnf("Using %s\n", winner)
}
return []string{winner}, nil
}
parent := filepath.Dir(pwd)
if parent == pwd {
return nil, fmt.Errorf("can't find a suitable configuration file in this directory or any parent. Is %q the right directory?", pwd)
}
pwd = parent
}
}
func parseConfigs(configPaths []string) ([]types.ConfigFile, error) {
var files []types.ConfigFile
for _, f := range configPaths {
var b []byte
var err error
if f == "-" {
b, err = ioutil.ReadAll(os.Stdin)
} else {
b, err = ioutil.ReadFile(f)
}
if err != nil {
return nil, err
}
config, err := loader.ParseYAML(b)
if err != nil {
return nil, err
}
files = append(files, types.ConfigFile{Filename: f, Config: config})
}
return files, nil
}
// getAsEqualsMap split key=value formatted strings into a key : value map
func getAsEqualsMap(em []string) map[string]string {
m := make(map[string]string)
for _, v := range em {
kv := strings.SplitN(v, "=", 2)
m[kv[0]] = kv[1]
}
return m
}

View File

@ -1,60 +0,0 @@
/*
Copyright 2020 Docker, Inc.
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 compose
import (
"os"
"testing"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/suite"
)
type ComposeTest struct {
suite.Suite
}
func (suite *ComposeTest) TestParseComposeFile() {
files := []string{"../tests/composefiles/aci-demo/aci_demo_port.yaml"}
config, err := parseConfigs(files)
Expect(err).To(BeNil())
services := config[0].Config["services"].(map[string]interface{})
Expect(len(services)).To(Equal(3))
}
func (suite *ComposeTest) TestParseComposeStdin() {
files := []string{"-"}
f, err := os.Open("../tests/composefiles/aci-demo/aci_demo_port.yaml")
Expect(err).To(BeNil())
defer func() {
err := f.Close()
Expect(err).To(BeNil())
}()
oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }() // Restore original Stdin
os.Stdin = f
config, err := parseConfigs(files)
Expect(err).To(BeNil())
services := config[0].Config["services"].(map[string]interface{})
Expect(len(services)).To(Equal(3))
}
func TestComposeProject(t *testing.T) {
RegisterTestingT(t)
suite.Run(t, new(ComposeTest))
}

View File

@ -22,6 +22,7 @@ import (
"context"
"errors"
"fmt"
"github.com/compose-spec/compose-go/cli"
"io"
"github.com/docker/api/context/cloud"
@ -109,8 +110,8 @@ func (cs *containerService) Delete(ctx context.Context, id string, force bool) e
type composeService struct{}
func (cs *composeService) Up(ctx context.Context, opts compose.ProjectOptions) error {
prj, err := compose.ProjectFromOptions(&opts)
func (cs *composeService) Up(ctx context.Context, opts cli.ProjectOptions) error {
prj, err := cli.ProjectFromOptions(&opts)
if err != nil {
return err
}
@ -118,8 +119,8 @@ func (cs *composeService) Up(ctx context.Context, opts compose.ProjectOptions) e
return nil
}
func (cs *composeService) Down(ctx context.Context, opts compose.ProjectOptions) error {
prj, err := compose.ProjectFromOptions(&opts)
func (cs *composeService) Down(ctx context.Context, opts cli.ProjectOptions) error {
prj, err := cli.ProjectFromOptions(&opts)
if err != nil {
return err
}

2
go.mod
View File

@ -16,7 +16,7 @@ require (
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5
github.com/Microsoft/hcsshim v0.8.9 // indirect
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129
github.com/compose-spec/compose-go v0.0.0-20200423124427-63dcf8c22cae
github.com/compose-spec/compose-go v0.0.0-20200629133725-59b25574fd55
github.com/containerd/console v1.0.0
github.com/containerd/containerd v1.3.5 // indirect
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb // indirect

11
go.sum
View File

@ -63,8 +63,8 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
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-20200423124427-63dcf8c22cae h1:5zRbbF5Gbkl7ZEJrKwYha2JMWgnfpPjSmv8+jCmkeSA=
github.com/compose-spec/compose-go v0.0.0-20200423124427-63dcf8c22cae/go.mod h1:1PUpzRF1O/65VOqXZuwpCuYY7pJxbIq1jbAvAf62FGM=
github.com/compose-spec/compose-go v0.0.0-20200629133725-59b25574fd55 h1:vX1uiHKgVnFBPBLxz6P28n+TKGt3jGThvDr2vkSP8D0=
github.com/compose-spec/compose-go v0.0.0-20200629133725-59b25574fd55/go.mod h1:ArodJ6gsEB7iWKrbV3fSHZ08LlBvSVB0Oqg04fX86t4=
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/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
@ -181,6 +181,8 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
@ -216,8 +218,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74 h1:kvRIeqJNICemq2UFLx8q/Pj+1IRNZS0XPTaMFkuNsvg=
github.com/moby/term v0.0.0-20200611042045-63b9a826fb74/go.mod h1:pJ0Ot5YGdTcMdxnPMyGCfAr6fKXe0g9cDlz16MuFEBE=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@ -276,7 +278,6 @@ github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvH
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.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=