code restructure

Signed-off-by: aiordache <anca.iordache@docker.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
aiordache 2020-06-11 19:22:12 +02:00 committed by Nicolas De Loof
parent d36b9b104e
commit bb98dae082
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
35 changed files with 464 additions and 421 deletions

View File

@ -3,9 +3,12 @@ package commands
import (
"context"
"fmt"
"io"
"os"
"strings"
"github.com/docker/cli/cli/command"
"github.com/docker/ecs-plugin/pkg/amazon"
amazon "github.com/docker/ecs-plugin/pkg/amazon/backend"
"github.com/docker/ecs-plugin/pkg/compose"
"github.com/docker/ecs-plugin/pkg/docker"
"github.com/spf13/cobra"
@ -47,11 +50,11 @@ func ConvertCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions)
if err != nil {
return err
}
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
template, err := client.Convert(project)
template, err := backend.Convert(project)
if err != nil {
return err
}
@ -77,11 +80,11 @@ func UpCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobr
if err != nil {
return err
}
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
return client.ComposeUp(context.Background(), project)
return backend.ComposeUp(context.Background(), project)
}),
}
cmd.Flags().StringVar(&opts.loadBalancerArn, "load-balancer", "", "")
@ -97,11 +100,20 @@ func PsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *cobr
if err != nil {
return err
}
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
return client.ComposePs(context.Background(), project)
tasks, err := backend.ComposePs(context.Background(), project)
if err != nil {
return err
}
printSection(os.Stdout, len(tasks), func(w io.Writer) {
for _, task := range tasks {
fmt.Fprintf(w, "%s\t%s\t%s\n", task.Name, task.State, strings.Join(task.Ports, " "))
}
}, "NAME", "STATE", "PORTS")
return nil
}),
}
cmd.Flags().StringVar(&opts.loadBalancerArn, "load-balancer", "", "")
@ -117,7 +129,7 @@ func DownCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
cmd := &cobra.Command{
Use: "down",
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
@ -126,11 +138,11 @@ func DownCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
if err != nil {
return err
}
return client.ComposeDown(context.Background(), project.Name, opts.DeleteCluster)
return backend.ComposeDown(context.Background(), project.Name, opts.DeleteCluster)
}
// project names passed as parameters
for _, name := range args {
err := client.ComposeDown(context.Background(), name, opts.DeleteCluster)
err := backend.ComposeDown(context.Background(), name, opts.DeleteCluster)
if err != nil {
return err
}
@ -146,7 +158,7 @@ func LogsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
cmd := &cobra.Command{
Use: "logs [PROJECT NAME]",
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
@ -161,7 +173,7 @@ func LogsCommand(dockerCli command.Cli, projectOpts *compose.ProjectOptions) *co
} else {
name = args[0]
}
return client.ComposeLogs(context.Background(), name)
return backend.ComposeLogs(context.Background(), name)
}),
}
return cmd

View File

