Skip to content

Commit

Permalink
Added ecs-deploy and updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
hamstah committed Jan 28, 2018
1 parent 0a2dde4 commit 47a9c28
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 5 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

Some specialised tools to avoid pulling boto3

* `elb`: ELB classic only (no ALB). Given a name returns the zone53 record associated with the ELB, including scheme (https returned if both available) and port.
* `elb-name`: Both ELB classic and ALB. Given a name, returns route53 record associated with the ELB. Does not include scheme or port as it doesn't check listeners.
* `s3-download`: Download a single file from s3
* `cloudwatch-put-metric-data`: Basic sending a metric value to cloudwatch
* `ec2-ip-from-name`: Given an EC2 name, list up to `-max-results` IPs associated with instances with that name (default is 1).
* `ecs`: Run a task definition
* `ecs-deploy`: Update the container images of a task and update services to use it
* `ecs-run-task`: Run a task definition
* `elb-resolve-elb-external-url`: ELB classic only (no ALB). Given a name returns the zone53 record associated with the ELB, including scheme (https returned if both available) and port.
* `elb-resolve-alb-external-url`: Both ELB classic and ALB. Given a name, returns route53 record associated with the ELB. Does not include scheme or port as it doesn't check listeners.
* `s3-download`: Download a single file from s3
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.1
3.2
137 changes: 137 additions & 0 deletions ecs/deploy/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package main

import (
"fmt"
"os"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecs"

kingpin "gopkg.in/alecthomas/kingpin.v2"
)

var (
app = kingpin.New("ecs-deploy", "Update a task definition on ECS")
region = app.Flag("region", "AWS Region").Default("eu-west-1").String()
taskName = app.Flag("task-name", "ECS task name").Required().String()
clusterName = app.Flag("cluster", "ECS cluster").Required().String()
services = app.Flag("service", "ECS services").Required().Strings()
images = app.Flag("images", "Change the images to the new ones. Container name=image").StringMap()
timeout = app.Flag("timeout", "Timeout when waiting for services to update").Default("300s").Duration()
)


func getTaskDefinition(svc *ecs.ECS, taskName string) (*ecs.TaskDefinition) {
input := &ecs.DescribeTaskDefinitionInput{
TaskDefinition: aws.String(taskName),
}

result, err := svc.DescribeTaskDefinition(input)
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to fetch the task definition", err)
os.Exit(1)
}
return result.TaskDefinition
}

func updateTaskDefinition(svc *ecs.ECS, taskDefinition *ecs.TaskDefinition) (*ecs.TaskDefinition) {
updateInput := &ecs.RegisterTaskDefinitionInput{
ContainerDefinitions: taskDefinition.ContainerDefinitions,
Cpu: taskDefinition.Cpu,
ExecutionRoleArn: taskDefinition.ExecutionRoleArn,
Family: taskDefinition.Family,
Memory: taskDefinition.Memory,
NetworkMode: taskDefinition.NetworkMode,
PlacementConstraints: taskDefinition.PlacementConstraints,
RequiresCompatibilities: taskDefinition.RequiresCompatibilities,
TaskRoleArn: taskDefinition.TaskRoleArn,
Volumes: taskDefinition.Volumes,
}

updateResult, err := svc.RegisterTaskDefinition(updateInput)
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to update the task definition", err)
os.Exit(1)
}
return updateResult.TaskDefinition
}

func main() {
kingpin.MustParse(app.Parse(os.Args[1:]))

config := aws.Config{Region: aws.String(*region)}
session := session.New(&config)
svc := ecs.New(session)

taskDefinition := getTaskDefinition(svc, *taskName)

if len(*images) != 0 {
for _, containerDefinition := range taskDefinition.ContainerDefinitions {
newImage := (*images)[*containerDefinition.Name]
if newImage != "" {
containerDefinition.Image = &newImage
}
}
}

newTaskDefinition := updateTaskDefinition(svc, taskDefinition)
fmt.Println(*newTaskDefinition.TaskDefinitionArn)

pending := 0
for _, service := range *services {

updateServiceInput := &ecs.UpdateServiceInput{
Cluster: aws.String(*clusterName),
Service: aws.String(service),
TaskDefinition: aws.String(*newTaskDefinition.TaskDefinitionArn),
}
_, err := svc.UpdateService(updateServiceInput)
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to update service", service, err)
} else {
pending += 1
}
}

serviceNamesInput := []*string{}
for _, service := range *services {
serviceNamesInput = append(serviceNamesInput, aws.String(service))
}

servicesInput := &ecs.DescribeServicesInput{
Cluster: aws.String(*clusterName),
Services: serviceNamesInput,
}

start := time.Now()
previousPending := 0
for pending > 0 {
servicesResult, err := svc.DescribeServices(servicesInput)
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to fetch services", err)
os.Exit(2)
}

previousPending = pending
pending = 0
for _, service := range servicesResult.Services {

if *service.Deployments[0].PendingCount != 0 {
pending += 1
}
}

if pending != 0 {
if time.Since(start) >= *timeout {
fmt.Println(os.Stderr, fmt.Sprintf("%s still pending, giving up after %s", pending, *timeout))
os.Exit(3)
}
if previousPending != pending {
fmt.Println(fmt.Sprintf("Waiting for %d service(s) to become ready", pending))
}
time.Sleep(1 * time.Second)
}
}
}
52 changes: 52 additions & 0 deletions scripts/create-release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
import github3
import getpass
import os
import mimetypes

base = os.path.join(os.path.dirname(__file__), "..")
version = open(os.path.join(base, "VERSION")).read().strip()

last_2fa = None

def my_two_factor_function():
global last_2fa
code = ''
while not code:
code = input('Enter 2FA code: [%s] ' % last_2fa) or last_2fa
last_2fa = code
return code


mimetypes.init()

current_user = os.getenv("USER")
username = input('Username: [%s] ' % current_user) or current_user
password = getpass.getpass('Password: ')

client = github3.login(username, password, two_factor_callback=my_two_factor_function)

repo = client.repository(username, 'awstools')

release = repo.release_from_tag('v%s' % version)
if release is None:
release = repo.create_release('v%s' % version, draft=False, prerelease=False)

bin_dir = os.path.join(base, "bin")
for file in os.listdir(bin_dir):
rel_file = os.path.join(bin_dir, file)
content_type, _ = mimetypes.guess_type(rel_file)
if content_type is None:
content_type = "application/octet-stream"

print(rel_file, content_type)
try:
asset = release.upload_asset(
content_type=content_type,
name=file,
asset=open(rel_file, 'rb').read(),
)
except Exception as e:
print(e)

print("\n\n\n")
7 changes: 7 additions & 0 deletions scripts/prepare-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

base=$(dirname $0)/..
version=$(cat ${base}/VERSION)

git tag -s v${version} -m "v${version}"
git push origin v${version}

0 comments on commit 47a9c28

Please sign in to comment.