Skip to content

Commit

Permalink
feat: instances category; instances list
Browse files Browse the repository at this point in the history
  • Loading branch information
Rafael Gumieri committed Nov 9, 2019
1 parent 9b13ba8 commit 4ab203a
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 42 deletions.
19 changes: 17 additions & 2 deletions cmd/clusters_list.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package cmd

import (
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/spf13/cobra"
)
Expand All @@ -18,8 +21,16 @@ func clustersListRun(cmd *cobra.Command, clusters []string) {
result, err := ecsI.ListClusters(input)
t.Must(err)

for _, f := range result.ClusterArns {
t.Outln(aws.StringValue(f))
for _, clusterARN := range result.ClusterArns {
if listARN {
t.Outln(aws.StringValue(clusterARN))
continue
}

parsedARN, err := arn.Parse(aws.StringValue(clusterARN))
t.Must(err)
parsedResource := strings.Split(parsedARN.Resource, "/")
t.Outln(parsedResource[len(parsedResource)-1])
}

if result.NextToken == nil {
Expand All @@ -38,4 +49,8 @@ var clustersListCmd = &cobra.Command{

func init() {
clustersCmd.AddCommand(clustersListCmd)

flags := clustersListCmd.Flags()

flags.BoolVar(&listARN, "arn", false, listARNSpec)
}
20 changes: 20 additions & 0 deletions cmd/instances.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cmd

import (
"github.com/spf13/cobra"
)

func instancesRun(cmd *cobra.Command, args []string) {
cmd.Help()
}

var instancesCmd = &cobra.Command{
Use: "instances [command]",
Short: "Commands to manage ECS instances",
Aliases: []string{"instance", "i"},
Run: instancesRun,
}

func init() {
rootCmd.AddCommand(instancesCmd)
}
194 changes: 194 additions & 0 deletions cmd/instances_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package cmd

import (
"fmt"
"os"
"strconv"
"strings"
"text/tabwriter"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// CompleteInstance has both ECS and EC2 instance description
type CompleteInstance struct {
EC2 *ec2.Instance
ECS *ecs.ContainerInstance
}

// SpotFleetRequestID get the Spot Fleet Request ID from the EC2 tags
func (i *CompleteInstance) SpotFleetRequestID() *string {
for _, tag := range i.EC2.Tags {
if aws.StringValue(tag.Key) == "aws:ec2spot:fleet-request-id" {
return tag.Value
}
}
return nil
}

func (i *CompleteInstance) formatProperty(property string, header bool) string {
switch property {
case "instance-id":
if header {
return "Instance ID"
}

return aws.StringValue(i.EC2.InstanceId)

case "ami-id":
if header {
return "AMI ID"
}

return aws.StringValue(i.EC2.ImageId)

case "sfr-id":
if header {
return "Spot Fleet request ID"
}

return aws.StringValue(i.SpotFleetRequestID())

case "running-tasks":
if header {
return "Tasks"
}

return strconv.FormatInt(aws.Int64Value(i.ECS.RunningTasksCount), 10)

case "status":
if header {
return "Status"
}

return aws.StringValue(i.ECS.Status)

case "status-reason":
if header {
return "Status reason"
}

return aws.StringValue(i.ECS.StatusReason)

case "agent-connected":
if header {
return "agent connected?"
}

if aws.BoolValue(i.ECS.AgentConnected) {
return "Yes"
}

return "No"

case "launch-time":
if header {
return "Status"
}

return aws.TimeValue(i.EC2.LaunchTime).Format("2006-01-02T15:04:05")

}

return ""
}

func instancesListRun(cmd *cobra.Command, instances []string) {
clustersDescription, err := ecsI.DescribeClusters(&ecs.DescribeClustersInput{
Clusters: []*string{
aws.String(viper.GetString("cluster")),
},
})

t.Must(err)

if len(clustersDescription.Clusters) == 0 {
t.Exitln("Source Cluster informed not found")
}

c := clustersDescription.Clusters[0]

instancesList, err := ecsI.ListContainerInstances(&ecs.ListContainerInstancesInput{
Cluster: c.ClusterName,
})

t.Must(err)

instancesDescription, err := ecsI.DescribeContainerInstances(&ecs.DescribeContainerInstancesInput{
Cluster: c.ClusterName,
ContainerInstances: instancesList.ContainerInstanceArns,
})
t.Must(err)

ec2InstancesIDs := make([]*string, 0)
completeInstances := make([]*CompleteInstance, 0)
for _, instance := range instancesDescription.ContainerInstances {
ec2InstancesIDs = append(ec2InstancesIDs, instance.Ec2InstanceId)
completeInstances = append(completeInstances, &CompleteInstance{ECS: instance})
}

ec2Description, err := ec2I.DescribeInstances(&ec2.DescribeInstancesInput{InstanceIds: ec2InstancesIDs})
t.Must(err)

for _, reservation := range ec2Description.Reservations {
for _, instance := range reservation.Instances {
for _, completeInstance := range completeInstances {
if aws.StringValue(completeInstance.ECS.Ec2InstanceId) != aws.StringValue(instance.InstanceId) {
continue
}

completeInstance.EC2 = instance
}
}
}

properties := viper.GetStringSlice("properties")

header := []string{}
for _, p := range properties {
header = append(header, completeInstances[0].formatProperty(p, true))
}

w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
fmt.Fprintln(w, strings.Join(header, "\t"))

for _, i := range completeInstances {
line := []string{}
for _, p := range properties {
line = append(line, i.formatProperty(p, false))
}
fmt.Fprintln(w, strings.Join(line, "\t"))
}

w.Flush()
}

var instancesListCmd = &cobra.Command{
Use: "list",
Short: "List instances of specified cluster",
Run: instancesListRun,
}

func init() {
instancesCmd.AddCommand(instancesListCmd)

flags := instancesListCmd.Flags()

flags.StringP("cluster", "c", "", requiredSpec+clusterSpec)
viper.BindPFlag("cluster", instancesListCmd.Flags().Lookup("cluster"))

defaultColumns := []string{
"instance-id",
"running-tasks",
"status",
"agent-connected",
}

flags.StringSliceP("properties", "p", defaultColumns, "properties to be listed as column\n")

viper.BindPFlag("properties", instancesListCmd.Flags().Lookup("properties"))
}
2 changes: 1 addition & 1 deletion cmd/services_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ func init() {

flags.BoolVar(&listARN, "arn", false, listARNSpec)

servicesCopyCmd.MarkFlagRequired("cluster")
servicesListCmd.MarkFlagRequired("cluster")

}
41 changes: 2 additions & 39 deletions cmd/user_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,15 @@ package cmd

var linuxUserData = `
#!/bin/bash
echo ECS_CLUSTER={{.Cluster}} >> /etc/ecs/ecs.config;echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config;
echo ECS_CLUSTER={{.Cluster}} >> /etc/ecs/ecs.config
echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config
`

var linuxSpotFleetUserData = `
#!/bin/bash
echo ECS_CLUSTER={{.Cluster}} >> /etc/ecs/ecs.config
echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config
echo ECS_ENABLE_SPOT_INSTANCE_DRAINING=true >> /etc/ecs/ecs.config
export PATH=/usr/local/bin:$PATH
yum -y install jq
easy_install pip
pip install awscli
aws configure set default.region {{.Region}}
cat <<EOF > /etc/init/spot-instance-termination-notice-handler.conf
description "Start spot instance termination handler monitoring script"
author "Amazon Web Services"
start on started ecs
script
echo \$\$ > /var/run/spot-instance-termination-notice-handler.pid
exec /usr/local/bin/spot-instance-termination-notice-handler.sh
end script
pre-start script
logger "[spot-instance-termination-notice-handler.sh]: spot instance termination
notice handler started"
end script
EOF
cat <<EOF > /usr/local/bin/spot-instance-termination-notice-handler.sh
#!/bin/bash
while sleep 5; do
if [ -z \$(curl -Isf http://169.254.169.254/latest/meta-data/spot/termination-time)]; then
/bin/false
else
logger "[spot-instance-termination-notice-handler.sh]: spot instance termination notice detected"
STATUS=DRAINING
ECS_CLUSTER=\$(curl -s http://localhost:51678/v1/metadata | jq .Cluster | tr -d \")
CONTAINER_INSTANCE=\$(curl -s http://localhost:51678/v1/metadata | jq .ContainerInstanceArn | tr -d \")
logger "[spot-instance-termination-notice-handler.sh]: putting instance in state \$STATUS"
/usr/local/bin/aws ecs update-container-instances-state --cluster \$ECS_CLUSTER --container-instances \$CONTAINER_INSTANCE --status \$STATUS
logger "[spot-instance-termination-notice-handler.sh]: putting myself to sleep..."
sleep 120 # exit loop as instance expires in 120 secs after terminating notification
fi
done
EOF
chmod +x /usr/local/bin/spot-instance-termination-notice-handler.sh
`

var windowsUserData = `<powershell> Initialize-ECSAgent -Cluster {{.Cluster}} -EnableTaskIAMRole -LoggingDrivers '["json-file","awslogs"]' </powershell>`
Expand Down

0 comments on commit 4ab203a

Please sign in to comment.