Skip to content

Commit

Permalink
Add support for "update log driver options" (#10)
Browse files Browse the repository at this point in the history
* Upgrade AWS to v1.20.21
* Add alterLogConfigurationLogDriverOptions
* Add alterLogConfigurations
* Add new flags
* Update documentation for new feature
  • Loading branch information
andresvia authored Dec 13, 2019
1 parent c1598cd commit eab994c
Show file tree
Hide file tree
Showing 11 changed files with 1,010 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
*.zip

.idea/
.DS_Store
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Usage of update-aws-ecs-service:
container-name=envvar-name=envvar-value
-container-image value
container-name=image
-container-logopt value
container-name=logdriver=logopt=value
-container-logsecret value
container-name=logdriver=logsecret=valuefrom
-container-secret value
container-name=secret-name=secret-valuefrom
-desired-count int
Expand Down Expand Up @@ -107,6 +111,17 @@ update-aws-ecs-service \
-container-secret mycontainer=mysecretname= \
```

💡 Combined updates are possible. For example: "Update the application container image and adjust the `awslogs` log driver options for the sidecar container."

```
update-aws-ecs-service \
-cluster example \
-service service1-application1 \
-container-image application=example.com/service1/application1:1a2b3c4 \
-container-logopt sidecar=awslogs=awslogs-group=/com/example/service1/application1 \
-container-logopt sidecar=awslogs=awslogs-stream-prefix=sidecar-1a2b3c4
```

### update-aws-ecs-service compared to AWS CodePipeline

- With `update-aws-ecs-service` there is no need to create individual AWS CodePipeline pipelines per service
Expand Down
23 changes: 23 additions & 0 deletions cmd/update-aws-ecs-service/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import "fmt"

type mapMapMapFlag map[string]map[string]map[string]string

func (kvs *mapMapMapFlag) String() string {
return fmt.Sprintf("%v", *kvs)
}

func (kvs mapMapMapFlag) Set(value string) error {
key, value := keyEqValue(value)
valueKey, value := keyEqValue(value)
valueValueKey, value := keyEqValue(value)
if kvs[key] == nil {
kvs[key] = map[string]map[string]string{}
}
if kvs[key][valueKey] == nil {
kvs[key][valueKey] = map[string]string{}
}
kvs[key][valueKey][valueValueKey] = value
return nil
}
42 changes: 42 additions & 0 deletions cmd/update-aws-ecs-service/flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"reflect"
"testing"
)

func TestMapMapMapFlag_Set(t *testing.T) {
actualStruct := mapMapMapFlag{}
if err := actualStruct.Set("container1=awslogs=region=us-west-2"); err != nil {
t.Fatal(err)
}
if err := actualStruct.Set("container1=awslogs=loggroup=group1"); err != nil {
t.Fatal(err)
}
if err := actualStruct.Set("container2=awslogs=="); err != nil {
t.Fatal(err)
}
if err := actualStruct.Set("container2=fluentd=option1=value1"); err != nil {
t.Fatal(err)
}
var expectedStruct mapMapMapFlag
expectedStruct = map[string]map[string]map[string]string{
"container1": {
"awslogs": {
"region": "us-west-2",
"loggroup": "group1",
},
},
"container2": {
"awslogs": {
"": "",
},
"fluentd": {
"option1": "value1",
},
},
}
if !reflect.DeepEqual(expectedStruct, actualStruct) {
t.Fatal()
}
}
24 changes: 15 additions & 9 deletions cmd/update-aws-ecs-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ func main() {
var images mapFlag = map[string]string{}
var envs mapMapFlag = map[string]map[string]string{}
var secrets mapMapFlag = map[string]map[string]string{}
var logopts mapMapMapFlag = map[string]map[string]map[string]string{}
var logsecrets mapMapMapFlag = map[string]map[string]map[string]string{}

flag.Var(&images, "container-image", "container-name=image")
flag.Var(&envs, "container-envvar", "container-name=envvar-name=envvar-value")
flag.Var(&secrets, "container-secret", "container-name=secret-name=secret-valuefrom")
flag.Var(&logopts, "container-logopt", "container-name=logdriver=logopt=value")
flag.Var(&logsecrets, "container-logsecret", "container-name=logdriver=logsecret=valuefrom")
flag.Parse()

sess := session.Must(session.NewSessionWithOptions(session.Options{
Expand All @@ -79,15 +83,17 @@ func main() {
}

esu := awsecs.ECSServiceUpdate{
API: *ecs.New(sess),
Cluster: *cluster,
Service: *service,
Image: images,
Environment: envs,
Secrets: secrets,
DesiredCount: int64ptr(*desiredCount),
Taskdef: *taskdef,
BackOff: backoff.NewExponentialBackOff(),
API: *ecs.New(sess),
Cluster: *cluster,
Service: *service,
Image: images,
Environment: envs,
Secrets: secrets,
LogDriverOptions: logopts,
LogDriverSecrets: logsecrets,
DesiredCount: int64ptr(*desiredCount),
Taskdef: *taskdef,
BackOff: backoff.NewExponentialBackOff(),
}

if err := esu.Apply(); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions ecs-alter-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ var (
ErrFailedRollback = errors.New("failed rollback")
)

func alterServiceOrValidatedRollBack(api ecs.ECS, cluster, service string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string, desiredCount *int64, taskdef string, bo backoff.BackOff) error {
oldsvc, alterSvcErr := alterServiceValidateDeployment(api, cluster, service, imageMap, envMaps, secretMaps, desiredCount, taskdef, bo)
func alterServiceOrValidatedRollBack(api ecs.ECS, cluster, service string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string, logopts map[string]map[string]map[string]string, logsecrets map[string]map[string]map[string]string, desiredCount *int64, taskdef string, bo backoff.BackOff) error {
oldsvc, alterSvcErr := alterServiceValidateDeployment(api, cluster, service, imageMap, envMaps, secretMaps, logopts, logsecrets, desiredCount, taskdef, bo)
if alterSvcErr != nil {
operation := func() error {
if oldsvc.ServiceName == nil {
Expand Down
55 changes: 24 additions & 31 deletions ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,36 +166,27 @@ func alterSecret(copy ecs.ContainerDefinition, secretMap map[string]string) ecs.
return copy
}

func copyTaskDef(api ecs.ECS, taskdef string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string) (string, error) {
func copyTaskDef(api ecs.ECS, taskdef string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string, logopts map[string]map[string]map[string]string, logsecrets map[string]map[string]map[string]string) (string, error) {
output, err := api.DescribeTaskDefinition(&ecs.DescribeTaskDefinitionInput{TaskDefinition: aws.String(taskdef)})
if err != nil {
return "", err
}

asRegisterTaskDefinitionInput := copyTd(*output.TaskDefinition, output.Tags)
tdCopy := alterSecrets(alterEnvironments(alterImages(asRegisterTaskDefinitionInput, imageMap), envMaps), secretMaps)

// if os.Getenv("DEBUG") == "YES" {
// out, _ := json.Marshal(asRegisterTaskDefinitionInput)
// fmt.Println(string(out))
// out, _ = json.Marshal(tdCopy)
// fmt.Println(string(out))
// panic("something")
// }
tdCopy := alterLogConfigurations(alterSecrets(alterEnvironments(alterImages(asRegisterTaskDefinitionInput, imageMap), envMaps), secretMaps), logopts, logsecrets)

if reflect.DeepEqual(asRegisterTaskDefinitionInput, tdCopy) {
return *output.TaskDefinition.TaskDefinitionArn, nil
} else {
tdNew, err := api.RegisterTaskDefinition(&tdCopy)
if err != nil {
return "", err
}
arn := tdNew.TaskDefinition.TaskDefinitionArn
return *arn, nil
}
tdNew, err := api.RegisterTaskDefinition(&tdCopy)
if err != nil {
return "", err
}
arn := tdNew.TaskDefinition.TaskDefinitionArn
return *arn, nil
}

func alterService(api ecs.ECS, cluster, service string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string, desiredCount *int64, taskdef string) (ecs.Service, ecs.Service, error) {
func alterService(api ecs.ECS, cluster, service string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string, logopts map[string]map[string]map[string]string, logsecrets map[string]map[string]map[string]string, desiredCount *int64, taskdef string) (ecs.Service, ecs.Service, error) {
output, err := api.DescribeServices(&ecs.DescribeServicesInput{Cluster: aws.String(cluster), Services: []*string{aws.String(service)}})
if err != nil {
return ecs.Service{}, ecs.Service{}, err
Expand All @@ -205,7 +196,7 @@ func alterService(api ecs.ECS, cluster, service string, imageMap map[string]stri
if taskdef != "" {
srcTaskDef = &taskdef
}
newTd, err := copyTaskDef(api, *srcTaskDef, imageMap, envMaps, secretMaps)
newTd, err := copyTaskDef(api, *srcTaskDef, imageMap, envMaps, secretMaps, logopts, logsecrets)
if err != nil {
return *svc, ecs.Service{}, err
}
Expand Down Expand Up @@ -273,8 +264,8 @@ func validateDeployment(api ecs.ECS, ecsService ecs.Service, bo backoff.BackOff)
return errNoPrimaryDeployment
}

func alterServiceValidateDeployment(api ecs.ECS, cluster, service string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string, desiredCount *int64, taskdef string, bo backoff.BackOff) (ecs.Service, error) {
oldsvc, newsvc, err := alterService(api, cluster, service, imageMap, envMaps, secretMaps, desiredCount, taskdef)
func alterServiceValidateDeployment(api ecs.ECS, cluster, service string, imageMap map[string]string, envMaps map[string]map[string]string, secretMaps map[string]map[string]string, logopts map[string]map[string]map[string]string, logsecrets map[string]map[string]map[string]string, desiredCount *int64, taskdef string, bo backoff.BackOff) (ecs.Service, error) {
oldsvc, newsvc, err := alterService(api, cluster, service, imageMap, envMaps, secretMaps, logopts, logsecrets, desiredCount, taskdef)
if err != nil {
return oldsvc, err
}
Expand All @@ -292,18 +283,20 @@ func alterServiceValidateDeployment(api ecs.ECS, cluster, service string, imageM

// ECSServiceUpdate encapsulates the attributes of an ECS service update
type ECSServiceUpdate struct {
API ecs.ECS // ECS Api
Cluster string // Cluster which the service is deployed to
Service string // Name of the service
Image map[string]string // Map of container names and images
Environment map[string]map[string]string // Map of container names environment variable name and value
Secrets map[string]map[string]string // Map of container names environment variable name and valueFrom
DesiredCount *int64 // If nil the service desired count is not altered
BackOff backoff.BackOff // BackOff strategy to use when validating the update
Taskdef string // If non empty used as base task definition instead of the current task definition
API ecs.ECS // ECS Api
Cluster string // Cluster which the service is deployed to
Service string // Name of the service
Image map[string]string // Map of container names and images
Environment map[string]map[string]string // Map of container names environment variable name and value
Secrets map[string]map[string]string // Map of container names environment variable name and valueFrom
LogDriverOptions map[string]map[string]map[string]string // Map of container names log driver name log driver option and value
LogDriverSecrets map[string]map[string]map[string]string // Map of container names log driver name log driver secret and valueFrom
DesiredCount *int64 // If nil the service desired count is not altered
BackOff backoff.BackOff // BackOff strategy to use when validating the update
Taskdef string // If non empty used as base task definition instead of the current task definition
}

// Apply the ECS Service Update
func (e *ECSServiceUpdate) Apply() error {
return alterServiceOrValidatedRollBack(e.API, e.Cluster, e.Service, e.Image, e.Environment, e.Secrets, e.DesiredCount, e.Taskdef, e.BackOff)
return alterServiceOrValidatedRollBack(e.API, e.Cluster, e.Service, e.Image, e.Environment, e.Secrets, e.LogDriverOptions, e.LogDriverSecrets, e.DesiredCount, e.Taskdef, e.BackOff)
}
Loading

0 comments on commit eab994c

Please sign in to comment.