Skip to content

Commit

Permalink
Move to public repo (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
andresvia authored Jun 17, 2019
1 parent 2ec3a48 commit 3c10a6f
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 52 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
*.zip

.idea/
46 changes: 46 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# contributing

Contribute to this library by submitting PR (pull requests).

Make sure your contribution passes the following validations:

1. New code must be formatted according to `fmt` standards:

`go fmt ./...`

2. New code must pass `lint` validations:

`golint ./...`

3. And new code must pass Go Vetting practices:

`go vet ./...`

I would like to keep this library simple, the proposed change must be a common use case.

## maintainer

Code is organized in a flat module space, the public API of the core module is intentionally small, with public
structures that expose only one public method always with the signature `(*struct) Apply() error`.

### example

```
type EnforceLaunchConfig struct {
// ...
}
EnforceLaunchConfig encapsulates the attributes of a LaunchConfig
enforcement
func (e *EnforceLaunchConfig) Apply() error
Apply the LaunchConfig enforcement
```

Packages `cmd/*` are entry points, `main` packages which make use of the public structures in the core module. Main
packages are purposely simple and only make use of one core structure, their job is just create a new structure run the
`Apply` method and handle the returned `error`.

## releases

Create a GitHub release, run `./make.sh` and attach the deliverables (zip files) to the release.

19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2019 Autodesk Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
162 changes: 149 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,56 @@

Library and tools for AWS ECS operations.

Get golang: https://golang.org/dl/
# contributing

RTFM: https://github.com/golang/go/wiki/SettingGOPATH
See [CONTRIBUTING.md](CONTRIBUTING.md).

# tools

## update-aws-ecs-service

This tool is inspired by [AWS CodePipeline image definitions file method for updating existing ECS services](https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create.html#pipelines-create-image-definitions), this tool attempts to do something similar, in a standalone fashion without depending on AWS CodePipeline.
Reliably update a single ECS service with a single simple discrete command.

![flowchart](update-aws-ecs-service.png)

Is a deployment tool inspired by
[AWS CodePipeline image definitions file method for updating existing ECS services](https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create.html#pipelines-create-image-definitions).
This tool is first and foremost an acknowledgment that orchestrating application deployments is a **hard problem** and
does not attempt to solve that, instead, it tries to do something similar to AWS CodePipeline in a standalone fashion
without depending on AWS CodePipeline, and more importantly without having to create individual AWS CodePipeline
pipelines.

Get:

Grab binary distribution from [releases tab](https://github.com/Autodesk/go-awsecs/releases). Or.

```
go get -v -u github.com/andresvia/go-awsecs/cmd/update-aws-ecs-service
go get -v -u github.com/Autodesk/go-awsecs/cmd/update-aws-ecs-service
```

Use:
Use<sup>1</sup>:

```
$GOPATH/bin/update-aws-ecs-service -h
update-aws-ecs-service --help
Usage of update-aws-ecs-service:
-cluster string
cluster name
-container-envvar value
container-name=envvar-name=envvar-value
-container-image value
container-name=image
-container-secret value
container-name=secret-name=secret-valuefrom
-desired-count int
desired-count (negative: no change) (default -1)
-profile string
profile name
-region string
region name
-service string
service name
-taskdef string
base task definition (instead of current)
```

Example.
Expand All @@ -47,49 +66,166 @@ docker push myrepo/myimg:newtag
Then, alter the existing container image only, like AWS CodePipeline does.

```
AWS_PROFILE=myprofile AWS_REGION=myregion $GOPATH/bin/update-aws-ecs-service \
update-aws-ecs-service \
-cluster mycluster \
-service myservice \
-container-image mycontainer=myrepo/myimg:newtag
# default timeout for the operation is 15 minutes
```

Alternatively, you can also alter, environment variables and service desired count.
Alternatively, you can also alter environment variables and service desired count.

```
AWS_PROFILE=myprofile AWS_REGION=myregion $GOPATH/bin/update-aws-ecs-service \
update-aws-ecs-service \
-cluster mycluster \
-service myservice \
-container-image mycontainer=myrepo/myimg:newtag \
-container-envvar mycontainer=envvarname=envvarvalue \
-desired-count 1
```

💡 Use the empty value on `-container-envvar` or `-container-secret` to unset (K.O.) the environment variable or
secret. Example.

```
update-aws-ecs-service \
-cluster mycluster \
-service myservice \
-container-envvar mycontainer=myenvvarname= \
-container-secret mycontainer=mysecretname= \
```

### 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
- `update-aws-ecs-service` allow updates of container definitions "Environment" and
"[Secrets](https://aws.amazon.com/about-aws/whats-new/2018/11/aws-launches-secrets-support-for-amazon-elastic-container-servic/)"

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

Although similar results can be achieved glueing multiple `awscli` commands, a single `update-aws-ecs-service` is
different.

- `aws ecs update-service` only invokes `UpdateService` which is an async call
- `aws ecs wait services-stable` is not linked to the ECS Deployment Entity<sup>2</sup> returned by `UpdateService`
- `update-aws-ecs-service` provides automatic rollback

### update-aws-ecs-service compared to Terraform

It is a [known issue](https://github.com/terraform-providers/terraform-provider-aws/issues/3107) that Terraform, does
not wait for an ECS Service to be updated, a decision made probably by design by Hashicorp.

However, `update-aws-ecs-service` can be used in conjunction with Terraform, just keep in mind that when
**provisioning** a service, start with an "initial task definition", and configure the lifecycle of the
`task_definition` attribute to `ignore_changes`.

```
resource "aws_ecs_service" "my_service" {
task_definition = "my_initial_task_def"
// ...
lifecycle {
ignore_changes = ["task_definition" /* ... */]
}
}
```

That way Terraform will be maintained as the "provisioning tool" and `update-aws-ecs-service` as the "deployment tool".

### update-aws-ecs-service compared to Terraform+scripts

- Why not just do `aws ecs wait services-stable` commands after the `terraform apply` command

Caveat 1: `wait` evaluates service stability but not that the desired deployment is applied the service may have become
stable because it was rolled back or rolled forward somewhere else, there is no certainty that "our" deployment was
the one that rendered the service stable

Caveat 2: `wait` does not handle service deployment rollback

- Why not just do `curl|httpie` commands after the `terraform apply` command until a desired result is obtained
probably after a number of times, for example by looking at an endpoint that returns the "deployed version" like:
http://myservice.example.com/api/version returns `{"version": "v2.0.0"}`

Caveat 1: This works only for services which are public (internet reachable) or reachable from the same location
where `curl|httpie` is executed, this is not always the case, some services are internal or not reachable from every
location

Caveat 2: Works only for HTTP services that provide a "version" endpoint

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

TBC<sup>3</sup>.

### update-aws-ecs-service compared to amazon-ecs-cli

TBC.

### update-aws-ecs-service compared to ecs-deploy

The [ecs-deploy](https://github.com/silinternational/ecs-deploy) script
[doesn't recognize multi-container tasks](https://github.com/silinternational/ecs-deploy/issues/132).

### update-aws-ecs-service compared to ecs-goploy

[ecs-goploy](https://github.com/h3poteto/ecs-goploy) as a re-implementation of ecs-deploy shares the same caveats.

### update-aws-ecs-service compared to Autodesk CloudOS

`update-aws-ecs-service` is not a framework, is just a tool to update existing AWS ECS services. You just need to know
how to build Docker images.

### update-aws-ecs-service compared to X

TBC.

## enforce-aws-ecs-asg-launchconfig

This tool is useful to ensure that all EC2 instances in a ECS cluster backed up by a ASG, share the launch configuration defined in the ASG. This tool doesn't work with launch templates. ECS EC2 Container Instances will be drained. EC2 Instances will be terminated (after they are drained).

![flowchart](enforce-aws-ecs-asg-launchconfig.png)

This tool is useful to ensure that all EC2 instances in a ECS cluster backed up by a ASG share the launch configuration
defined in the ASG. This tool does not work with launch templates! ECS EC2 Container Instances will be drained. EC2
Instances will be terminated (after they have been drained).

**Important**: Depending of your cluster service(s) deployment configuration, services **will experiment downtime**.
For example use a service deployment configuration like, "Minimum healthy percent": `100` and "Maximum percent": `200`
to prevent downtime `enforce-aws-ecs-asg-launchconfig` does not do anything special to prevent downtime it depends
entirely of your cluster service(s) specific configuration(s).

Get:

Grab binary distribution from [releases tab](https://github.com/Autodesk/go-awsecs/releases). Or.

```
go get -v -u github.com/andresvia/go-awsecs/cmd/enforce-aws-ecs-asg-launchconfig
go get -v -u github.com/Autodesk/go-awsecs/cmd/enforce-aws-ecs-asg-launchconfig
```

Use:

```
$GOPATH/bin/enforce-aws-ecs-asg-launchconfig -h
enforce-aws-ecs-asg-launchconfig --help
Usage of enforce-aws-ecs-asg-launchconfig:
-asg string
asg name
-cluster string
cluster name
-profile string
profile name
-region string
region name
```

Example:

```
AWS_REGION=myregion AWS_PROFILE=myprofile $GOPATH/bin/enforce-aws-ecs-asg-launchconfig \
enforce-aws-ecs-asg-launchconfig \
-asg myasgname \
-cluster myclustername
# default timeout for the operation is 15 minutes
```

----

1. https://unix.stackexchange.com/a/111557/19393
2. https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_Deployment.html
3. To Be Compared
19 changes: 14 additions & 5 deletions cmd/enforce-aws-ecs-asg-launchconfig/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package main

import (
"flag"
"github.com/andresvia/go-awsecs"
"github.com/Autodesk/go-awsecs"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/ec2"
Expand All @@ -14,14 +15,22 @@ import (
func main() {
cluster := flag.String("cluster", "", "cluster name")
asg := flag.String("asg", "", "asg name")
profile := flag.String("profile", "", "profile name")
region := flag.String("region", "", "region name")
flag.Parse()

session := session.Must(session.NewSession())
sess := session.Must(session.NewSessionWithOptions(session.Options{
Profile: *profile,
}))

if *region != "" {
sess = sess.Copy(&aws.Config{Region: region})
}

elc := awsecs.EnforceLaunchConfig{
ECSAPI: *ecs.New(session),
ASAPI: *autoscaling.New(session),
EC2API: *ec2.New(session),
ECSAPI: *ecs.New(sess),
ASAPI: *autoscaling.New(sess),
EC2API: *ec2.New(sess),
ASGName: *asg,
ECSClusterName: *cluster,
BackOff: backoff.NewExponentialBackOff(),
Expand Down
Loading

0 comments on commit 3c10a6f

Please sign in to comment.