mirror of https://github.com/docker/compose.git
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:
parent
d36b9b104e
commit
bb98dae082
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{}
|
|
@ -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
|
||||
}
|
|
@ -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{}
|
|
@ -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 {
|
|
@ -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",
|
||||
})
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package amazon
|
||||
package backend
|
||||
|
||||
const (
|
||||
ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package amazon
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"github.com/compose-spec/compose-go/types"
|
|
@ -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")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package amazon
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)}
|
||||
}
|
||||
}
|
|
@ -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,
|
|
@ -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)
|
||||
}
|
|
@ -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"`
|
|
@ -1,4 +1,4 @@
|
|||
package amazon
|
||||
package types
|
||||
|
||||
const (
|
||||
ExtensionSecurityGroup = "x-aws-securitygroup"
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue