mirror of https://github.com/docker/compose.git
API calls to register services matching compose.yaml
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
91daf0dcc0
commit
4542e05ddf
|
@ -3,6 +3,10 @@ package amazon
|
||||||
import (
|
import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ecs"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
"github.com/docker/ecs-plugin/pkg/compose"
|
"github.com/docker/ecs-plugin/pkg/compose"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,6 +29,10 @@ func NewClient(profile string, cluster string, region string) (compose.API, erro
|
||||||
Cluster: cluster,
|
Cluster: cluster,
|
||||||
Region: region,
|
Region: region,
|
||||||
sess: sess,
|
sess: sess,
|
||||||
|
ECS: ecs.New(sess),
|
||||||
|
EC2: ec2.New(sess),
|
||||||
|
CW: cloudwatchlogs.New(sess),
|
||||||
|
IAM: iam.New(sess),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +40,10 @@ type client struct {
|
||||||
Cluster string
|
Cluster string
|
||||||
Region string
|
Region string
|
||||||
sess *session.Session
|
sess *session.Session
|
||||||
|
ECS *ecs.ECS
|
||||||
|
EC2 *ec2.EC2
|
||||||
|
CW *cloudwatchlogs.CloudWatchLogs
|
||||||
|
IAM *iam.IAM
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ compose.API = &client{}
|
var _ compose.API = &client{}
|
||||||
|
|
|
@ -1,11 +1,79 @@
|
||||||
package amazon
|
package amazon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ecs"
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/ecs-plugin/pkg/compose"
|
"github.com/docker/ecs-plugin/pkg/compose"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *client) ComposeUp(project *compose.Project) error {
|
func (c *client) ComposeUp(project *compose.Project) error {
|
||||||
fmt.Println("TODO Up")
|
vpc, err := c.GetDefaultVPC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
subnets, err := c.GetSubNets(vpc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
securityGroup, err := c.CreateSecurityGroup(project, vpc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logGroup, err := c.GetOrCreateLogGroup(project.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range project.Services {
|
||||||
|
err = c.CreateService(service, securityGroup, subnets, logGroup)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *client) CreateService(service types.ServiceConfig, securityGroup *string, subnets []*string, logGroup *string) error {
|
||||||
|
task, err := ConvertToTaskDefinition(service)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
role, err := c.GetEcsTaskExecutionRole(service)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
task.ExecutionRoleArn = role
|
||||||
|
|
||||||
|
for _, def := range task.ContainerDefinitions {
|
||||||
|
def.LogConfiguration.Options["awslogs-group"] = logGroup
|
||||||
|
def.LogConfiguration.Options["awslogs-stream-prefix"] = aws.String(service.Name)
|
||||||
|
def.LogConfiguration.Options["awslogs-region"] = aws.String(c.Region)
|
||||||
|
}
|
||||||
|
|
||||||
|
arn, err := c.RegisterTaskDefinition(task)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.ECS.CreateService(&ecs.CreateServiceInput{
|
||||||
|
Cluster: aws.String(c.Cluster),
|
||||||
|
DesiredCount: aws.Int64(1), // FIXME get from deploy options
|
||||||
|
LaunchType: aws.String(ecs.LaunchTypeFargate), //FIXME use service.Isolation tro select EC2 vs Fargate
|
||||||
|
NetworkConfiguration: &ecs.NetworkConfiguration{
|
||||||
|
AwsvpcConfiguration: &ecs.AwsVpcConfiguration{
|
||||||
|
AssignPublicIp: aws.String(ecs.AssignPublicIpEnabled),
|
||||||
|
SecurityGroups: []*string{securityGroup},
|
||||||
|
Subnets: subnets,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ServiceName: aws.String(service.Name),
|
||||||
|
SchedulingStrategy: aws.String(ecs.SchedulingStrategyReplica),
|
||||||
|
TaskDefinition: arn,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package amazon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/service/ecs"
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConvertToTaskDefinition(service types.ServiceConfig) (*ecs.RegisterTaskDefinitionInput, error) {
|
||||||
|
panic("Please implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (c client) RegisterTaskDefinition(task *ecs.RegisterTaskDefinitionInput) (*string, error) {
|
||||||
|
logrus.Debug("Register Task Definition")
|
||||||
|
def, err := c.ECS.RegisterTaskDefinition(task)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return def.TaskDefinition.TaskDefinitionArn, err
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package amazon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetOrCreateLogGroup retrieve a pre-existing log group for project or create one
|
||||||
|
func (c client) GetOrCreateLogGroup(project string) (*string, error) {
|
||||||
|
logrus.Debug("Create Log Group")
|
||||||
|
logGroup := fmt.Sprintf("/ecs/%s", project)
|
||||||
|
_, err := c.CW.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{
|
||||||
|
LogGroupName: aws.String(logGroup),
|
||||||
|
Tags: map[string]*string{
|
||||||
|
ProjectTag: aws.String(project),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(*cloudwatchlogs.ResourceAlreadyExistsException); !ok {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &logGroup, nil
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package amazon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/docker/ecs-plugin/pkg/compose"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetDefaultVPC retrieve the default VPC for AWS account
|
||||||
|
func (c client) GetDefaultVPC() (*string, error) {
|
||||||
|
logrus.Debug("Retrieve default VPC")
|
||||||
|
vpcs, err := c.EC2.DescribeVpcs(&ec2.DescribeVpcsInput{
|
||||||
|
Filters: []*ec2.Filter{
|
||||||
|
{
|
||||||
|
Name: aws.String("isDefault"),
|
||||||
|
Values: []*string{aws.String("true")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(vpcs.Vpcs) == 0 {
|
||||||
|
return nil, fmt.Errorf("account has not default VPC")
|
||||||
|
}
|
||||||
|
return vpcs.Vpcs[0].VpcId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// GetSubNets retrieve default subnets for a VPC
|
||||||
|
func (c client) GetSubNets(vpc *string) ([]*string, error) {
|
||||||
|
logrus.Debug("Retrieve SubNets")
|
||||||
|
subnets, err := c.EC2.DescribeSubnets(&ec2.DescribeSubnetsInput{
|
||||||
|
DryRun: nil,
|
||||||
|
Filters: []*ec2.Filter{
|
||||||
|
{
|
||||||
|
Name: aws.String("vpc-id"),
|
||||||
|
Values: []*string{vpc},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: aws.String("default-for-az"),
|
||||||
|
Values: []*string{aws.String("true")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := []*string{}
|
||||||
|
for _, subnet := range subnets.Subnets {
|
||||||
|
ids = append(ids, subnet.SubnetId)
|
||||||
|
}
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSecurityGroup create a security group for the project
|
||||||
|
func (c client) CreateSecurityGroup(project *compose.Project, vpc *string) (*string, error) {
|
||||||
|
logrus.Debug("Create Security Group")
|
||||||
|
name := fmt.Sprintf("%s Security Group", project)
|
||||||
|
securityGroup, err := c.EC2.CreateSecurityGroup(&ec2.CreateSecurityGroupInput{
|
||||||
|
Description: aws.String(name),
|
||||||
|
GroupName: aws.String(name),
|
||||||
|
VpcId: vpc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.EC2.CreateTags(&ec2.CreateTagsInput{
|
||||||
|
Resources: []*string{securityGroup.GroupId},
|
||||||
|
Tags: []*ec2.Tag{
|
||||||
|
{
|
||||||
|
Key: aws.String("Name"),
|
||||||
|
Value: aws.String(name),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: aws.String(ProjectTag),
|
||||||
|
Value: aws.String(project.Name),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return securityGroup.GroupId, nil
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package amazon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
|
||||||
|
|
||||||
|
var defaultTaskExecutionRole *string
|
||||||
|
|
||||||
|
// GetEcsTaskExecutionRole retrieve the role ARN to apply for task execution
|
||||||
|
func (c client) GetEcsTaskExecutionRole(spec types.ServiceConfig) (*string, error) {
|
||||||
|
if arn, ok := spec.Extras["x-ecs-TaskExecutionRole"]; ok {
|
||||||
|
s := arn.(string)
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
if defaultTaskExecutionRole != nil {
|
||||||
|
return defaultTaskExecutionRole, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debug("Retrieve Task Execution Role")
|
||||||
|
entities, err := c.IAM.ListEntitiesForPolicy(&iam.ListEntitiesForPolicyInput{
|
||||||
|
EntityFilter: aws.String("Role"),
|
||||||
|
PolicyArn: aws.String(ECSTaskExecutionPolicy),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(entities.PolicyRoles) == 0 {
|
||||||
|
return nil, fmt.Errorf("no Role is attached to AmazonECSTaskExecutionRole Policy, please provide an explicit task execution role")
|
||||||
|
}
|
||||||
|
if len(entities.PolicyRoles) > 1 {
|
||||||
|
return nil, fmt.Errorf("multiple Roles are attached to AmazonECSTaskExecutionRole Policy, please provide an explicit task execution role")
|
||||||
|
}
|
||||||
|
|
||||||
|
role, err := c.IAM.GetRole(&iam.GetRoleInput{
|
||||||
|
RoleName: entities.PolicyRoles[0].RoleName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultTaskExecutionRole = role.Role.Arn
|
||||||
|
return role.Role.Arn, nil
|
||||||
|
}
|
Loading…
Reference in New Issue