@ -10,7 +10,8 @@ import (
"text/tabwriter"
"github.com/docker/cli/cli/command"
"github.com/docker/ecs-plugin/pkg/amazon"
amazon "github.com/docker/ecs-plugin/pkg/amazon/backend"
"github.com/docker/ecs-plugin/pkg/amazon/types"
"github.com/docker/ecs-plugin/pkg/docker"
"github.com/spf13/cobra"
)
@ -47,7 +48,7 @@ func CreateSecret(dockerCli command.Cli) *cobra.Command {
Use: "create NAME",
Short: "Creates a secret.",
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
@ -56,8 +57,8 @@ func CreateSecret(dockerCli command.Cli) *cobra.Command {
}
name := args[0]
secret := docker.NewSecret(name, opts.Username, opts.Password, opts.Description)
id, err := client.CreateSecret(context.Background(), secret)
secret := types.NewSecret(name, opts.Username, opts.Password, opts.Description)
id, err := backend.CreateSecret(context.Background(), secret)
fmt.Println(id)
return err
}),
@ -73,7 +74,7 @@ func InspectSecret(dockerCli command.Cli) *cobra.Command {
Use: "inspect ID",
Short: "Displays secret details",
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
@ -81,7 +82,7 @@ func InspectSecret(dockerCli command.Cli) *cobra.Command {
return errors.New("Missing mandatory parameter: ID")
}
id := args[0]
secret, err := client.InspectSecret(context.Background(), id)
secret, err := backend.InspectSecret(context.Background(), id)
if err != nil {
return err
}
@ -102,11 +103,11 @@ func ListSecrets(dockerCli command.Cli) *cobra.Command {
Aliases: []string{"ls"},
Short: "List secrets stored for the existing account.",
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
secrets, err := client.ListSecrets(context.Background())
secrets, err := backend.ListSecrets(context.Background())
if err != nil {
return err
}
@ -125,21 +126,21 @@ func DeleteSecret(dockerCli command.Cli) *cobra.Command {
Aliases: []string{"rm", "remove"},
Short: "Removes a secret.",
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
backend, err := amazon.NewBackend(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
if err != nil {
return err
}
if len(args) == 0 {
return errors.New("Missing mandatory parameter: [NAME]")
}
return client.DeleteSecret(context.Background(), args[0], opts.recover)
return backend.DeleteSecret(context.Background(), args[0], opts.recover)
}),
}
cmd.Flags().BoolVar(&opts.recover, "recover", false, "Enable recovery.")
return cmd
}
func printList(out io.Writer, secrets []docker.Secret) {
func printList(out io.Writer, secrets []types.Secret) {
printSection(out, len(secrets), func(w io.Writer) {
for _, secret := range secrets {
fmt.Fprintf(w, "%s\t%s\t%s\n", secret.ID, secret.Name, secret.Description)

8
ecs/pkg/amazon/amazon.go Normal file
View File

@ -0,0 +1,8 @@
package amazon
import (
"github.com/docker/ecs-plugin/pkg/amazon/backend"
"github.com/docker/ecs-plugin/pkg/compose"
)
var _ compose.API = &backend.Backend{}

View File

@ -1,11 +0,0 @@
package amazon
//go:generate mockgen -destination=./api_mock.go -self_package "github.com/docker/ecs-plugin/pkg/amazon" -package=amazon . API
type API interface {
downAPI
upAPI
logsAPI
secretsAPI
listAPI
}

View File

@ -1,9 +1,9 @@
package amazon
package backend
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/docker/ecs-plugin/pkg/compose"
"github.com/docker/ecs-plugin/pkg/amazon/sdk"
)
const (
@ -12,7 +12,7 @@ const (
ServiceTag = "com.docker.compose.service"
)
func NewClient(profile string, cluster string, region string) (compose.API, error) {
func NewBackend(profile string, cluster string, region string) (*Backend, error) {
sess, err := session.NewSessionWithOptions(session.Options{
Profile: profile,
Config: aws.Config{
@ -22,17 +22,15 @@ func NewClient(profile string, cluster string, region string) (compose.API, erro
if err != nil {
return nil, err
}
return &client{
return &Backend{
Cluster: cluster,
Region: region,
api: NewAPI(sess),
api: sdk.NewAPI(sess),
}, nil
}
type client struct {
type Backend struct {
Cluster string
Region string
api API
api sdk.API
}
var _ compose.API = &client{}

View File

@ -1,4 +1,4 @@
package amazon
package backend
import (
"fmt"
@ -21,6 +21,9 @@ import (
"github.com/awslabs/goformation/v4/cloudformation/logs"
cloudmap "github.com/awslabs/goformation/v4/cloudformation/servicediscovery"
"github.com/awslabs/goformation/v4/cloudformation/tags"
"github.com/docker/ecs-plugin/pkg/amazon/compatibility"
sdk "github.com/docker/ecs-plugin/pkg/amazon/sdk"
btypes "github.com/docker/ecs-plugin/pkg/amazon/types"
"github.com/docker/ecs-plugin/pkg/compose"
)
@ -33,8 +36,8 @@ const (
)
// Convert a compose project into a CloudFormation template
func (c client) Convert(project *compose.Project) (*cloudformation.Template, error) {
warnings := Check(project)
func (b Backend) Convert(project *compose.Project) (*cloudformation.Template, error) {
warnings := compatibility.Check(project)
for _, w := range warnings {
logrus.Warn(w)
}
@ -75,7 +78,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
// Create Cluster is `ParameterClusterName` parameter is not set
template.Conditions["CreateCluster"] = cloudformation.Equals("", cloudformation.Ref(ParameterClusterName))
cluster := c.createCluster(project, template)
cluster := createCluster(project, template)
networks := map[string]string{}
for _, net := range project.Networks {
@ -88,17 +91,18 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
}
// Private DNS namespace will allow DNS name for the services to be <service>.<project>.local
c.createCloudMap(project, template)
createCloudMap(project, template)
loadBalancerARN := c.createLoadBalancer(project, template)
loadBalancerARN := createLoadBalancer(project, template)
for _, service := range project.Services {
definition, err := Convert(project, service)
definition, err := sdk.Convert(project, service)
if err != nil {
return nil, err
}
taskExecutionRole, err := c.createTaskExecutionRole(service, err, definition, template)
taskExecutionRole, err := createTaskExecutionRole(service, err, definition, template)
if err != nil {
return template, err
}
@ -112,7 +116,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
// FIXME ECS only support HTTP(s) health checks, while Docker only support CMD
}
serviceRegistry := c.createServiceRegistry(service, template, healthCheck)
serviceRegistry := createServiceRegistry(service, template, healthCheck)
serviceSecurityGroups := []string{}
for net := range service.Networks {
@ -124,14 +128,14 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
if len(service.Ports) > 0 {
for _, port := range service.Ports {
protocol := strings.ToUpper(port.Protocol)
if c.getLoadBalancerType(project) == elbv2.LoadBalancerTypeEnumApplication {
if getLoadBalancerType(project) == elbv2.LoadBalancerTypeEnumApplication {
protocol = elbv2.ProtocolEnumHttps
if port.Published == 80 {
protocol = elbv2.ProtocolEnumHttp
}
}
targetGroupName := c.createTargetGroup(project, service, port, template, protocol)
listenerName := c.createListener(service, port, template, targetGroupName, loadBalancerARN, protocol)
targetGroupName := createTargetGroup(project, service, port, template, protocol)
listenerName := createListener(service, port, template, targetGroupName, loadBalancerARN, protocol)
dependsOn = append(dependsOn, listenerName)
serviceLB = append(serviceLB, ecs.Service_LoadBalancer{
ContainerName: service.Name,
@ -184,7 +188,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
return template, nil
}
func (c client) getLoadBalancerType(project *compose.Project) string {
func getLoadBalancerType(project *compose.Project) string {
for _, service := range project.Services {
for _, port := range service.Ports {
if port.Published != 80 && port.Published != 443 {
@ -195,7 +199,7 @@ func (c client) getLoadBalancerType(project *compose.Project) string {
return elbv2.LoadBalancerTypeEnumApplication
}
func (c client) getLoadBalancerSecurityGroups(project *compose.Project, template *cloudformation.Template) []string {
func getLoadBalancerSecurityGroups(project *compose.Project, template *cloudformation.Template) []string {
securityGroups := []string{}
for _, network := range project.Networks {
if !network.Internal {
@ -206,15 +210,15 @@ func (c client) getLoadBalancerSecurityGroups(project *compose.Project, template
return uniqueStrings(securityGroups)
}
func (c client) createLoadBalancer(project *compose.Project, template *cloudformation.Template) string {
func createLoadBalancer(project *compose.Project, template *cloudformation.Template) string {
loadBalancerName := fmt.Sprintf("%sLoadBalancer", strings.Title(project.Name))
// Create LoadBalancer if `ParameterLoadBalancerName` is not set
template.Conditions["CreateLoadBalancer"] = cloudformation.Equals("", cloudformation.Ref(ParameterLoadBalancerARN))
loadBalancerType := c.getLoadBalancerType(project)
loadBalancerType := getLoadBalancerType(project)
securityGroups := []string{}
if loadBalancerType == elbv2.LoadBalancerTypeEnumApplication {
securityGroups = c.getLoadBalancerSecurityGroups(project, template)
securityGroups = getLoadBalancerSecurityGroups(project, template)
}
template.Resources[loadBalancerName] = &elasticloadbalancingv2.LoadBalancer{
@ -237,7 +241,7 @@ func (c client) createLoadBalancer(project *compose.Project, template *cloudform
return cloudformation.If("CreateLoadBalancer", cloudformation.Ref(loadBalancerName), cloudformation.Ref(ParameterLoadBalancerARN))
}
func (c client) createListener(service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, targetGroupName string, loadBalancerARN string, protocol string) string {
func createListener(service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, targetGroupName string, loadBalancerARN string, protocol string) string {
listenerName := fmt.Sprintf(
"%s%s%dListener",
normalizeResourceName(service.Name),
@ -266,7 +270,7 @@ func (c client) createListener(service types.ServiceConfig, port types.ServicePo
return listenerName
}
func (c client) createTargetGroup(project *compose.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, protocol string) string {
func createTargetGroup(project *compose.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, protocol string) string {
targetGroupName := fmt.Sprintf(
"%s%s%dTargetGroup",
normalizeResourceName(service.Name),
@ -289,7 +293,7 @@ func (c client) createTargetGroup(project *compose.Project, service types.Servic
return targetGroupName
}
func (c client) createServiceRegistry(service types.ServiceConfig, template *cloudformation.Template, healthCheck *cloudmap.Service_HealthCheckConfig) ecs.Service_ServiceRegistry {
func createServiceRegistry(service types.ServiceConfig, template *cloudformation.Template, healthCheck *cloudmap.Service_HealthCheckConfig) ecs.Service_ServiceRegistry {
serviceRegistration := fmt.Sprintf("%sServiceDiscoveryEntry", normalizeResourceName(service.Name))
serviceRegistry := ecs.Service_ServiceRegistry{
RegistryArn: cloudformation.GetAtt(serviceRegistration, "Arn"),
@ -316,9 +320,9 @@ func (c client) createServiceRegistry(service types.ServiceConfig, template *clo
return serviceRegistry
}
func (c client) createTaskExecutionRole(service types.ServiceConfig, err error, definition *ecs.TaskDefinition, template *cloudformation.Template) (string, error) {
func createTaskExecutionRole(service types.ServiceConfig, err error, definition *ecs.TaskDefinition, template *cloudformation.Template) (string, error) {
taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", normalizeResourceName(service.Name))
policy, err := c.getPolicy(definition)
policy, err := getPolicy(definition)
if err != nil {
return taskExecutionRole, err
}
@ -341,7 +345,7 @@ func (c client) createTaskExecutionRole(service types.ServiceConfig, err error,
return taskExecutionRole, nil
}
func (c client) createCluster(project *compose.Project, template *cloudformation.Template) string {
func createCluster(project *compose.Project, template *cloudformation.Template) string {
template.Resources["Cluster"] = &ecs.Cluster{
ClusterName: project.Name,
Tags: []tags.Tag{
@ -356,7 +360,7 @@ func (c client) createCluster(project *compose.Project, template *cloudformation
return cluster
}
func (c client) createCloudMap(project *compose.Project, template *cloudformation.Template) {
func createCloudMap(project *compose.Project, template *cloudformation.Template) {
template.Resources["CloudMap"] = &cloudmap.PrivateDnsNamespace{
Description: fmt.Sprintf("Service Map for Docker Compose project %s", project.Name),
Name: fmt.Sprintf("%s.local", project.Name),
@ -365,7 +369,7 @@ func (c client) createCloudMap(project *compose.Project, template *cloudformatio
}
func convertNetwork(project *compose.Project, net types.NetworkConfig, vpc string, template *cloudformation.Template) string {
if sg, ok := net.Extras[ExtensionSecurityGroup]; ok {
if sg, ok := net.Extras[btypes.ExtensionSecurityGroup]; ok {
logrus.Debugf("Security Group for network %q set by user to %q", net.Name, sg)
return sg.(string)
}
@ -428,7 +432,7 @@ func normalizeResourceName(s string) string {
return strings.Title(regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(s, ""))
}
func (c client) getPolicy(taskDef *ecs.TaskDefinition) (*PolicyDocument, error) {
func getPolicy(taskDef *ecs.TaskDefinition) (*PolicyDocument, error) {
arns := []string{}
for _, container := range taskDef.ContainerDefinitions {
if container.RepositoryCredentials != nil {

View File

@ -1,4 +1,4 @@
package amazon
package backend
import (
"fmt"
@ -13,6 +13,7 @@ import (
"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"github.com/docker/ecs-plugin/pkg/compose"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)
@ -103,7 +104,7 @@ services:
}
func convertResultAsString(t *testing.T, project *compose.Project, clusterName string) string {
client, err := NewClient("", clusterName, "")
client, err := NewBackend("", clusterName, "")
assert.NilError(t, err)
result, err := client.Convert(project)
assert.NilError(t, err)
@ -133,7 +134,7 @@ func convertYaml(t *testing.T, yaml string) *cloudformation.Template {
assert.NilError(t, err)
err = compose.Normalize(model)
assert.NilError(t, err)
template, err := client{}.Convert(&compose.Project{
template, err := Backend{}.Convert(&compose.Project{
Config: *model,
Name: "test",
})

View File

@ -0,0 +1,31 @@
package backend
import (
"context"
"fmt"
"github.com/docker/ecs-plugin/pkg/amazon/types"
)
func (b *Backend) ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error {
err := b.api.DeleteStack(ctx, projectName)
if err != nil {
return err
}
err = b.WaitStackCompletion(ctx, projectName, types.StackDelete)
if err != nil {
return err
}
if !deleteCluster {
return nil
}
fmt.Printf("Delete cluster %s", b.Cluster)
if err = b.api.DeleteCluster(ctx, b.Cluster); err != nil {
return err
}
fmt.Printf("... done. \n")
return nil
}

View File

@ -1,17 +1,19 @@
package amazon
package backend
import (
"context"
"testing"
"github.com/docker/ecs-plugin/pkg/amazon/sdk"
btypes "github.com/docker/ecs-plugin/pkg/amazon/types"
"github.com/golang/mock/gomock"
)
func TestDownDontDeleteCluster(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
m := NewMockAPI(ctrl)
c := &client{
m := sdk.NewMockAPI(ctrl)
c := &Backend{
Cluster: "test_cluster",
Region: "region",
api: m,
@ -20,7 +22,7 @@ func TestDownDontDeleteCluster(t *testing.T) {
recorder := m.EXPECT()
recorder.DeleteStack(ctx, "test_project").Return(nil)
recorder.GetStackID(ctx, "test_project").Return("stack-123", nil)
recorder.WaitStackComplete(ctx, "stack-123", StackDelete).Return(nil)
recorder.WaitStackComplete(ctx, "stack-123", btypes.StackDelete).Return(nil)
recorder.DescribeStackEvents(ctx, "stack-123").Return(nil, nil)
c.ComposeDown(ctx, "test_project", false)
@ -29,8 +31,8 @@ func TestDownDontDeleteCluster(t *testing.T) {
func TestDownDeleteCluster(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
m := NewMockAPI(ctrl)
c := &client{
m := sdk.NewMockAPI(ctrl)
c := &Backend{
Cluster: "test_cluster",
Region: "region",
api: m,
@ -40,7 +42,7 @@ func TestDownDeleteCluster(t *testing.T) {
recorder := m.EXPECT()
recorder.DeleteStack(ctx, "test_project").Return(nil)
recorder.GetStackID(ctx, "test_project").Return("stack-123", nil)
recorder.WaitStackComplete(ctx, "stack-123", StackDelete).Return(nil)
recorder.WaitStackComplete(ctx, "stack-123", btypes.StackDelete).Return(nil)
recorder.DescribeStackEvents(ctx, "stack-123").Return(nil, nil)
recorder.DeleteCluster(ctx, "test_cluster").Return(nil)

View File

@ -1,4 +1,4 @@
package amazon
package backend
const (
ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"

View File

@ -0,0 +1,63 @@
package backend
import (
"context"
"fmt"
"sort"
"strings"
"github.com/docker/ecs-plugin/pkg/amazon/types"
"github.com/docker/ecs-plugin/pkg/compose"
)
func (b *Backend) ComposePs(ctx context.Context, project *compose.Project) ([]types.TaskStatus, error) {
cluster := b.Cluster
if cluster == "" {
cluster = project.Name
}
arns := []string{}
for _, service := range project.Services {
tasks, err := b.api.ListTasks(ctx, cluster, service.Name)
if err != nil {
return []types.TaskStatus{}, err
}
arns = append(arns, tasks...)
}
if len(arns) == 0 {
return []types.TaskStatus{}, nil
}
tasks, err := b.api.DescribeTasks(ctx, cluster, arns...)
if err != nil {
return []types.TaskStatus{}, err
}
networkInterfaces := []string{}
for _, t := range tasks {
if t.NetworkInterface != "" {
networkInterfaces = append(networkInterfaces, t.NetworkInterface)
}
}
publicIps, err := b.api.GetPublicIPs(ctx, networkInterfaces...)
if err != nil {
return []types.TaskStatus{}, err
}
sort.Slice(tasks, func(i, j int) bool {
return strings.Compare(tasks[i].Service, tasks[j].Service) < 0
})
for i, t := range tasks {
ports := []string{}
s, err := project.GetService(t.Service)
if err != nil {
return []types.TaskStatus{}, err
}
for _, p := range s.Ports {
ports = append(ports, fmt.Sprintf("%s:%d->%d/%s", publicIps[t.NetworkInterface], p.Published, p.Target, p.Protocol))
}
tasks[i].Name = s.Name
tasks[i].Ports = ports
}
return tasks, nil
}

View File

@ -1,4 +1,4 @@
package amazon
package backend
import (
"context"
@ -11,8 +11,8 @@ import (
"github.com/docker/ecs-plugin/pkg/console"
)
func (c *client) ComposeLogs(ctx context.Context, projectName string) error {
err := c.api.GetLogs(ctx, projectName, &logConsumer{
func (b *Backend) ComposeLogs(ctx context.Context, projectName string) error {
err := b.api.GetLogs(ctx, projectName, &logConsumer{
colors: map[string]console.ColorFunc{},
width: 0,
})
@ -26,11 +26,6 @@ func (c *client) ComposeLogs(ctx context.Context, projectName string) error {
return nil
}
type logConsumer struct {
colors map[string]console.ColorFunc
width int
}
func (l *logConsumer) Log(service, container, message string) {
cf, ok := l.colors[service]
if !ok {
@ -54,10 +49,7 @@ func (l *logConsumer) computeWidth() {
l.width = width + 3
}
type LogConsumer interface {
Log(service, container, message string)
}
type logsAPI interface {
GetLogs(ctx context.Context, name string, consumer LogConsumer) error
type logConsumer struct {
colors map[string]console.ColorFunc
width int
}

View File

@ -0,0 +1,23 @@
package backend
import (
"context"
"github.com/docker/ecs-plugin/pkg/amazon/types"
)
func (b Backend) CreateSecret(ctx context.Context, secret types.Secret) (string, error) {
return b.api.CreateSecret(ctx, secret)
}
func (b Backend) InspectSecret(ctx context.Context, id string) (types.Secret, error) {
return b.api.InspectSecret(ctx, id)
}
func (b Backend) ListSecrets(ctx context.Context) ([]types.Secret, error) {
return b.api.ListSecrets(ctx)
}
func (b Backend) DeleteSecret(ctx context.Context, id string, recover bool) error {
return b.api.DeleteSecret(ctx, id, recover)
}

View File

@ -0,0 +1,100 @@
package backend
import (
"context"
"fmt"
"github.com/docker/ecs-plugin/pkg/amazon/types"
"github.com/docker/ecs-plugin/pkg/compose"
)
func (b *Backend) ComposeUp(ctx context.Context, project *compose.Project) error {
if b.Cluster != "" {
ok, err := b.api.ClusterExists(ctx, b.Cluster)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("configured cluster %q does not exist", b.Cluster)
}
}
update, err := b.api.StackExists(ctx, project.Name)
if err != nil {
return err
}
if update {
return fmt.Errorf("we do not (yet) support updating an existing CloudFormation stack")
}
template, err := b.Convert(project)
if err != nil {
return err
}
vpc, err := b.GetVPC(ctx, project)
if err != nil {
return err
}
subNets, err := b.api.GetSubNets(ctx, vpc)
if err != nil {
return err
}
lb, err := b.GetLoadBalancer(ctx, project)
if err != nil {
return err
}
parameters := map[string]string{
ParameterClusterName: b.Cluster,
ParameterVPCId: vpc,
ParameterSubnet1Id: subNets[0],
ParameterSubnet2Id: subNets[1],
ParameterLoadBalancerARN: lb,
}
err = b.api.CreateStack(ctx, project.Name, template, parameters)
if err != nil {
return err
}
fmt.Println()
return b.WaitStackCompletion(ctx, project.Name, types.StackCreate)
}
func (b Backend) GetVPC(ctx context.Context, project *compose.Project) (string, error) {
//check compose file for custom VPC selected
if vpc, ok := project.Extras[types.ExtensionVPC]; ok {
vpcID := vpc.(string)
ok, err := b.api.VpcExists(ctx, vpcID)
if err != nil {
return "", err
}
if !ok {
return "", fmt.Errorf("VPC does not exist: %s", vpc)
}
}
defaultVPC, err := b.api.GetDefaultVPC(ctx)
if err != nil {
return "", err
}
return defaultVPC, nil
}
func (b Backend) GetLoadBalancer(ctx context.Context, project *compose.Project) (string, error) {
//check compose file for custom VPC selected
if lb, ok := project.Extras[types.ExtensionLB]; ok {
lbName := lb.(string)
ok, err := b.api.LoadBalancerExists(ctx, lbName)
if err != nil {
return "", err
}
if !ok {
return "", fmt.Errorf("Load Balancer does not exist: %s", lb)
}
return b.api.GetLoadBalancerARN(ctx, lbName)
}
return "", nil
}

View File

@ -1,4 +1,4 @@
package amazon
package backend
import (
"context"
@ -8,16 +8,15 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/docker/ecs-plugin/pkg/console"
)
func (c *client) WaitStackCompletion(ctx context.Context, name string, operation int) error {
func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operation int) error {
w := console.NewProgressWriter()
knownEvents := map[string]struct{}{}
// Get the unique Stack ID so we can collect events without getting some from previous deployments with same name
stackID, err := c.api.GetStackID(ctx, name)
stackID, err := b.api.GetStackID(ctx, name)
if err != nil {
return err
}
@ -26,7 +25,7 @@ func (c *client) WaitStackCompletion(ctx context.Context, name string, operation
done := make(chan bool)
go func() {
c.api.WaitStackComplete(ctx, stackID, operation) //nolint:errcheck
b.api.WaitStackComplete(ctx, stackID, operation) //nolint:errcheck
ticker.Stop()
done <- true
}()
@ -39,7 +38,7 @@ func (c *client) WaitStackCompletion(ctx context.Context, name string, operation
completed = true
case <-ticker.C:
}
events, err := c.api.DescribeStackEvents(ctx, stackID)
events, err := b.api.DescribeStackEvents(ctx, stackID)
if err != nil {
return err
}
@ -65,14 +64,3 @@ func (c *client) WaitStackCompletion(ctx context.Context, name string, operation
}
return stackErr
}
type waitAPI interface {
GetStackID(ctx context.Context, name string) (string, error)
WaitStackComplete(ctx context.Context, name string, operation int) error
DescribeStackEvents(ctx context.Context, stackID string) ([]*cloudformation.StackEvent, error)
}
const (
StackCreate = iota
StackDelete
)

View File

@ -1,13 +0,0 @@
package amazon
import (
"testing"
"gotest.tools/v3/assert"
)
func TestInvalidNetworkMode(t *testing.T) {
project := load(t, "testdata/invalid_network_mode.yaml")
err := Check(project)
assert.Error(t, err[0], "'network_mode' \"bridge\" is not supported")
}

View File

@ -1,4 +1,4 @@
package amazon
package compatibility
import (
"github.com/compose-spec/compose-go/types"

View File

@ -0,0 +1,23 @@
package compatibility
import (
"testing"
"github.com/docker/ecs-plugin/pkg/compose"
"gotest.tools/v3/assert"
)
func load(t *testing.T, paths ...string) *compose.Project {
options := compose.ProjectOptions{
Name: t.Name(),
ConfigPaths: paths,
}
project, err := compose.ProjectFromOptions(&options)
assert.NilError(t, err)
return project
}
func TestInvalidNetworkMode(t *testing.T) {
project := load(t, "../backend/testdata/invalid_network_mode.yaml")
err := Check(project)
assert.Error(t, err[0], "'network_mode' \"bridge\" is not supported")
}

View File

@ -1,4 +1,4 @@
package amazon
package compatibility
import (
"fmt"

View File

@ -1,34 +0,0 @@
package amazon
import (
"context"
"fmt"
)
func (c *client) ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error {
err := c.api.DeleteStack(ctx, projectName)
if err != nil {
return err
}
err = c.WaitStackCompletion(ctx, projectName, StackDelete)
if err != nil {
return err
}
if !deleteCluster {
return nil
}
fmt.Printf("Delete cluster %s", c.Cluster)
if err = c.api.DeleteCluster(ctx, c.Cluster); err != nil {
return err
}
fmt.Printf("... done. \n")
return nil
}
type downAPI interface {
DeleteStack(ctx context.Context, name string) error
DeleteCluster(ctx context.Context, name string) error
}

View File

@ -1,80 +0,0 @@
package amazon
import (
"context"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/docker/ecs-plugin/pkg/compose"
)
func (c *client) ComposePs(ctx context.Context, project *compose.Project) error {
cluster := c.Cluster
if cluster == "" {
cluster = project.Name
}
w := tabwriter.NewWriter(os.Stdout, 20, 2, 3, ' ', 0)
fmt.Fprintf(w, "Name\tState\tPorts\n")
defer w.Flush()
arns := []string{}
for _, service := range project.Services {
tasks, err := c.api.ListTasks(ctx, cluster, service.Name)
if err != nil {
return err
}
arns = append(arns, tasks...)
}
if len(arns) == 0 {
return nil
}
tasks, err := c.api.DescribeTasks(ctx, cluster, arns...)
if err != nil {
return err
}
networkInterfaces := []string{}
for _, t := range tasks {
if t.NetworkInterface != "" {
networkInterfaces = append(networkInterfaces, t.NetworkInterface)
}
}
publicIps, err := c.api.GetPublicIPs(ctx, networkInterfaces...)
if err != nil {
return err
}
sort.Slice(tasks, func(i, j int) bool {
return strings.Compare(tasks[i].Service, tasks[j].Service) < 0
})
for _, t := range tasks {
ports := []string{}
s, err := project.GetService(t.Service)
if err != nil {
return err
}
for _, p := range s.Ports {
ports = append(ports, fmt.Sprintf("%s:%d->%d/%s", publicIps[t.NetworkInterface], p.Published, p.Target, p.Protocol))
}
fmt.Fprintf(w, "%s\t%s\t%s\n", s.Name, t.State, strings.Join(ports, ", "))
}
return nil
}
type TaskStatus struct {
State string
Service string
NetworkInterface string
PublicIP string
}
type listAPI interface {
ListTasks(ctx context.Context, cluster string, name string) ([]string, error)
DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]TaskStatus, error)
GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error)
}

61
ecs/pkg/amazon/sdk/api.go Normal file
View File

@ -0,0 +1,61 @@
package sdk
import (
"context"
cf "github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/awslabs/goformation/v4/cloudformation"
"github.com/docker/ecs-plugin/pkg/amazon/types"
)
//go:generate mockgen -destination=./api_mock.go -self_package "github.com/docker/ecs-plugin/pkg/amazon" -package=amazon . API
type API interface {
downAPI
upAPI
logsAPI
secretsAPI
listAPI
}
type upAPI interface {
waitAPI
GetDefaultVPC(ctx context.Context) (string, error)
VpcExists(ctx context.Context, vpcID string) (bool, error)
GetSubNets(ctx context.Context, vpcID string) ([]string, error)
ClusterExists(ctx context.Context, name string) (bool, error)
StackExists(ctx context.Context, name string) (bool, error)
CreateStack(ctx context.Context, name string, template *cloudformation.Template, parameters map[string]string) error
LoadBalancerExists(ctx context.Context, name string) (bool, error)
GetLoadBalancerARN(ctx context.Context, name string) (string, error)
}
type downAPI interface {
DeleteStack(ctx context.Context, name string) error
DeleteCluster(ctx context.Context, name string) error
}
type logsAPI interface {
GetLogs(ctx context.Context, name string, consumer types.LogConsumer) error
}
type secretsAPI interface {
CreateSecret(ctx context.Context, secret types.Secret) (string, error)
InspectSecret(ctx context.Context, id string) (types.Secret, error)
ListSecrets(ctx context.Context) ([]types.Secret, error)
DeleteSecret(ctx context.Context, id string, recover bool) error
}
type listAPI interface {
ListTasks(ctx context.Context, cluster string, name string) ([]string, error)
DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]types.TaskStatus, error)
GetPublicIPs(ctx context.Context, interfaces ...string) (map[string]string, error)
}
type waitAPI interface {
GetStackID(ctx context.Context, name string) (string, error)
WaitStackComplete(ctx context.Context, name string, operation int) error
DescribeStackEvents(ctx context.Context, stackID string) ([]*cf.StackEvent, error)
}

View File

@ -2,7 +2,7 @@
// Source: github.com/docker/ecs-plugin/pkg/amazon (interfaces: API)
// Package amazon is a generated GoMock package.
package amazon
package sdk
import (
context "context"
@ -10,7 +10,7 @@ import (
cloudformation "github.com/aws/aws-sdk-go/service/cloudformation"
cloudformation0 "github.com/awslabs/goformation/v4/cloudformation"
docker "github.com/docker/ecs-plugin/pkg/docker"
btypes "github.com/docker/ecs-plugin/pkg/amazon/types"
gomock "github.com/golang/mock/gomock"
)
@ -53,7 +53,7 @@ func (mr *MockAPIMockRecorder) ClusterExists(arg0, arg1 interface{}) *gomock.Cal
}
// CreateSecret mocks base method
func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 docker.Secret) (string, error) {
func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 btypes.Secret) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateSecret", arg0, arg1)
ret0, _ := ret[0].(string)
@ -139,14 +139,14 @@ func (mr *MockAPIMockRecorder) DescribeStackEvents(arg0, arg1 interface{}) *gomo
}
// DescribeTasks mocks base method
func (m *MockAPI) DescribeTasks(arg0 context.Context, arg1 string, arg2 ...string) ([]TaskStatus, error) {
func (m *MockAPI) DescribeTasks(arg0 context.Context, arg1 string, arg2 ...string) ([]btypes.TaskStatus, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "DescribeTasks", varargs...)
ret0, _ := ret[0].([]TaskStatus)
ret0, _ := ret[0].([]btypes.TaskStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -174,7 +174,7 @@ func (mr *MockAPIMockRecorder) GetDefaultVPC(arg0 interface{}) *gomock.Call {
}
// GetLogs mocks base method
func (m *MockAPI) GetLogs(arg0 context.Context, arg1 string, arg2 LogConsumer) error {
func (m *MockAPI) GetLogs(arg0 context.Context, arg1 string, arg2 btypes.LogConsumer) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetLogs", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
@ -238,10 +238,10 @@ func (mr *MockAPIMockRecorder) GetSubNets(arg0, arg1 interface{}) *gomock.Call {
}
// InspectSecret mocks base method
func (m *MockAPI) InspectSecret(arg0 context.Context, arg1 string) (docker.Secret, error) {
func (m *MockAPI) InspectSecret(arg0 context.Context, arg1 string) (btypes.Secret, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InspectSecret", arg0, arg1)
ret0, _ := ret[0].(docker.Secret)
ret0, _ := ret[0].(btypes.Secret)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -253,10 +253,10 @@ func (mr *MockAPIMockRecorder) InspectSecret(arg0, arg1 interface{}) *gomock.Cal
}
// ListSecrets mocks base method
func (m *MockAPI) ListSecrets(arg0 context.Context) ([]docker.Secret, error) {
func (m *MockAPI) ListSecrets(arg0 context.Context) ([]btypes.Secret, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListSecrets", arg0)
ret0, _ := ret[0].([]docker.Secret)
ret0, _ := ret[0].([]btypes.Secret)
ret1, _ := ret[1].(error)
return ret0, ret1
}

View File

@ -1,4 +1,4 @@
package amazon
package sdk
import (
"fmt"
@ -13,6 +13,7 @@ import (
"github.com/awslabs/goformation/v4/cloudformation/tags"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/opts"
t "github.com/docker/ecs-plugin/pkg/amazon/types"
"github.com/docker/ecs-plugin/pkg/compose"
)
@ -318,7 +319,7 @@ func getImage(image string) string {
func getRepoCredentials(service types.ServiceConfig) *ecs.TaskDefinition_RepositoryCredentials {
// extract registry and namespace string from image name
for key, value := range service.Extras {
if key == ExtensionPullCredentials {
if key == t.ExtensionPullCredentials {
return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: value.(string)}
}
}

View File

@ -1,4 +1,4 @@
package amazon
package sdk
import (
"context"
@ -25,7 +25,8 @@ import (
cf "github.com/awslabs/goformation/v4/cloudformation"
"github.com/sirupsen/logrus"
"github.com/docker/ecs-plugin/pkg/docker"
"github.com/docker/ecs-plugin/pkg/amazon/types"
t "github.com/docker/ecs-plugin/pkg/amazon/types"
)
type sdk struct {
@ -188,9 +189,9 @@ func (s sdk) WaitStackComplete(ctx context.Context, name string, operation int)
StackName: aws.String(name),
}
switch operation {
case StackCreate:
case t.StackCreate:
return s.CF.WaitUntilStackCreateCompleteWithContext(ctx, input)
case StackDelete:
case t.StackDelete:
return s.CF.WaitUntilStackDeleteCompleteWithContext(ctx, input)
default:
return fmt.Errorf("internal error: unexpected stack operation %d", operation)
@ -235,7 +236,7 @@ func (s sdk) DeleteStack(ctx context.Context, name string) error {
return err
}
func (s sdk) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
func (s sdk) CreateSecret(ctx context.Context, secret t.Secret) (string, error) {
logrus.Debug("Create secret " + secret.Name)
secretStr, err := secret.GetCredString()
if err != nil {
@ -253,17 +254,17 @@ func (s sdk) CreateSecret(ctx context.Context, secret docker.Secret) (string, er
return *response.ARN, nil
}
func (s sdk) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
func (s sdk) InspectSecret(ctx context.Context, id string) (t.Secret, error) {
logrus.Debug("Inspect secret " + id)
response, err := s.SM.DescribeSecret(&secretsmanager.DescribeSecretInput{SecretId: &id})
if err != nil {
return docker.Secret{}, err
return t.Secret{}, err
}
labels := map[string]string{}
for _, tag := range response.Tags {
labels[*tag.Key] = *tag.Value
}
secret := docker.Secret{
secret := t.Secret{
ID: *response.ARN,
Name: *response.Name,
Labels: labels,
@ -274,14 +275,14 @@ func (s sdk) InspectSecret(ctx context.Context, id string) (docker.Secret, error
return secret, nil
}
func (s sdk) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
func (s sdk) ListSecrets(ctx context.Context) ([]t.Secret, error) {
logrus.Debug("List secrets ...")
response, err := s.SM.ListSecrets(&secretsmanager.ListSecretsInput{})
if err != nil {
return []docker.Secret{}, err
return []t.Secret{}, err
}
var secrets []docker.Secret
var secrets []t.Secret
for _, sec := range response.SecretList {
@ -293,7 +294,7 @@ func (s sdk) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
if sec.Description != nil {
description = *sec.Description
}
secrets = append(secrets, docker.Secret{
secrets = append(secrets, t.Secret{
ID: *sec.ARN,
Name: *sec.Name,
Labels: labels,
@ -310,7 +311,7 @@ func (s sdk) DeleteSecret(ctx context.Context, id string, recover bool) error {
return err
}
func (s sdk) GetLogs(ctx context.Context, name string, consumer LogConsumer) error {
func (s sdk) GetLogs(ctx context.Context, name string, consumer types.LogConsumer) error {
logGroup := fmt.Sprintf("/docker-compose/%s", name)
var startTime int64
for {
@ -356,7 +357,7 @@ func (s sdk) ListTasks(ctx context.Context, cluster string, service string) ([]s
return arns, nil
}
func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]TaskStatus, error) {
func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string) ([]t.TaskStatus, error) {
tasks, err := s.ECS.DescribeTasksWithContext(ctx, &ecs.DescribeTasksInput{
Cluster: aws.String(cluster),
Tasks: aws.StringSlice(arns),
@ -364,7 +365,7 @@ func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string)
if err != nil {
return nil, err
}
result := []TaskStatus{}
result := []t.TaskStatus{}
for _, task := range tasks.Tasks {
var networkInterface string
for _, attachement := range task.Attachments {
@ -376,7 +377,7 @@ func (s sdk) DescribeTasks(ctx context.Context, cluster string, arns ...string)
}
}
}
result = append(result, TaskStatus{
result = append(result, t.TaskStatus{
State: *task.LastStatus,
Service: strings.Replace(*task.Group, "service:", "", 1),
NetworkInterface: networkInterface,

View File

@ -1,30 +0,0 @@
package amazon
import (
"context"
"github.com/docker/ecs-plugin/pkg/docker"
)
type secretsAPI interface {
CreateSecret(ctx context.Context, secret docker.Secret) (string, error)
InspectSecret(ctx context.Context, id string) (docker.Secret, error)
ListSecrets(ctx context.Context) ([]docker.Secret, error)
DeleteSecret(ctx context.Context, id string, recover bool) error
}
func (c client) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
return c.api.CreateSecret(ctx, secret)
}
func (c client) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
return c.api.InspectSecret(ctx, id)
}
func (c client) ListSecrets(ctx context.Context) ([]docker.Secret, error) {
return c.api.ListSecrets(ctx)
}
func (c client) DeleteSecret(ctx context.Context, id string, recover bool) error {
return c.api.DeleteSecret(ctx, id, recover)
}

View File

@ -1,9 +1,25 @@
package docker
package types
import (
"encoding/json"
import "encoding/json"
type TaskStatus struct {
Name string
State string
Service string
NetworkInterface string
PublicIP string
Ports []string
}
const (
StackCreate = iota
StackDelete
)
type LogConsumer interface {
Log(service, container, message string)
}
type Secret struct {
ID string `json:"ID"`
Name string `json:"Name"`

View File

@ -1,4 +1,4 @@
package amazon
package types
const (
ExtensionSecurityGroup = "x-aws-securitygroup"

View File

@ -1,114 +0,0 @@
package amazon
import (
"context"
"fmt"
"github.com/awslabs/goformation/v4/cloudformation"
"github.com/docker/ecs-plugin/pkg/compose"
)
func (c *client) ComposeUp(ctx context.Context, project *compose.Project) error {
if c.Cluster != "" {
ok, err := c.api.ClusterExists(ctx, c.Cluster)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("configured cluster %q does not exist", c.Cluster)
}
}
update, err := c.api.StackExists(ctx, project.Name)
if err != nil {
return err
}
if update {
return fmt.Errorf("we do not (yet) support updating an existing CloudFormation stack")
}
template, err := c.Convert(project)
if err != nil {
return err
}
vpc, err := c.GetVPC(ctx, project)
if err != nil {
return err
}
subNets, err := c.api.GetSubNets(ctx, vpc)
if err != nil {
return err
}
lb, err := c.GetLoadBalancer(ctx, project)
if err != nil {
return err
}
parameters := map[string]string{
ParameterClusterName: c.Cluster,
ParameterVPCId: vpc,
ParameterSubnet1Id: subNets[0],
ParameterSubnet2Id: subNets[1],
ParameterLoadBalancerARN: lb,
}
err = c.api.CreateStack(ctx, project.Name, template, parameters)
if err != nil {
return err
}
fmt.Println()
return c.WaitStackCompletion(ctx, project.Name, StackCreate)
}
func (c client) GetVPC(ctx context.Context, project *compose.Project) (string, error) {
//check compose file for custom VPC selected
if vpc, ok := project.Extras[ExtensionVPC]; ok {
vpcID := vpc.(string)
ok, err := c.api.VpcExists(ctx, vpcID)
if err != nil {
return "", err
}
if !ok {
return "", fmt.Errorf("VPC does not exist: %s", vpc)
}
}
defaultVPC, err := c.api.GetDefaultVPC(ctx)
if err != nil {
return "", err
}
return defaultVPC, nil
}
func (c client) GetLoadBalancer(ctx context.Context, project *compose.Project) (string, error) {
//check compose file for custom VPC selected
if lb, ok := project.Extras[ExtensionLB]; ok {
lbName := lb.(string)
ok, err := c.api.LoadBalancerExists(ctx, lbName)
if err != nil {
return "", err
}
if !ok {
return "", fmt.Errorf("Load Balancer does not exist: %s", lb)
}
return c.api.GetLoadBalancerARN(ctx, lbName)
}
return "", nil
}
type upAPI interface {
waitAPI
GetDefaultVPC(ctx context.Context) (string, error)
VpcExists(ctx context.Context, vpcID string) (bool, error)
GetSubNets(ctx context.Context, vpcID string) ([]string, error)
ClusterExists(ctx context.Context, name string) (bool, error)
StackExists(ctx context.Context, name string) (bool, error)
CreateStack(ctx context.Context, name string, template *cloudformation.Template, parameters map[string]string) error
LoadBalancerExists(ctx context.Context, name string) (bool, error)
GetLoadBalancerARN(ctx context.Context, name string) (string, error)
}

View File

@ -4,7 +4,7 @@ import (
"context"
"github.com/awslabs/goformation/v4/cloudformation"
"github.com/docker/ecs-plugin/pkg/docker"
"github.com/docker/ecs-plugin/pkg/amazon/types"
)
type API interface {
@ -13,9 +13,9 @@ type API interface {
ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error
ComposeLogs(ctx context.Context, projectName string) error
CreateSecret(ctx context.Context, secret docker.Secret) (string, error)
InspectSecret(ctx context.Context, id string) (docker.Secret, error)
ListSecrets(ctx context.Context) ([]docker.Secret, error)
CreateSecret(ctx context.Context, secret types.Secret) (string, error)
InspectSecret(ctx context.Context, id string) (types.Secret, error)
ListSecrets(ctx context.Context) ([]types.Secret, error)
DeleteSecret(ctx context.Context, id string, recover bool) error
ComposePs(background context.Context, project *Project) error
ComposePs(background context.Context, project *Project) ([]types.TaskStatus, error)
}