/
aws-rollout.go
150 lines (135 loc) · 3.88 KB
/
aws-rollout.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"errors"
"fmt"
"os"
"regexp"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecs"
flag "github.com/ogier/pflag"
)
func failOnError(err error) {
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
}
// findClusterArn finds a cluster's arn from a more human
// readable name
func findClusterArn(svc *ecs.ECS, clusterName string) (string, error) {
params := &ecs.ListClustersInput{}
clusters, err := svc.ListClusters(params)
if err != nil {
return "", err
}
for _, ClusterID := range clusters.ClusterArns {
var pattern = `/` + clusterName + `$`
matched, _ := regexp.MatchString(pattern, *ClusterID)
if matched {
return *ClusterID, nil
}
}
return "", errors.New("Could not find cluster with name: " + clusterName)
}
// findServiceArn finds a service arn from a more human
// readable name
func findServiceArn(svc *ecs.ECS, clusterArn string, serviceName string) (string, error) {
params := &ecs.ListServicesInput{
Cluster: aws.String(clusterArn),
}
params.SetMaxResults(100)
services, err := svc.ListServices(params)
if err != nil {
return "", err
}
for _, ServiceID := range services.ServiceArns {
var pattern = `/` + serviceName + `$`
matched, _ := regexp.MatchString(pattern, *ServiceID)
if matched {
return *ServiceID, nil
}
}
return "", errors.New("Could not find service with name: " + serviceName)
}
// findTaskArn
// finds the first task definition in a cluster and service.
//
// Returns task ARN
//
// TODO:
// filter the resulting tasks by essential: true
// and only return the essential?
// either that or assume that the new image will
// somewhat match the old image, and use that to pick
// one of the tasks.
func findTaskArn(svc *ecs.ECS, clusterArn string, serviceArn string) (string, error) {
params := &ecs.DescribeServicesInput{
Services: []*string{
aws.String(serviceArn),
},
Cluster: aws.String(clusterArn),
}
resp, err := svc.DescribeServices(params)
return *resp.Services[0].TaskDefinition, err
}
// setImage
// Create a new Task Definition based on an existing
// ARN, and a new image.
//
// Returns new task's ARN
//
func setImage(svc *ecs.ECS, taskArn string, image string) (string, error) {
params := &ecs.DescribeTaskDefinitionInput{TaskDefinition: aws.String(taskArn)}
resp, err := svc.DescribeTaskDefinition(params)
if err != nil {
return "", err
}
task := resp.TaskDefinition
var out = taskArn
if *task.ContainerDefinitions[0].Image != image {
task.ContainerDefinitions[0].Image = &image
regResp, err := svc.RegisterTaskDefinition(&ecs.RegisterTaskDefinitionInput{
Family: task.Family,
ContainerDefinitions: task.ContainerDefinitions,
Volumes: task.Volumes,
})
if err != nil {
return "", err
}
out = *regResp.TaskDefinition.TaskDefinitionArn
}
return out, nil
}
func main() {
var cluster = flag.String("cluster", "default", "Name of cluster")
flag.Parse()
if flag.NArg() < 2 {
fmt.Println("Usage:\n\taws-rollout [service] [image]")
return
}
var service = flag.Arg(0)
var image = flag.Arg(1)
fmt.Printf("Cluster: %s\n", *cluster)
fmt.Printf("Service: %s\n", service)
fmt.Printf("Image: %s\n", image)
svc := ecs.New(session.New())
clusterArn, err := findClusterArn(svc, *cluster)
failOnError(err)
serviceArn, err := findServiceArn(svc, clusterArn, service)
failOnError(err)
taskArn, err := findTaskArn(svc, clusterArn, serviceArn)
failOnError(err)
newTaskArn, err := setImage(svc, taskArn, image)
failOnError(err)
params := &ecs.UpdateServiceInput{
Service: aws.String(serviceArn),
Cluster: aws.String(clusterArn),
TaskDefinition: aws.String(newTaskArn),
}
serv, err := svc.UpdateService(params)
failOnError(err)
fmt.Printf("Deployed Task: %s\n", newTaskArn)
fmt.Printf("Pending Count: %d\n", *serv.Service.PendingCount)
fmt.Println("Deployment Success!")
}