github.com/awslabs/fargatecli@v0.3.2-0.20210502061925-aa1c09cd404e/cmd/task_run.go (about)

     1  package cmd
     2  
     3  import (
     4  	CWL "github.com/awslabs/fargatecli/cloudwatchlogs"
     5  	"github.com/awslabs/fargatecli/console"
     6  	"github.com/awslabs/fargatecli/docker"
     7  	EC2 "github.com/awslabs/fargatecli/ec2"
     8  	ECR "github.com/awslabs/fargatecli/ecr"
     9  	ECS "github.com/awslabs/fargatecli/ecs"
    10  	"github.com/awslabs/fargatecli/git"
    11  	IAM "github.com/awslabs/fargatecli/iam"
    12  	"github.com/spf13/cobra"
    13  )
    14  
    15  const typeTask string = "task"
    16  
    17  type TaskRunOperation struct {
    18  	Cpu              string
    19  	EnvVars          []ECS.EnvVar
    20  	Image            string
    21  	Memory           string
    22  	Num              int64
    23  	SecurityGroupIds []string
    24  	SubnetIds        []string
    25  	TaskName         string
    26  	TaskRole         string
    27  	TaskCommand      []string
    28  }
    29  
    30  func (o *TaskRunOperation) Validate() {
    31  	err := validateCpuAndMemory(o.Cpu, o.Memory)
    32  
    33  	if err != nil {
    34  		console.ErrorExit(err, "Invalid settings: %s CPU units / %s MiB", o.Cpu, o.Memory)
    35  	}
    36  
    37  	if o.Num < 1 {
    38  		console.ErrorExit(err, "Invalid number of tasks: %d, num must be > 1", o.Num)
    39  	}
    40  }
    41  
    42  func (o *TaskRunOperation) SetEnvVars(inputEnvVars []string) {
    43  	o.EnvVars = extractEnvVars(inputEnvVars)
    44  }
    45  
    46  var (
    47  	flagTaskRunNum              int64
    48  	flagTaskRunCpu              string
    49  	flagTaskRunEnvVars          []string
    50  	flagTaskRunImage            string
    51  	flagTaskRunMemory           string
    52  	flagTaskRunSecurityGroupIds []string
    53  	flagTaskRunSubnetIds        []string
    54  	flagTaskRunTaskRole         string
    55  	flagTaskRunTaskCommand      []string
    56  )
    57  
    58  var taskRunCmd = &cobra.Command{
    59  	Use:   "run <task name>",
    60  	Short: "Run new tasks",
    61  	Long: `Run new tasks
    62  
    63  You must specify a task group name in order to interact with the task(s) in
    64  subsequent commands to view logs, stop and inspect tasks. Task group names do
    65  not have to be unique -- multiple configurations of task instances can be
    66  started with the same task group.
    67  
    68  Multiple instances of a task can be run by specifying a number in the --num
    69  flag. If no number is specified, a single task instance will be run.
    70  
    71  CPU and memory settings can be optionally specified as CPU units and mebibytes
    72  respectively using the --cpu and --memory flags. Every 1024 CPU units is
    73  equivilent to a single vCPU. AWS Fargate only supports certain combinations of
    74  CPU and memory configurations:
    75  
    76  | CPU (CPU Units) | Memory (MiB)                          |
    77  | --------------- | ------------------------------------- |
    78  | 256             | 512, 1024, or 2048                    |
    79  | 512             | 1024 through 4096 in 1GiB increments  |
    80  | 1024            | 2048 through 8192 in 1GiB increments  |
    81  | 2048            | 4096 through 16384 in 1GiB increments |
    82  | 4096            | 8192 through 30720 in 1GiB increments |
    83  
    84  If not specified, fargate will launch minimally sized tasks at 0.25 vCPU (256
    85  CPU units) and 0.5GB (512 MiB) of memory.
    86  
    87  The Docker container image to use in the task can be optionally specified via
    88  the --image flag. If not specified, fargate will build a new Docker container
    89  image from the current working directory and push it to Amazon ECR in a
    90  repository named for the task group. If the current working directory is a git
    91  repository, the container image will be tagged with the short ref of the HEAD
    92  commit. If not, a timestamp in the format of YYYYMMDDHHMMSS will be used.
    93  
    94  Environment variables can be specified via the --env flag. Specify --env with a
    95  key=value parameter multiple times to add multiple variables.
    96  
    97  Security groups can optionally be specified for the task by passing the
    98  --security-group-id flag with a security group ID. To add multiple security
    99  groups, pass --security-group-id with a security group ID multiple times. If
   100  --security-group-id is omitted, a permissive security group will be applied to
   101  the task.
   102  
   103  By default, the task will be created in the default VPC and attached to the
   104  default VPC subnets for each availability zone. You can override this by
   105  specifying explicit subnets by passing the --subnet-id flag with a subnet ID.
   106  
   107  A task role can be optionally specified via the --task-role flag by providing
   108  eith a full IAM role ARN or the name of an IAM role. The tasks will be able to
   109  assume this role.
   110  
   111  The default command of the docker image can be overridden using the
   112  --task-command flag, where the value is a string of comma seperated values
   113  representing the command. These values will be placed into an array as per
   114  the requirements of the docker CMD syntax`,
   115  
   116  	Args: cobra.ExactArgs(1),
   117  	Run: func(cmd *cobra.Command, args []string) {
   118  		operation := &TaskRunOperation{
   119  			Cpu:              flagTaskRunCpu,
   120  			Image:            flagTaskRunImage,
   121  			Memory:           flagTaskRunMemory,
   122  			Num:              flagTaskRunNum,
   123  			SecurityGroupIds: flagTaskRunSecurityGroupIds,
   124  			SubnetIds:        flagTaskRunSubnetIds,
   125  			TaskName:         args[0],
   126  			TaskRole:         flagTaskRunTaskRole,
   127  			TaskCommand:      flagTaskRunTaskCommand,
   128  		}
   129  
   130  		operation.SetEnvVars(flagTaskRunEnvVars)
   131  		operation.Validate()
   132  
   133  		runTask(operation)
   134  	},
   135  }
   136  
   137  func init() {
   138  	taskRunCmd.Flags().Int64VarP(&flagTaskRunNum, "num", "n", 1, "Number of task instances to run")
   139  	taskRunCmd.Flags().StringSliceVarP(&flagTaskRunEnvVars, "env", "e", []string{}, "Environment variables to set [e.g. KEY=value] (can be specified multiple times)")
   140  	taskRunCmd.Flags().StringVarP(&flagTaskRunCpu, "cpu", "c", "256", "Amount of cpu units to allocate for each task")
   141  	taskRunCmd.Flags().StringVarP(&flagTaskRunImage, "image", "i", "", "Docker image to run; if omitted Fargate will build an image from the Dockerfile in the current directory")
   142  	taskRunCmd.Flags().StringVarP(&flagTaskRunMemory, "memory", "m", "512", "Amount of MiB to allocate for each task")
   143  	taskRunCmd.Flags().StringSliceVar(&flagTaskRunSecurityGroupIds, "security-group-id", []string{}, "ID of a security group to apply to the task (can be specified multiple times)")
   144  	taskRunCmd.Flags().StringSliceVar(&flagTaskRunSubnetIds, "subnet-id", []string{}, "ID of a subnet in which to place the task (can be specified multiple times)")
   145  	taskRunCmd.Flags().StringVarP(&flagTaskRunTaskRole, "task-role", "", "", "Name or ARN of an IAM role that the tasks can assume")
   146  	taskRunCmd.Flags().StringSliceVar(&flagTaskRunTaskCommand, "task-command", []string{}, "Command to run inside container instead of the one specified in the docker image")
   147  	taskCmd.AddCommand(taskRunCmd)
   148  }
   149  
   150  func runTask(operation *TaskRunOperation) {
   151  	cwl := CWL.New(sess)
   152  	ec2 := EC2.New(sess)
   153  	ecr := ECR.New(sess)
   154  	ecs := ECS.New(sess, clusterName)
   155  	iam := IAM.New(sess)
   156  	ecsTaskExecutionRoleArn := iam.CreateEcsTaskExecutionRole()
   157  	logGroupName := cwl.CreateLogGroup(taskLogGroupFormat, operation.TaskName)
   158  
   159  	if len(operation.SecurityGroupIds) == 0 {
   160  		defaultSecurityGroupID, _ := ec2.GetDefaultSecurityGroupID()
   161  		operation.SecurityGroupIds = []string{defaultSecurityGroupID}
   162  	}
   163  
   164  	if len(operation.SubnetIds) == 0 {
   165  		operation.SubnetIds, _ = ec2.GetDefaultSubnetIDs()
   166  	}
   167  
   168  	if operation.Image == "" {
   169  		var repositoryUri, tag string
   170  
   171  		if ecr.IsRepositoryCreated(operation.TaskName) {
   172  			repositoryUri = ecr.GetRepositoryUri(operation.TaskName)
   173  		} else {
   174  			repositoryUri = ecr.CreateRepository(operation.TaskName)
   175  		}
   176  
   177  		if git.IsCwdGitRepo() {
   178  			tag = git.GetShortSha()
   179  		} else {
   180  			tag = docker.GenerateTag()
   181  		}
   182  
   183  		repository := docker.NewRepository(repositoryUri)
   184  		username, password := ecr.GetUsernameAndPassword()
   185  
   186  		repository.Login(username, password)
   187  		repository.Build(tag)
   188  		repository.Push(tag)
   189  
   190  		operation.Image = repository.UriFor(tag)
   191  	}
   192  
   193  	taskDefinitionArn := ecs.CreateTaskDefinition(
   194  		&ECS.CreateTaskDefinitionInput{
   195  			Cpu:              operation.Cpu,
   196  			EnvVars:          operation.EnvVars,
   197  			ExecutionRoleArn: ecsTaskExecutionRoleArn,
   198  			Image:            operation.Image,
   199  			LogGroupName:     logGroupName,
   200  			LogRegion:        region,
   201  			Memory:           operation.Memory,
   202  			Name:             operation.TaskName,
   203  			Type:             typeTask,
   204  			TaskRole:         operation.TaskRole,
   205  			TaskCommand:      operation.TaskCommand,
   206  		},
   207  	)
   208  
   209  	ecs.RunTask(
   210  		&ECS.RunTaskInput{
   211  			ClusterName:       clusterName,
   212  			Count:             operation.Num,
   213  			TaskName:          operation.TaskName,
   214  			TaskDefinitionArn: taskDefinitionArn,
   215  			SubnetIds:         operation.SubnetIds,
   216  			SecurityGroupIds:  operation.SecurityGroupIds,
   217  		},
   218  	)
   219  
   220  	console.Info("Running task %s", operation.TaskName)
   221  }