Skip to content

Commit 6f73765

Browse files
author
Hsing-Hui Hsu
committed
Add force update flag
Values under 'run_params' in the ECSParams file are not considered as part of the task definition and therefore will not trigger a new task to be run if the the values under this field change. To ensure a better developer experience, we are adding this flag to allow tasks/services to be stopped and re-launched with the new run parameters.
1 parent ff2b846 commit 6f73765

5 files changed

Lines changed: 33 additions & 17 deletions

File tree

ecs-cli/modules/cli/compose/entity/entity_helper.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func SetupTaskDefinitionCache() cache.Cache {
8585
// GetOrCreateTaskDefinition gets the task definition from cache if present, else
8686
// creates it in ECS and persists in a local cache. It also sets the latest
8787
// taskDefinition to the current instance of task
88+
// TODO: convert to method on entity, since it changes state of entity?
8889
func GetOrCreateTaskDefinition(entity ProjectEntity) (*ecs.TaskDefinition, error) {
8990
taskDefinition := entity.TaskDefinition()
9091
log.WithFields(log.Fields{

ecs-cli/modules/cli/compose/entity/task/task.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/context"
1818
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/entity"
1919
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/entity/types"
20+
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/commands/flags"
2021
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils"
2122
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils/cache"
2223
composeutils "github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils/compose"
@@ -99,7 +100,8 @@ func (t *Task) Start() error {
99100
// if count of running tasks = 0, starts 1
100101
// if count != 0, and the task definitions differed, then its stops the old ones and starts the new ones
101102
func (t *Task) Up() error {
102-
return t.up(true)
103+
updateTasks := t.Context().CLIContext.Bool(flags.ForceUpdateFlag)
104+
return t.up(updateTasks)
103105
}
104106

105107
// Info returns a formatted list of containers (running and stopped) in the current cluster
@@ -108,19 +110,20 @@ func (t *Task) Info(filterLocal bool) (project.InfoSet, error) {
108110
return entity.Info(t, filterLocal)
109111
}
110112

111-
// Scale finds out the current count of running tasks for this project and scales to the desired count
113+
// Scale finds out the current count of running tasks for this project and scales to the desired count.
114+
// Any run params specified will be taken into account.
112115
// if desired = current, noop
113116
// if desired > current, stops the extra ones
114117
// if desired < current, start new ones (also if current was 0, create a new task definition)
115-
func (t *Task) Scale(expectedCount int) error {
118+
func (t *Task) Scale(desiredCount int) error {
116119
ecsTasks, err := entity.CollectTasksWithStatus(t, ecs.DesiredStatusRunning, true)
117120
if err != nil {
118121
return err
119122
}
120123

121124
observedCount := len(ecsTasks)
122125

123-
if expectedCount == observedCount {
126+
if desiredCount == observedCount {
124127
// NoOp
125128
log.WithFields(log.Fields{
126129
"countOfRunningTasks": observedCount,
@@ -129,18 +132,18 @@ func (t *Task) Scale(expectedCount int) error {
129132
return nil
130133
}
131134

132-
// running more than expected, stop the tasks
133-
if expectedCount < observedCount {
134-
diff := observedCount - expectedCount
135+
// running more than desired, stop the extra tasks
136+
if desiredCount < observedCount {
137+
diff := observedCount - desiredCount
135138
ecsTasksToStop := []*ecs.Task{}
136139
for i := 0; i < diff; i++ {
137140
ecsTasksToStop = append(ecsTasksToStop, ecsTasks[i])
138141
}
139142
return t.stopTasks(ecsTasksToStop)
140143
}
141144

142-
// if expected > observed, then run the difference
143-
diff := expectedCount - observedCount
145+
// if desired > observed, then run the difference
146+
diff := desiredCount - observedCount
144147

145148
var taskDef string
146149
// if nothing was running, create new task definition
@@ -250,6 +253,7 @@ func (t *Task) stopTasks(ecsTasks []*ecs.Task) error {
250253
}
251254

252255
// runTasks issues run task request to ECS Service in chunks of count=10
256+
// it always takes into account the latest ECS params
253257
func (t *Task) runTasks(taskDefinition string, totalCount int) ([]*ecs.Task, error) {
254258
result := []*ecs.Task{}
255259
chunkSize := 10 // can issue only up to 10 tasks in a RunTask Call
@@ -302,6 +306,7 @@ func convertToECSTaskOverride(overrides map[string][]string) (*ecs.TaskOverride,
302306
return ecsOverrides, nil
303307
}
304308

309+
// buildRunTaskInput will account for what is currently specified in ECS Params
305310
func (t *Task) buildRunTaskInput(taskDefinition string, count int, overrides map[string][]string) (*ecs.RunTaskInput, error) {
306311
cluster := t.Context().CommandConfig.Cluster
307312
launchType := t.Context().CommandConfig.LaunchType
@@ -356,7 +361,6 @@ func (t *Task) buildRunTaskInput(taskDefinition string, count int, overrides map
356361
runTaskInput.PlacementStrategy = placementStrategy
357362
}
358363

359-
360364
if launchType != "" {
361365
runTaskInput.LaunchType = aws.String(launchType)
362366
}
@@ -373,10 +377,11 @@ func (t *Task) createOne() error {
373377
return t.waitForRunTasks(ecsTask)
374378
}
375379

376-
// up gets a list of running tasks and if updateTasks is set to true, it updates it with the latest task definition
377-
// if count of running tasks = 0, starts 1
378-
// if count != 0, and the task definitions differed, then its stops the old ones and starts the new ones
379-
func (t *Task) up(updateTasks bool) error {
380+
// up gets a list of running tasks. If there are no running tasks, it starts 1 task.
381+
// If there are no running tasks, and either the task definition has changed or
382+
// forceUpdate is specified, then the running tasks are stopped and relaunched
383+
// with the task definition and run parameters in the current call.
384+
func (t *Task) up(forceUpdate bool) error {
380385
ecsTasks, err := entity.CollectTasksWithStatus(t, ecs.DesiredStatusRunning, true)
381386
if err != nil {
382387
return err
@@ -407,7 +412,7 @@ func (t *Task) up(updateTasks bool) error {
407412

408413
ecsTaskArns := make(map[string]bool)
409414

410-
if oldTaskDef != newTaskDef {
415+
if oldTaskDef != newTaskDef || forceUpdate {
411416
log.WithFields(log.Fields{"taskDefinition": newTaskDef}).Info("Updating to new task definition")
412417

413418
chunkSize := 10

ecs-cli/modules/clients/aws/ecs/client.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,9 @@ func (c *ecsClient) RegisterTaskDefinition(request *ecs.RegisterTaskDefinitionIn
224224
func (c *ecsClient) RegisterTaskDefinitionIfNeeded(
225225
request *ecs.RegisterTaskDefinitionInput,
226226
taskDefinitionCache cache.Cache) (*ecs.TaskDefinition, error) {
227+
227228
if request.Family == nil {
228-
return nil, errors.New("invalid task definitions: family is required")
229+
return nil, errors.New("invalid task definition: family is required")
229230
}
230231

231232
taskDefResp, err := c.DescribeTaskDefinition(aws.StringValue(request.Family))

ecs-cli/modules/commands/compose/compose_command.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func upCommand(factory composeFactory.ProjectFactory) cli.Command {
132132
Name: "up",
133133
Usage: "Creates an ECS task definition from your compose file (if it does not already exist) and runs one instance of that task on your cluster (a combination of create and start).",
134134
Action: compose.WithProject(factory, compose.ProjectUp, false),
135-
Flags: append(flags.OptionalConfigFlags(), flags.OptionalLaunchTypeFlag(), flags.OptionalCreateLogsFlag()),
135+
Flags: append(flags.OptionalConfigFlags(), flags.OptionalLaunchTypeFlag(), flags.OptionalCreateLogsFlag(), flags.OptionalForceUpdateFlag()),
136136
OnUsageError: flags.UsageErrorFactory("up"),
137137
}
138138
}

ecs-cli/modules/commands/flags/flags.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ const (
9494
ComposeFileNameFlag = "file"
9595
TaskRoleArnFlag = "task-role-arn"
9696
ECSParamsFileNameFlag = "ecs-params"
97+
ForceUpdateFlag = "force-update"
9798

9899
// Compose Service
99100
CreateServiceCommandName = "create"
@@ -182,6 +183,14 @@ func OptionalCreateLogsFlag() cli.Flag {
182183
}
183184
}
184185

186+
// OptionalForceUpdateFlag allows users to force an update of running tasks on compose up.
187+
func OptionalForceUpdateFlag() cli.Flag {
188+
return cli.BoolFlag{
189+
Name: ForceUpdateFlag + ",u",
190+
Usage: "[Optional] Forces update of task or service with current run parameters",
191+
}
192+
}
193+
185194
// UsageErrorFactory Returns a usage error function for the specified command
186195
func UsageErrorFactory(command string) func(*cli.Context, error, bool) error {
187196
return func(c *cli.Context, err error, isSubcommand bool) error {

0 commit comments

Comments
 (0)