Skip to content

Commit

Permalink
Merge pull request #43 from uc-cdis/feat/ecs_idempotancy
Browse files Browse the repository at this point in the history
ECS launch mode fixes
  • Loading branch information
jawadqur authored Feb 10, 2022
2 parents 229e88a + 53d9dd7 commit 541d29b
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 16 deletions.
8 changes: 8 additions & 0 deletions hatchery/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,11 @@ func launchEcsWorkspace(ctx context.Context, userName string, hash string, acces
return err
}

_, err = svc.CreateEcsTaskExecutionRole()
if err != nil {
return err
}

taskDef := CreateTaskDefinitionInput{
Image: hatchApp.Image,
Cpu: cpu,
Expand Down Expand Up @@ -438,14 +443,17 @@ func launchEcsWorkspace(ctx context.Context, userName string, hash string, acces
{
ContainerPath: aws.String("/home/jovyan/data"),
SourceVolume: aws.String("data-volume"),
ReadOnly: aws.Bool(false),
},
{
ContainerPath: aws.String("/home/jovyan/pd"),
SourceVolume: aws.String("pd"),
ReadOnly: aws.Bool(false),
},
{
ContainerPath: aws.String("/home/jovyan/.gen3"),
SourceVolume: aws.String("gen3"),
ReadOnly: aws.Bool(false),
},
},
Args: hatchApp.Args,
Expand Down
4 changes: 2 additions & 2 deletions hatchery/efs.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ func (creds *CREDS) createAccessPoint(FileSystemId string, userName string, svc
CreationInfo: &efs.CreationInfo{
OwnerGid: aws.Int64(100),
OwnerUid: aws.Int64(1000),
Permissions: aws.String("0755"),
Permissions: aws.String("755"),
},
Path: aws.String("/"),
Path: aws.String("/pd"),
},
}

Expand Down
110 changes: 96 additions & 14 deletions hatchery/iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
)
Expand All @@ -13,33 +14,52 @@ func (creds *CREDS) taskRole(userName string) (*string, error) {
Credentials: creds.creds,
Region: aws.String("us-east-1"),
})))

pm := Config.PayModelMap[userName]
policyArn := fmt.Sprintf("arn:aws:iam::%s:policy/%s", pm.AWSAccountId, fmt.Sprintf("ws-task-policy-%s", userName))
taskRoleInput := &iam.GetRoleInput{
RoleName: aws.String(userToResourceName(userName, "pod")),
}
taskRole, _ := svc.GetRole(taskRoleInput)
if taskRole.Role != nil {
return taskRole.Role.Arn, nil
} else {
policy, err := svc.CreatePolicy(&iam.CreatePolicyInput{
policyAlreadyExists := false
_, err := svc.CreatePolicy(&iam.CreatePolicyInput{
PolicyDocument: aws.String(`{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "HatcheryPolicy",
"Effect": "Allow",
"Action": "elasticfilesystem:*",
"Resource": [
"arn:aws:elasticfilesystem:*:*:access-point/*",
"arn:aws:elasticfilesystem:*:*:file-system/*"
]
"Action": [
"elasticfilesystem:ClientMount",
"elasticfilesystem:ClientWrite",
"elasticfilesystem:ClientRootAccess"
],
"Resource": "*"
}
]
}`),
PolicyName: aws.String(fmt.Sprintf("ws-task-policy-%s", userName)),
})
if err != nil {
return nil, fmt.Errorf("failed to create policy: %s", err)
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
// Update the policy to the latest spec if it is existed already
case iam.ErrCodeEntityAlreadyExistsException:
policyAlreadyExists = true
case iam.ErrCodeLimitExceededException:
fmt.Println(iam.ErrCodeLimitExceededException, aerr.Error())
case iam.ErrCodeNoSuchEntityException:
fmt.Println(iam.ErrCodeNoSuchEntityException, aerr.Error())
case iam.ErrCodeServiceFailureException:
fmt.Println(iam.ErrCodeServiceFailureException, aerr.Error())
default:
fmt.Println(aerr.Error())
}
}
if !policyAlreadyExists {
return nil, err
}
}
createTaskRoleInput := &iam.CreateRoleInput{
RoleName: aws.String(userToResourceName(userName, "pod")),
Expand All @@ -59,19 +79,81 @@ func (creds *CREDS) taskRole(userName string) (*string, error) {
`),
}

createTaskRole, err := svc.CreateRole(createTaskRoleInput)
if err != nil {
return nil, fmt.Errorf("failed to create TaskRole: %s", err)
}

_, err = svc.AttachRolePolicy(&iam.AttachRolePolicyInput{
PolicyArn: policy.Policy.Arn,
PolicyArn: &policyArn,
RoleName: aws.String(userToResourceName(userName, "pod")),
})
if err != nil {
return nil, fmt.Errorf("failed to attach RolePolicy: %s", err)
}
createTaskRole, err := svc.CreateRole(createTaskRoleInput)
if err != nil {
return nil, fmt.Errorf("failed to create TaskRole: %s", err)
}

return createTaskRole.Role.Arn, nil
}

}
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
// The task execution role grants the Amazon ECS container and Fargate agents permission to make AWS API calls on your behalf.
const ecsTaskExecutionRoleName = "ecsTaskExecutionRole"
const ecsTaskExecutionPolicyArn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
const ecsTaskExecutionRoleAssumeRolePolicyDocument = `{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}`

func (creds *CREDS) CreateEcsTaskExecutionRole() (*string, error) {
svc := iam.New(session.Must(session.NewSession(&aws.Config{
Credentials: creds.creds,
Region: aws.String("us-east-1"),
})))
getRoleResp, err := svc.GetRole(
&iam.GetRoleInput{
RoleName: aws.String(ecsTaskExecutionRoleName),
},
)

if err == nil {
return getRoleResp.Role.Arn, nil
}

createRoleResp, err := svc.CreateRole(
&iam.CreateRoleInput{
AssumeRolePolicyDocument: aws.String(ecsTaskExecutionRoleAssumeRolePolicyDocument),
RoleName: aws.String(ecsTaskExecutionRoleName),
},
)

if err != nil {
fmt.Println(err)
return nil, err
}

ecsTaskExecutionRoleArn := createRoleResp.Role.Arn

_, err = svc.AttachRolePolicy(
&iam.AttachRolePolicyInput{
RoleName: aws.String(ecsTaskExecutionRoleName),
PolicyArn: aws.String(ecsTaskExecutionPolicyArn),
},
)

if err != nil {
fmt.Println(err)
return nil, err
}

return ecsTaskExecutionRoleArn, nil
}

0 comments on commit 541d29b

Please sign in to comment.