1
0
mirror of https://github.com/docker/compose.git synced 2025-04-08 17:05:13 +02:00

Create volume for compose app

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-10-09 08:18:39 +02:00
parent e903326e1a
commit 5d5765173e
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
11 changed files with 274 additions and 153 deletions

@ -68,9 +68,12 @@ type API interface {
GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error)
LoadBalancerType(ctx context.Context, arn string) (string, error)
GetLoadBalancerURL(ctx context.Context, arn string) (string, error)
WithVolumeSecurityGroups(ctx context.Context, id string, fn func(securityGroups []string) error) error
GetParameter(ctx context.Context, name string) (string, error)
SecurityGroupExists(ctx context.Context, sg string) (bool, error)
DeleteCapacityProvider(ctx context.Context, arn string) error
DeleteAutoscalingGroup(ctx context.Context, arn string) error
FileSystemExists(ctx context.Context, id string) (bool, error)
FindFileSystem(ctx context.Context, tags map[string]string) (string, error)
CreateFileSystem(ctx context.Context, tags map[string]string) (string, error)
DeleteFileSystem(ctx context.Context, id string) error
}

@ -20,13 +20,17 @@ import (
"context"
"fmt"
"github.com/aws/aws-sdk-go/service/elbv2"
"github.com/awslabs/goformation/v4/cloudformation/ec2"
"github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2"
"github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/errdefs"
"github.com/aws/aws-sdk-go/service/elbv2"
"github.com/awslabs/goformation/v4/cloudformation"
"github.com/awslabs/goformation/v4/cloudformation/ec2"
"github.com/awslabs/goformation/v4/cloudformation/ecs"
"github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2"
"github.com/compose-spec/compose-go/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@ -77,6 +81,10 @@ func (b *ecsAPIService) parse(ctx context.Context, project *types.Project) (awsR
if err != nil {
return r, err
}
r.filesystems, err = b.parseExternalVolumes(ctx, project)
if err != nil {
return r, err
}
return r, nil
}
@ -88,7 +96,7 @@ func (b *ecsAPIService) parseClusterExtension(ctx context.Context, project *type
return "", err
}
if !ok {
return "", fmt.Errorf("cluster does not exist: %s", cluster)
return "", errors.Wrapf(errdefs.ErrNotFound, "cluster %q does not exist", cluster)
}
return cluster, nil
}
@ -143,42 +151,62 @@ func (b *ecsAPIService) parseLoadBalancerExtension(ctx context.Context, project
func (b *ecsAPIService) parseExternalNetworks(ctx context.Context, project *types.Project) (map[string]string, error) {
securityGroups := make(map[string]string, len(project.Networks))
for name, net := range project.Networks {
if !net.External.External {
continue
}
sg := net.Name
// FIXME remove this for G.A
if x, ok := net.Extensions[extensionSecurityGroup]; ok {
logrus.Warn("to use an existing security-group, use `network.external` and `network.name` in your compose file")
logrus.Debugf("Security Group for network %q set by user to %q", net.Name, x)
sg = x.(string)
net.External.External = true
net.Name = x.(string)
project.Networks[name] = net
}
exists, err := b.aws.SecurityGroupExists(ctx, sg)
if !net.External.External {
continue
}
exists, err := b.aws.SecurityGroupExists(ctx, net.Name)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("security group %s doesn't exist", sg)
return nil, errors.Wrapf(errdefs.ErrNotFound, "security group %q doesn't exist", net.Name)
}
securityGroups[name] = sg
securityGroups[name] = net.Name
}
return securityGroups, nil
}
func (b *ecsAPIService) parseExternalVolumes(ctx context.Context, project *types.Project) (map[string]string, error) {
filesystems := make(map[string]string, len(project.Volumes))
// project.Volumes.filter(|v| v.External.External).first(|v| b.SDK.FileSystemExists(ctx, vol.Name))?
for name, vol := range project.Volumes {
if !vol.External.External {
if vol.External.External {
exists, err := b.aws.FileSystemExists(ctx, vol.Name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.Wrapf(errdefs.ErrNotFound, "EFS file system %q doesn't exist", vol.Name)
}
filesystems[name] = vol.Name
continue
}
exists, err := b.SDK.FileSystemExists(ctx, vol.Name)
logrus.Debugf("searching for existing filesystem as volume %q", name)
tags := map[string]string{
compose.ProjectTag: project.Name,
compose.VolumeTag: name,
}
id, err := b.aws.FindFileSystem(ctx, tags)
if err != nil {
return nil, err
}
if !exists {
return nil, fmt.Errorf("EFS file system %s doesn't exist", vol.Name)
if id == "" {
logrus.Debug("no EFS filesystem found, create a fresh new one")
id, err = b.aws.CreateFileSystem(ctx, tags)
if err != nil {
return nil, err
}
}
filesystems[name] = vol.Name
filesystems[name] = id
}
return filesystems, nil
}
@ -206,11 +234,9 @@ func (b *ecsAPIService) ensureNetworks(r *awsResources, project *types.Project,
r.securityGroups = make(map[string]string, len(project.Networks))
}
for name, net := range project.Networks {
if net.External.External {
r.securityGroups[name] = net.Name
if _, ok := r.securityGroups[name]; ok {
continue
}
securityGroup := networkResourceName(name)
template.Resources[securityGroup] = &ec2.SecurityGroup{
GroupDescription: fmt.Sprintf("%s Security Group for %s network", project.Name, name),
@ -230,12 +256,6 @@ func (b *ecsAPIService) ensureNetworks(r *awsResources, project *types.Project,
}
}
func (b *ecsAPIService) ensureVolumes(r *awsResources, project *types.Project, template *cloudformation.Template) {
if r.filesystems == nil {
r.filesystems = make(map[string]string, len(project.Volumes))
}
}
func (b *ecsAPIService) ensureLoadBalancer(r *awsResources, project *types.Project, template *cloudformation.Template) {
if r.loadBalancer != "" {
return

@ -110,6 +110,21 @@ func (mr *MockAPIMockRecorder) CreateCluster(arg0, arg1 interface{}) *gomock.Cal
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCluster", reflect.TypeOf((*MockAPI)(nil).CreateCluster), arg0, arg1)
}
// CreateFileSystem mocks base method
func (m *MockAPI) CreateFileSystem(arg0 context.Context, arg1 map[string]string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFileSystem", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateFileSystem indicates an expected call of CreateFileSystem
func (mr *MockAPIMockRecorder) CreateFileSystem(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFileSystem", reflect.TypeOf((*MockAPI)(nil).CreateFileSystem), arg0, arg1)
}
// CreateSecret mocks base method
func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 secrets.Secret) (string, error) {
m.ctrl.T.Helper()
@ -167,6 +182,20 @@ func (mr *MockAPIMockRecorder) DeleteCapacityProvider(arg0, arg1 interface{}) *g
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCapacityProvider", reflect.TypeOf((*MockAPI)(nil).DeleteCapacityProvider), arg0, arg1)
}
// DeleteFileSystem mocks base method
func (m *MockAPI) DeleteFileSystem(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteFileSystem", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteFileSystem indicates an expected call of DeleteFileSystem
func (mr *MockAPIMockRecorder) DeleteFileSystem(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFileSystem", reflect.TypeOf((*MockAPI)(nil).DeleteFileSystem), arg0, arg1)
}
// DeleteSecret mocks base method
func (m *MockAPI) DeleteSecret(arg0 context.Context, arg1 string, arg2 bool) error {
m.ctrl.T.Helper()
@ -225,6 +254,36 @@ func (mr *MockAPIMockRecorder) DescribeStackEvents(arg0, arg1 interface{}) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeStackEvents", reflect.TypeOf((*MockAPI)(nil).DescribeStackEvents), arg0, arg1)
}
// FileSystemExists mocks base method
func (m *MockAPI) FileSystemExists(arg0 context.Context, arg1 string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FileSystemExists", arg0, arg1)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FileSystemExists indicates an expected call of FileSystemExists
func (mr *MockAPIMockRecorder) FileSystemExists(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FileSystemExists", reflect.TypeOf((*MockAPI)(nil).FileSystemExists), arg0, arg1)
}
// FindFileSystem mocks base method
func (m *MockAPI) FindFileSystem(arg0 context.Context, arg1 map[string]string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindFileSystem", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FindFileSystem indicates an expected call of FindFileSystem
func (mr *MockAPIMockRecorder) FindFileSystem(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindFileSystem", reflect.TypeOf((*MockAPI)(nil).FindFileSystem), arg0, arg1)
}
// GetDefaultVPC mocks base method
func (m *MockAPI) GetDefaultVPC(arg0 context.Context) (string, error) {
m.ctrl.T.Helper()
@ -587,20 +646,6 @@ func (mr *MockAPIMockRecorder) WaitStackComplete(arg0, arg1, arg2 interface{}) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitStackComplete", reflect.TypeOf((*MockAPI)(nil).WaitStackComplete), arg0, arg1, arg2)
}
// WithVolumeSecurityGroups mocks base method
func (m *MockAPI) WithVolumeSecurityGroups(arg0 context.Context, arg1 string, arg2 func([]string) error) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "WithVolumeSecurityGroups", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// WithVolumeSecurityGroups indicates an expected call of WithVolumeSecurityGroups
func (mr *MockAPIMockRecorder) WithVolumeSecurityGroups(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithVolumeSecurityGroups", reflect.TypeOf((*MockAPI)(nil).WithVolumeSecurityGroups), arg0, arg1, arg2)
}
// getURLWithPortMapping mocks base method
func (m *MockAPI) getURLWithPortMapping(arg0 context.Context, arg1 []string) ([]compose.PortPublisher, error) {
m.ctrl.T.Helper()

@ -72,6 +72,8 @@ func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*c
// Private DNS namespace will allow DNS name for the services to be <service>.<project>.local
b.createCloudMap(project, template, resources.vpc)
b.createNFSMountTarget(project, resources, template)
for _, service := range project.Services {
err := b.createService(project, service, template, resources)
if err != nil {
@ -81,17 +83,6 @@ func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*c
b.createAutoscalingPolicy(project, resources, template, service)
}
// Create a NFS inbound rule on each mount target for volumes
// as "source security group" use an arbitrary network attached to service(s) who mounts target volume
for n, vol := range project.Volumes {
err := b.aws.WithVolumeSecurityGroups(ctx, vol.Name, func(securityGroups []string) error {
return b.createNFSmountIngress(securityGroups, project, n, template)
})
if err != nil {
return nil, err
}
}
err = b.createCapacityProvider(ctx, project, template, resources)
if err != nil {
return nil, err
@ -104,7 +95,7 @@ func (b *ecsAPIService) createService(project *types.Project, service types.Serv
taskExecutionRole := b.createTaskExecutionRole(project, service, template)
taskRole := b.createTaskRole(project, service, template)
definition, err := b.createTaskDefinition(project, service)
definition, err := b.createTaskDefinition(project, service, resources)
if err != nil {
return err
}
@ -152,6 +143,10 @@ func (b *ecsAPIService) createService(project *types.Project, service types.Serv
dependsOn = append(dependsOn, serviceResourceName(dependency))
}
for _, s := range service.Volumes {
dependsOn = append(dependsOn, b.mountTargets(s.Source, resources)...)
}
minPercent, maxPercent, err := computeRollingUpdateLimits(service)
if err != nil {
return err
@ -326,12 +321,11 @@ func (b *ecsAPIService) createTargetGroup(project *types.Project, service types.
port.Published,
)
template.Resources[targetGroupName] = &elasticloadbalancingv2.TargetGroup{
HealthCheckEnabled: false,
Port: int(port.Target),
Protocol: protocol,
Tags: projectTags(project),
TargetType: elbv2.TargetTypeEnumIp,
VpcId: vpc,
Port: int(port.Target),
Protocol: protocol,
Tags: projectTags(project),
TargetType: elbv2.TargetTypeEnumIp,
VpcId: vpc,
}
return targetGroupName
}

@ -23,6 +23,8 @@ import (
"reflect"
"testing"
"github.com/awslabs/goformation/v4/cloudformation/efs"
"github.com/golang/mock/gomock"
"github.com/docker/compose-cli/api/compose"
@ -336,7 +338,7 @@ services:
assert.Check(t, loadBalancer.Type == elbv2.LoadBalancerTypeEnumNetwork)
}
func TestUseCustomNetwork(t *testing.T) {
func TestUseExternalNetwork(t *testing.T) {
template := convertYaml(t, `
services:
test:
@ -355,6 +357,65 @@ networks:
assert.Check(t, s.NetworkConfiguration.AwsvpcConfiguration.SecurityGroups[0] == "sg-123abc") //nolint:staticcheck
}
func testVolume(t *testing.T, yaml string, fn ...func(m *MockAPIMockRecorder)) {
template := convertYaml(t, yaml, fn...)
s := template.Resources["DbdataNFSMountTargetOnSubnet1"].(*efs.MountTarget)
assert.Check(t, s != nil)
assert.Equal(t, s.FileSystemId, "fs-123abc") //nolint:staticcheck
s = template.Resources["DbdataNFSMountTargetOnSubnet2"].(*efs.MountTarget)
assert.Check(t, s != nil)
assert.Equal(t, s.FileSystemId, "fs-123abc") //nolint:staticcheck
}
func TestUseExternalVolume(t *testing.T) {
testVolume(t, `
services:
test:
image: nginx
volumes:
db-data:
external: true
name: fs-123abc
`, useDefaultVPC, func(m *MockAPIMockRecorder) {
m.FileSystemExists(gomock.Any(), "fs-123abc").Return(true, nil)
})
}
func TestCreateVolume(t *testing.T) {
tags := map[string]string{
compose.ProjectTag: t.Name(),
compose.VolumeTag: "db-data",
}
testVolume(t, `
services:
test:
image: nginx
volumes:
db-data: {}
`, useDefaultVPC, func(m *MockAPIMockRecorder) {
m.FindFileSystem(gomock.Any(), tags).Return("", nil)
m.CreateFileSystem(gomock.Any(), tags).Return("fs-123abc", nil)
})
}
func TestReusePreviousVolume(t *testing.T) {
tags := map[string]string{
compose.ProjectTag: t.Name(),
compose.VolumeTag: "db-data",
}
testVolume(t, `
services:
test:
image: nginx
volumes:
db-data: {}
`, useDefaultVPC, func(m *MockAPIMockRecorder) {
m.FindFileSystem(gomock.Any(), tags).Return("fs-123abc", nil)
})
}
func TestServiceMapping(t *testing.T) {
template := convertYaml(t, `
services:

@ -97,7 +97,9 @@ var compatibleComposeAttributes = []string{
"secrets.file",
"volumes",
"volumes.external",
"volumes.name",
"networks.external",
"networks.name",
}
func (c *fargateCompatibilityChecker) CheckImage(service *types.ServiceConfig) {
@ -133,9 +135,3 @@ func (c *fargateCompatibilityChecker) CheckLoggingDriver(config *types.LoggingCo
c.Unsupported("services.logging.driver %s is not supported", config.Driver)
}
}
func (c *fargateCompatibilityChecker) CheckVolumeConfigExternal(config *types.VolumeConfig) {
if !config.External.External {
c.Unsupported("non-external volumes are not supported")
}
}

@ -39,7 +39,7 @@ import (
const secretsInitContainerImage = "docker/ecs-secrets-sidecar"
const searchDomainInitContainerImage = "docker/ecs-searchdomain-sidecar"
func (b *ecsAPIService) createTaskDefinition(project *types.Project, service types.ServiceConfig) (*ecs.TaskDefinition, error) {
func (b *ecsAPIService) createTaskDefinition(project *types.Project, service types.ServiceConfig, resources awsResources) (*ecs.TaskDefinition, error) {
cpu, mem, err := toLimits(service)
if err != nil {
return nil, err
@ -81,11 +81,11 @@ func (b *ecsAPIService) createTaskDefinition(project *types.Project, service typ
}
for _, v := range service.Volumes {
source := project.Volumes[v.Source]
volume := project.Volumes[v.Source]
volumes = append(volumes, ecs.TaskDefinition_Volume{
EFSVolumeConfiguration: &ecs.TaskDefinition_EFSVolumeConfiguration{
FilesystemId: source.Name,
RootDirectory: source.DriverOpts["root_directory"],
FilesystemId: resources.filesystems[v.Source],
RootDirectory: volume.DriverOpts["root_directory"],
},
Name: v.Source,
})
@ -100,7 +100,6 @@ func (b *ecsAPIService) createTaskDefinition(project *types.Project, service typ
if err != nil {
return nil, err
}
var reservations *types.Resource
if service.Deploy != nil && service.Deploy.Resources.Reservations != nil {
reservations = service.Deploy.Resources.Reservations

@ -787,28 +787,6 @@ func (s sdk) GetLoadBalancerURL(ctx context.Context, arn string) (string, error)
return dnsName, nil
}
func (s sdk) WithVolumeSecurityGroups(ctx context.Context, id string, fn func(securityGroups []string) error) error {
mounts, err := s.EFS.DescribeMountTargetsWithContext(ctx, &efs.DescribeMountTargetsInput{
FileSystemId: aws.String(id),
})
if err != nil {
return err
}
for _, mount := range mounts.MountTargets {
groups, err := s.EFS.DescribeMountTargetSecurityGroupsWithContext(ctx, &efs.DescribeMountTargetSecurityGroupsInput{
MountTargetId: mount.MountTargetId,
})
if err != nil {
return err
}
err = fn(aws.StringValueSlice(groups.SecurityGroups))
if err != nil {
return err
}
}
return nil
}
func (s sdk) GetParameter(ctx context.Context, name string) (string, error) {
parameter, err := s.SSM.GetParameterWithContext(ctx, &ssm.GetParameterInput{
Name: aws.String(name),
@ -869,19 +847,58 @@ func (s sdk) FileSystemExists(ctx context.Context, id string) (bool, error) {
return len(desc.FileSystems) > 0, nil
}
func (s sdk) CreateFileSystem(ctx context.Context, name string) (string, error) {
func (s sdk) FindFileSystem(ctx context.Context, tags map[string]string) (string, error) {
var token *string
for {
desc, err := s.EFS.DescribeFileSystemsWithContext(ctx, &efs.DescribeFileSystemsInput{
Marker: token,
})
if err != nil {
return "", err
}
for _, filesystem := range desc.FileSystems {
if containsAll(filesystem.Tags, tags) {
return aws.StringValue(filesystem.FileSystemId), nil
}
}
if desc.NextMarker == token {
return "", nil
}
token = desc.NextMarker
}
}
func containsAll(tags []*efs.Tag, required map[string]string) bool {
TAGS:
for key, value := range required {
for _, t := range tags {
if aws.StringValue(t.Key) == key && aws.StringValue(t.Value) == value {
continue TAGS
}
}
return false
}
return true
}
func (s sdk) CreateFileSystem(ctx context.Context, tags map[string]string) (string, error) {
var efsTags []*efs.Tag
for k, v := range tags {
efsTags = append(efsTags, &efs.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
res, err := s.EFS.CreateFileSystemWithContext(ctx, &efs.CreateFileSystemInput{
Tags: []*efs.Tag{
{
Key: aws.String(compose.VolumeTag),
Value: aws.String(name),
},
},
Encrypted: aws.Bool(true),
Tags: efsTags,
})
if err != nil {
return "", err
}
return aws.StringValue(res.FileSystemId), nil
id := aws.StringValue(res.FileSystemId)
logrus.Debugf("Created file system %q", id)
return id, nil
}
func (s sdk) DeleteFileSystem(ctx context.Context, id string) error {

@ -20,40 +20,27 @@ import (
"fmt"
"github.com/awslabs/goformation/v4/cloudformation"
"github.com/awslabs/goformation/v4/cloudformation/ec2"
"github.com/awslabs/goformation/v4/cloudformation/ecs"
"github.com/awslabs/goformation/v4/cloudformation/efs"
"github.com/compose-spec/compose-go/types"
)
func (b *ecsAPIService) createNFSmountIngress(securityGroups []string, project *types.Project, n string, template *cloudformation.Template) error {
target := securityGroups[0]
for _, s := range project.Services {
for _, v := range s.Volumes {
if v.Source != n {
continue
func (b *ecsAPIService) createNFSMountTarget(project *types.Project, resources awsResources, template *cloudformation.Template) {
for volume := range project.Volumes {
for _, subnet := range resources.subnets {
name := fmt.Sprintf("%sNFSMountTargetOn%s", normalizeResourceName(volume), normalizeResourceName(subnet))
template.Resources[name] = &efs.MountTarget{
FileSystemId: resources.filesystems[volume],
SecurityGroups: resources.allSecurityGroups(),
SubnetId: subnet,
}
var source string
for net := range s.Networks {
network := project.Networks[net]
if ext, ok := network.Extensions[extensionSecurityGroup]; ok {
source = ext.(string)
} else {
source = networkResourceName(net)
}
break
}
name := fmt.Sprintf("%sNFSMount%s", normalizeResourceName(s.Name), normalizeResourceName(n))
template.Resources[name] = &ec2.SecurityGroupIngress{
Description: fmt.Sprintf("Allow NFS mount for %s on %s", s.Name, n),
GroupId: target,
SourceSecurityGroupId: cloudformation.Ref(source),
IpProtocol: "tcp",
FromPort: 2049,
ToPort: 2049,
}
service := template.Resources[serviceResourceName(s.Name)].(*ecs.Service)
service.AWSCloudFormationDependsOn = append(service.AWSCloudFormationDependsOn, name)
}
}
return nil
}
func (b *ecsAPIService) mountTargets(volume string, resources awsResources) []string {
var refs []string
for _, subnet := range resources.subnets {
refs = append(refs, fmt.Sprintf("%sNFSMountTargetOn%s", normalizeResourceName(volume), normalizeResourceName(subnet)))
}
return refs
}

11
go.mod

@ -6,8 +6,6 @@ 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
replace github.com/awslabs/goformation/v4 => github.com/ndeloof/goformation/v4 v4.8.1-0.20200827081523-b7a7ac375adf
require (
github.com/AlecAivazis/survey/v2 v2.1.1
github.com/Azure/azure-sdk-for-go v43.3.0+incompatible
@ -21,10 +19,10 @@ require (
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/aws/aws-sdk-go v1.34.8
github.com/awslabs/goformation/v4 v4.14.0
github.com/aws/aws-sdk-go v1.35.7
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-20200907084823-057e1edc5b6f
github.com/compose-spec/compose-go v0.0.0-20201005072614-3b6106793209
github.com/containerd/console v1.0.0
github.com/containerd/containerd v1.3.5 // indirect
github.com/docker/cli v0.0.0-20200528204125-dd360c7c0de8
@ -53,7 +51,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/prometheus/tsdb v0.7.1
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b
github.com/sirupsen/logrus v1.6.0
github.com/sirupsen/logrus v1.7.0
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
@ -63,6 +61,7 @@ require (
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-20200625203802-6e8e738ad208
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // indirect
google.golang.org/grpc v1.32.0
google.golang.org/protobuf v1.25.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect

30
go.sum

@ -96,8 +96,10 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.34.8 h1:GDfVeXG8XQDbpOeAj7415F8qCQZwvY/k/fj+HBqUnBA=
github.com/aws/aws-sdk-go v1.34.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.35.7 h1:FHMhVhyc/9jljgFAcGkQDYjpC9btM0B8VfkLBfctdNE=
github.com/aws/aws-sdk-go v1.35.7/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
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,8 +122,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
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-20200907084823-057e1edc5b6f h1:YsU3/17YA/skXpCQbRcrzWJxslWZ2lmvQK0bRiCyC38=
github.com/compose-spec/compose-go v0.0.0-20200907084823-057e1edc5b6f/go.mod h1:voTGL1mRFcKRaFbi1lXGlR1YffS/9YD1jnVl4N/rYzw=
github.com/compose-spec/compose-go v0.0.0-20201005072614-3b6106793209 h1:PLZiS7hjkiAqZYBRAEq3tbGlhCh6/R14dO1ahwbEIBg=
github.com/compose-spec/compose-go v0.0.0-20201005072614-3b6106793209/go.mod h1:rNXXqhdClEljsNb6QDIOqTQaRfigwTgGZZM6Zpr3LeY=
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=
@ -193,8 +195,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
@ -296,8 +296,10 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
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=
@ -314,8 +316,6 @@ 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.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
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=
@ -364,8 +364,6 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/ndeloof/goformation/v4 v4.8.1-0.20200827081523-b7a7ac375adf h1:jdmD8L6TKRZpa7B4qUmjiWRBMkgbfUF/7pi/Kgba5lA=
github.com/ndeloof/goformation/v4 v4.8.1-0.20200827081523-b7a7ac375adf/go.mod h1:GcJULxCJfloT+3pbqCluXftdEK2AD/UqpS3hkaaBntg=
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/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@ -429,8 +427,8 @@ 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 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
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=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@ -459,7 +457,6 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -616,6 +613,7 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
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-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=
@ -633,6 +631,8 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
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-20201009025420-dfb3f7c4e634 h1:bNEHhJCnrwMKNMmOx3yAynp5vs5/gRy+XWFtZFu7NBM=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/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=