Skip to content

Commit 6dbe79f

Browse files
committed
Merge pull request #4 from CpuID/cpuid_multi_cluster
Multi ECS Cluster Support
2 parents 8beee25 + e233af2 commit 6dbe79f

File tree

2 files changed

+92
-26
lines changed

2 files changed

+92
-26
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ container instances for now). The result will be a CSV of VPC/private IPs, exclu
4040
instance (you normally don't want to attempt to join yourself if orchestrating a cluster):
4141

4242
```
43-
root@cddb6164b344:/# ./discoverer nginx
43+
root@cddb6164b344:/# ./ecs-discoverer -s nginx
4444
10.20.0.97
4545
```
4646

47+
Use `./ecs-discoverer --help` for a full help listing.
48+
4749
## Building
4850

4951
```

main.go

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/aws/aws-sdk-go/aws/session"
1212
"github.com/aws/aws-sdk-go/service/ec2"
1313
"github.com/aws/aws-sdk-go/service/ecs"
14+
"github.com/codegangsta/cli"
1415
"io/ioutil"
1516
"net/http"
1617
"os"
@@ -65,6 +66,30 @@ func formatAwsError(err error) {
6566
}
6667
}
6768

69+
// Verify that the ECS cluster exists.
70+
func verifyClusterExists(ecs_obj *ecs.ECS, cluster string) {
71+
params := &ecs.DescribeClustersInput{
72+
Clusters: []*string{
73+
aws.String(cluster),
74+
},
75+
}
76+
clusters, err := ecs_obj.DescribeClusters(params)
77+
78+
if err != nil {
79+
fmt.Println("Cannot verify if ECS cluster exists:")
80+
formatAwsError(err)
81+
os.Exit(1)
82+
}
83+
if len(clusters.Clusters) == 0 {
84+
fmt.Printf("Error: ECS Cluster '%s' does not exist, cannot proceed.\n", cluster)
85+
os.Exit(1)
86+
}
87+
if len(clusters.Clusters) != 1 {
88+
fmt.Printf("Error: Unexpected number of ECS Clusters returned when searching for '%s'. Received: %+v\n", cluster, clusters.Clusters)
89+
os.Exit(1)
90+
}
91+
}
92+
6893
// Verify that the ECS service exists.
6994
func verifyServiceExists(ecs_obj *ecs.ECS, cluster string, service string) {
7095
params := &ecs.DescribeServicesInput{
@@ -82,7 +107,7 @@ func verifyServiceExists(ecs_obj *ecs.ECS, cluster string, service string) {
82107
}
83108
}
84109

85-
func getContainerInstanceArnsForService(ecs_obj *ecs.ECS, cluster string, service string, local_container_instance_arn string) []string {
110+
func getContainerInstanceArnsForService(ecs_obj *ecs.ECS, cluster string, service string, local_container_instance_arn string, debug bool) []string {
86111
// Fetch a task list based on the service name.
87112
list_tasks_params := &ecs.ListTasksInput{
88113
Cluster: &cluster,
@@ -137,7 +162,7 @@ func getContainerInstanceArnsForService(ecs_obj *ecs.ECS, cluster string, servic
137162
return result
138163
}
139164

140-
func getEc2InstanceIdsFromContainerInstances(ecs_obj *ecs.ECS, cluster string, container_instances []string) []string {
165+
func getEc2InstanceIdsFromContainerInstances(ecs_obj *ecs.ECS, cluster string, container_instances []string, debug bool) []string {
141166
params := &ecs.DescribeContainerInstancesInput{
142167
Cluster: aws.String(cluster),
143168
ContainerInstances: aws.StringSlice(container_instances),
@@ -173,7 +198,7 @@ func getEc2InstanceIdsFromContainerInstances(ecs_obj *ecs.ECS, cluster string, c
173198
return result
174199
}
175200

176-
func getEc2PrivateIpsFromInstanceIds(ec2_obj *ec2.EC2, instance_ids []string) []string {
201+
func getEc2PrivateIpsFromInstanceIds(ec2_obj *ec2.EC2, instance_ids []string, debug bool) []string {
177202
params := &ec2.DescribeInstancesInput{
178203
InstanceIds: aws.StringSlice(instance_ids),
179204
}
@@ -214,22 +239,7 @@ func getEc2PrivateIpsFromInstanceIds(ec2_obj *ec2.EC2, instance_ids []string) []
214239
return result
215240
}
216241

217-
var debug bool
218-
219-
func main() {
220-
// Check that the ECS service name is passed in as an argument.
221-
if len(os.Args) != 2 && len(os.Args) != 3 {
222-
fmt.Println("Usage:", os.Args[0], "ecs_service_name [debug]")
223-
os.Exit(1)
224-
}
225-
ecs_service := os.Args[1]
226-
if len(os.Args) == 3 && os.Args[2] == "debug" {
227-
fmt.Println("Debug Mode Enabled")
228-
debug = true
229-
} else {
230-
debug = false
231-
}
232-
242+
func doMain(current_cluster bool, cluster_name string, ecs_service string, debug bool) {
233243
// Get the metadata from the ECS agent on the local Docker host.
234244
local_ecs_agent_metadata := getEcsAgentMetadata()
235245

@@ -242,17 +252,26 @@ func main() {
242252
os.Exit(1)
243253
}
244254

245-
// Discover the ECS cluster this EC2 instance belongs to, via local ECS agent.
246-
ecs_cluster := local_ecs_agent_metadata.Cluster
247-
248255
// Reusable config session object for AWS services with current region attached.
256+
// TODOLATER: support other regions via a flag.
249257
aws_config_session := session.New(&aws.Config{Region: aws.String(region)})
250258

251259
// Create an ECS service object.
252260
ecs_obj := ecs.New(aws_config_session)
253261
// Create an EC2 service object.
254262
ec2_obj := ec2.New(aws_config_session)
255263

264+
var ecs_cluster string
265+
if current_cluster == true {
266+
// Discover the ECS cluster this EC2 instance belongs to, via local ECS agent.
267+
ecs_cluster = local_ecs_agent_metadata.Cluster
268+
} else {
269+
// Use the user provided WAN cluster, for connecting to services in a different ECS Cluster.
270+
// First we verify the cluster exists before proceeding.
271+
verifyClusterExists(ecs_obj, cluster_name)
272+
ecs_cluster = cluster_name
273+
}
274+
256275
// Check that the service exists.
257276
verifyServiceExists(ecs_obj, ecs_cluster, ecs_service)
258277

@@ -261,22 +280,67 @@ func main() {
261280

262281
// Get all tasks for the given service, in this ECS cluster. We exclude the current container instance in the result,
263282
// as we only need to know about all other instances.
264-
container_instances := getContainerInstanceArnsForService(ecs_obj, ecs_cluster, ecs_service, local_ecs_agent_metadata.ContainerInstanceArn)
283+
container_instances := getContainerInstanceArnsForService(ecs_obj, ecs_cluster, ecs_service, local_ecs_agent_metadata.ContainerInstanceArn, debug)
265284
if debug == true {
266285
fmt.Println("container_instances:", strings.Join(container_instances, ","))
267286
}
268287

269288
// Get EC2 instance IDs for all container instances returned.
270-
instance_ids := getEc2InstanceIdsFromContainerInstances(ecs_obj, ecs_cluster, container_instances)
289+
instance_ids := getEc2InstanceIdsFromContainerInstances(ecs_obj, ecs_cluster, container_instances, debug)
271290
if debug == true {
272291
fmt.Println("instance_ids:", strings.Join(instance_ids, ","))
273292
}
274293

275294
// Get the private IP of the EC2 (container) instance running the ECS agent.
276-
instance_private_ips := getEc2PrivateIpsFromInstanceIds(ec2_obj, instance_ids)
295+
instance_private_ips := getEc2PrivateIpsFromInstanceIds(ec2_obj, instance_ids, debug)
277296
if debug == true {
278297
fmt.Println("instance_private_ips:", strings.Join(instance_private_ips, ","))
279298
}
280299

281300
fmt.Println(strings.Join(instance_private_ips, ","))
282301
}
302+
303+
func parseFlags(c *cli.Context) (bool, string, string, bool) {
304+
current_cluster := false
305+
cluster := ""
306+
if c.String("c") == "" {
307+
current_cluster = true
308+
} else {
309+
cluster = c.String("cluster")
310+
}
311+
if c.String("s") == "" {
312+
fmt.Printf("Error: Service (-s) must not be empty. Cannot proceed.\n\n")
313+
cli.ShowAppHelp(c)
314+
os.Exit(1)
315+
}
316+
return current_cluster, cluster, c.String("s"), c.Bool("d")
317+
}
318+
319+
func main() {
320+
app := cli.NewApp()
321+
app.Name = "ecs-discoverer"
322+
app.Version = "0.3.0"
323+
app.Usage = "Discovery tool for Private IPs of ECS EC2 Container Instances for a given Service/Cluster"
324+
app.Action = func(c *cli.Context) {
325+
current_cluster, cluster, service, debug := parseFlags(c)
326+
doMain(current_cluster, cluster, service, debug)
327+
}
328+
app.Flags = []cli.Flag{
329+
cli.StringFlag{
330+
Name: "c",
331+
Value: "",
332+
Usage: "ECS Cluster Name (Optional - defaults to the ECS Cluster of this instance only)",
333+
},
334+
cli.BoolFlag{
335+
Name: "d",
336+
Usage: "Debug Mode",
337+
},
338+
cli.StringFlag{
339+
Name: "s",
340+
Value: "",
341+
Usage: "ECS Service Name (Required)",
342+
},
343+
}
344+
345+
app.Run(os.Args)
346+
}

0 commit comments

Comments
 (0)