don't set service `Name` so they can be updated by CloudFormation

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-06-25 08:14:54 +02:00
parent a1eba59a46
commit 934e7ab9ea
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
13 changed files with 103 additions and 53 deletions

View File

@ -9,7 +9,8 @@ ENV GO111MODULE=on
ARG ALPINE_PKG_DOCKER_VERSION
RUN apk add --no-cache \
docker=${ALPINE_PKG_DOCKER_VERSION} \
make
make \
build-base
COPY go.* .
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
@ -18,7 +19,6 @@ COPY . .
FROM base AS make-plugin
ARG TARGETOS
ARG TARGETARCH
RUN apk add build-base
RUN GO111MODULE=on go get github.com/golang/mock/mockgen@latest
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \

View File

@ -14,7 +14,7 @@ require (
github.com/bugsnag/panicwrap v1.2.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cloudflare/cfssl v1.4.1 // indirect
github.com/compose-spec/compose-go v0.0.0-20200622094647-0bb9a6c7d89a
github.com/compose-spec/compose-go v0.0.0-20200624120600-614475470cd8
github.com/containerd/containerd v1.3.2 // indirect
github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb // indirect
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492

View File

@ -60,6 +60,10 @@ github.com/compose-spec/compose-go v0.0.0-20200617133919-fca3bb55c5cc h1:jZfF+Hz
github.com/compose-spec/compose-go v0.0.0-20200617133919-fca3bb55c5cc/go.mod h1:d3Vb4tH01Pr4YKD3RvfwguRcezDBUYJTVYgpCSRYSVg=
github.com/compose-spec/compose-go v0.0.0-20200622094647-0bb9a6c7d89a h1:FmEuebUePUA0Kd/NSiCmdPG/n6eKdZdBtIbfejVtRS8=
github.com/compose-spec/compose-go v0.0.0-20200622094647-0bb9a6c7d89a/go.mod h1:ih9anT8po+49hrb+1j3ldIJ/YRAaBH52ErlQLTKE2Yo=
github.com/compose-spec/compose-go v0.0.0-20200624090650-5d46d553c1e6 h1:9rsA2PlPOv50IOnzSiTqCWrWr3u2q7shPr76Y5hlxF0=
github.com/compose-spec/compose-go v0.0.0-20200624090650-5d46d553c1e6/go.mod h1:ih9anT8po+49hrb+1j3ldIJ/YRAaBH52ErlQLTKE2Yo=
github.com/compose-spec/compose-go v0.0.0-20200624120600-614475470cd8 h1:sVvKsoXizFOuJNc8dM91IeET2/zDNFj3hwHgk437iJ8=
github.com/compose-spec/compose-go v0.0.0-20200624120600-614475470cd8/go.mod h1:ih9anT8po+49hrb+1j3ldIJ/YRAaBH52ErlQLTKE2Yo=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=

View File

@ -6,12 +6,6 @@ import (
"github.com/docker/ecs-plugin/pkg/amazon/sdk"
)
const (
ProjectTag = "com.docker.compose.project"
NetworkTag = "com.docker.compose.network"
ServiceTag = "com.docker.compose.service"
)
func NewBackend(profile string, cluster string, region string) (*Backend, error) {
sess, err := session.NewSessionWithOptions(session.Options{
Profile: profile,

View File

@ -31,13 +31,22 @@ const (
)
type FargateCompatibilityChecker struct {
*compatibility.AllowList
compatibility.AllowList
}
func (c *FargateCompatibilityChecker) CheckPortsPublished(p *types.ServicePortConfig) {
if p.Published == 0 {
p.Published = p.Target
}
if p.Published != p.Target {
c.Error("published port can't be set to a distinct value than container port")
}
}
// Convert a compose project into a CloudFormation template
func (b Backend) Convert(project *types.Project) (*cloudformation.Template, error) {
var checker compatibility.Checker = FargateCompatibilityChecker{
&compatibility.AllowList{
var checker compatibility.Checker = &FargateCompatibilityChecker{
compatibility.AllowList{
Supported: []string{
"services.command",
"services.container_name",
@ -161,7 +170,7 @@ func (b Backend) Convert(project *types.Project) (*cloudformation.Template, erro
dependsOn = append(dependsOn, listenerName)
serviceLB = append(serviceLB, ecs.Service_LoadBalancer{
ContainerName: service.Name,
ContainerPort: int(port.Published),
ContainerPort: int(port.Target),
TargetGroupArn: cloudformation.Ref(targetGroupName),
})
}
@ -195,11 +204,11 @@ func (b Backend) Convert(project *types.Project) (*cloudformation.Template, erro
ServiceRegistries: []ecs.Service_ServiceRegistry{serviceRegistry},
Tags: []tags.Tag{
{
Key: ProjectTag,
Key: compose.ProjectTag,
Value: project.Name,
},
{
Key: ServiceTag,
Key: compose.ServiceTag,
Value: service.Name,
},
},
@ -252,7 +261,7 @@ func createLoadBalancer(project *types.Project, template *cloudformation.Templat
},
Tags: []tags.Tag{
{
Key: ProjectTag,
Key: compose.ProjectTag,
Value: project.Name,
},
},
@ -267,7 +276,7 @@ func createListener(service types.ServiceConfig, port types.ServicePortConfig, t
"%s%s%dListener",
normalizeResourceName(service.Name),
strings.ToUpper(port.Protocol),
port.Published,
port.Target,
)
//add listener to dependsOn
//https://stackoverflow.com/questions/53971873/the-target-group-does-not-have-an-associated-load-balancer
@ -286,7 +295,7 @@ func createListener(service types.ServiceConfig, port types.ServicePortConfig, t
},
LoadBalancerArn: loadBalancerARN,
Protocol: protocol,
Port: int(port.Published),
Port: int(port.Target),
}
return listenerName
}
@ -304,7 +313,7 @@ func createTargetGroup(project *types.Project, service types.ServiceConfig, port
Protocol: protocol,
Tags: []tags.Tag{
{
Key: ProjectTag,
Key: compose.ProjectTag,
Value: project.Name,
},
},
@ -371,7 +380,7 @@ func createCluster(project *types.Project, template *cloudformation.Template) st
ClusterName: project.Name,
Tags: []tags.Tag{
{
Key: ProjectTag,
Key: compose.ProjectTag,
Value: project.Name,
},
},
@ -420,11 +429,11 @@ func convertNetwork(project *types.Project, net types.NetworkConfig, vpc string,
VpcId: vpc,
Tags: []tags.Tag{
{
Key: ProjectTag,
Key: compose.ProjectTag,
Value: project.Name,
},
{
Key: NetworkTag,
Key: compose.NetworkTag,
Value: net.Name,
},
},

View File

@ -5,6 +5,7 @@ import (
"github.com/compose-spec/compose-go/cli"
"github.com/docker/ecs-plugin/pkg/compose"
"github.com/docker/ecs-plugin/pkg/console"
)
func (b *Backend) Down(ctx context.Context, options cli.ProjectOptions) error {
@ -22,7 +23,8 @@ func (b *Backend) Down(ctx context.Context, options cli.ProjectOptions) error {
return err
}
err = b.WaitStackCompletion(ctx, name, compose.StackDelete)
w := console.NewProgressWriter()
err = b.WaitStackCompletion(ctx, name, compose.StackDelete, w)
if err != nil {
return err
}

View File

@ -14,18 +14,22 @@ func (b *Backend) Ps(ctx context.Context, project *types.Project) ([]compose.Ser
cluster = project.Name
}
status := []compose.ServiceStatus{}
for _, service := range project.Services {
desc, err := b.api.DescribeService(ctx, cluster, service.Name)
status, err := b.api.DescribeServices(ctx, cluster, project.Name)
if err != nil {
return nil, err
}
for i, state := range status {
s, err := project.GetService(state.Name)
if err != nil {
return nil, err
}
ports := []string{}
for _, p := range service.Ports {
for _, p := range s.Ports {
ports = append(ports, fmt.Sprintf("*:%d->%d/%s", p.Published, p.Target, p.Protocol))
}
desc.Ports = ports
status = append(status, desc)
state.Ports = ports
status[i] = state
}
return status, nil
}

View File

@ -7,6 +7,7 @@ import (
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types"
"github.com/docker/ecs-plugin/pkg/compose"
"github.com/docker/ecs-plugin/pkg/console"
)
func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error {
@ -67,7 +68,11 @@ func (b *Backend) Up(ctx context.Context, options cli.ProjectOptions) error {
}
fmt.Println()
return b.WaitStackCompletion(ctx, project.Name, compose.StackCreate)
w := console.NewProgressWriter()
for k := range template.Resources {
w.ResourceEvent(k, "PENDING", "")
}
return b.WaitStackCompletion(ctx, project.Name, compose.StackCreate, w)
}
func (b Backend) GetVPC(ctx context.Context, project *types.Project) (string, error) {

View File

@ -11,8 +11,7 @@ import (
"github.com/docker/ecs-plugin/pkg/console"
)
func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operation int) error {
w := console.NewProgressWriter()
func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operation int, w console.ProgressWriter) error {
knownEvents := map[string]struct{}{}
// Get the unique Stack ID so we can collect events without getting some from previous deployments with same name
@ -53,7 +52,7 @@ func (b *Backend) WaitStackCompletion(ctx context.Context, name string, operatio
}
knownEvents[*event.EventId] = struct{}{}
resource := fmt.Sprintf("%s %q", aws.StringValue(event.ResourceType), aws.StringValue(event.LogicalResourceId))
resource := aws.StringValue(event.LogicalResourceId)
reason := aws.StringValue(event.ResourceStatusReason)
status := aws.StringValue(event.ResourceStatus)
w.ResourceEvent(resource, status, reason)

View File

@ -49,7 +49,7 @@ type secretsAPI interface {
}
type listAPI interface {
DescribeService(ctx context.Context, cluster string, name string) (compose.ServiceStatus, error)
DescribeServices(ctx context.Context, cluster string, project string) ([]compose.ServiceStatus, error)
}
type waitAPI interface {

View File

@ -122,19 +122,19 @@ func (mr *MockAPIMockRecorder) DeleteStack(arg0, arg1 interface{}) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStack", reflect.TypeOf((*MockAPI)(nil).DeleteStack), arg0, arg1)
}
// DescribeService mocks base method
func (m *MockAPI) DescribeService(arg0 context.Context, arg1, arg2 string) (compose.ServiceStatus, error) {
// DescribeServices mocks base method
func (m *MockAPI) DescribeServices(arg0 context.Context, arg1, arg2 string) ([]compose.ServiceStatus, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DescribeService", arg0, arg1, arg2)
ret0, _ := ret[0].(compose.ServiceStatus)
ret := m.ctrl.Call(m, "DescribeServices", arg0, arg1, arg2)
ret0, _ := ret[0].([]compose.ServiceStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DescribeService indicates an expected call of DescribeService
func (mr *MockAPIMockRecorder) DescribeService(arg0, arg1, arg2 interface{}) *gomock.Call {
// DescribeServices indicates an expected call of DescribeServices
func (mr *MockAPIMockRecorder) DescribeServices(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeService", reflect.TypeOf((*MockAPI)(nil).DescribeService), arg0, arg1, arg2)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeServices", reflect.TypeOf((*MockAPI)(nil).DescribeServices), arg0, arg1, arg2)
}
// DescribeStackEvents mocks base method

View File

@ -175,7 +175,7 @@ func (s sdk) CreateStack(ctx context.Context, name string, template *cf.Template
StackName: aws.String(name),
TemplateBody: aws.String(string(json)),
Parameters: param,
TimeoutInMinutes: aws.Int64(10),
TimeoutInMinutes: aws.Int64(15),
Capabilities: []*string{
aws.String(cloudformation.CapabilityCapabilityIam),
},
@ -341,20 +341,46 @@ func (s sdk) GetLogs(ctx context.Context, name string, consumer compose.LogConsu
}
}
func (s sdk) DescribeService(ctx context.Context, cluster string, name string) (compose.ServiceStatus, error) {
services, err := s.ECS.DescribeServicesWithContext(ctx, &ecs.DescribeServicesInput{
Cluster: aws.String(cluster),
Services: aws.StringSlice([]string{name}),
func (s sdk) DescribeServices(ctx context.Context, cluster string, project string) ([]compose.ServiceStatus, error) {
// TODO handle pagination
list, err := s.ECS.ListServicesWithContext(ctx, &ecs.ListServicesInput{
Cluster: aws.String(cluster),
})
if err != nil {
return compose.ServiceStatus{}, err
return nil, err
}
return compose.ServiceStatus{
ID: *services.Services[0].ServiceName,
Name: name,
Replicas: int(*services.Services[0].RunningCount),
Desired: int(*services.Services[0].DesiredCount),
}, nil
services, err := s.ECS.DescribeServicesWithContext(ctx, &ecs.DescribeServicesInput{
Cluster: aws.String(cluster),
Services: list.ServiceArns,
})
if err != nil {
return nil, err
}
status := []compose.ServiceStatus{}
for _, service := range services.Services {
var name string
var stack string
for _, t := range service.Tags {
switch *t.Key {
case compose.ProjectTag:
stack = *t.Value
case compose.ServiceTag:
name = *t.Value
}
}
if stack != project {
continue
}
status = append(status, compose.ServiceStatus{
ID: *service.ServiceName,
Name: name,
Replicas: int(*services.Services[0].RunningCount),
Desired: int(*services.Services[0].DesiredCount),
})
}
return status, nil
}
func (s sdk) ListTasks(ctx context.Context, cluster string, family string) ([]string, error) {

7
ecs/pkg/compose/tags.go Normal file
View File

@ -0,0 +1,7 @@
package compose
const (
ProjectTag = "com.docker.compose.project"
NetworkTag = "com.docker.compose.network"
ServiceTag = "com.docker.compose.service"
)