Skip to content

Commit c2c4b14

Browse files
committed
fargate support
1 parent f182856 commit c2c4b14

File tree

7 files changed

+386
-185
lines changed

7 files changed

+386
-185
lines changed

cmd/exec.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
"github.com/apex/log"
6+
"github.com/spf13/cobra"
7+
"github.com/spf13/viper"
8+
"github.com/aws/aws-sdk-go/aws"
9+
"github.com/aws/aws-sdk-go/aws/session"
10+
"github.com/aws/aws-sdk-go/service/ecs"
11+
"github.com/springload/ecs-tool/lib"
12+
)
13+
14+
// execCmd executes a command in an existing ECS Fargate container.
15+
var execCmd = &cobra.Command{
16+
Use: "exec",
17+
Short: "Executes a command in an existing ECS Fargate container",
18+
Long: `Executes a specified command in a running container on an ECS Fargate cluster.
19+
This command allows for interactive sessions and command execution in Fargate.`,
20+
Args: cobra.MinimumNArgs(1),
21+
Run: func(cmd *cobra.Command, args []string) {
22+
// Command to be executed within the container
23+
command := args[0]
24+
25+
// Establish an AWS session using the specified profile and region from configuration
26+
sess, err := session.NewSessionWithOptions(session.Options{
27+
Profile: viper.GetString("profile"),
28+
Config: aws.Config{
29+
Region: aws.String(viper.GetString("region")),
30+
},
31+
})
32+
if err != nil {
33+
log.WithError(err).Error("Failed to create AWS session")
34+
os.Exit(1)
35+
}
36+
37+
// Create a new ECS service client with the session
38+
svc := ecs.New(sess)
39+
40+
// Execute the command in the specified ECS container using the ECS service client
41+
err = lib.ExecuteCommandInContainer(svc, viper.GetString("cluster"), viper.GetString("service_name"), viper.GetString("container_name"), command)
42+
if err != nil {
43+
log.WithError(err).Error("Failed to execute command in ECS Fargate container")
44+
os.Exit(1)
45+
} else {
46+
log.Info("Command executed successfully in ECS Fargate container")
47+
os.Exit(0)
48+
}
49+
},
50+
}
51+
52+
func init() {
53+
rootCmd.AddCommand(execCmd)
54+
execCmd.Flags().String("profile", "", "AWS profile to use")
55+
execCmd.Flags().String("region", "", "AWS region to operate in")
56+
execCmd.Flags().String("cluster", "", "Name of the ECS cluster")
57+
execCmd.Flags().String("service_name", "", "Name of the ECS service")
58+
execCmd.Flags().String("container_name", "", "Name of the container in the task")
59+
60+
viper.BindPFlag("profile", execCmd.Flags().Lookup("profile"))
61+
viper.BindPFlag("region", execCmd.Flags().Lookup("region"))
62+
viper.BindPFlag("cluster", execCmd.Flags().Lookup("cluster"))
63+
viper.BindPFlag("service_name", execCmd.Flags().Lookup("service_name"))
64+
viper.BindPFlag("container_name", execCmd.Flags().Lookup("container_name"))
65+
66+
// Set default values or read from a configuration file
67+
viper.SetDefault("region", "us-east-1")
68+
viper.SetDefault("container_name", "default-container")
69+
}

cmd/root.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,17 @@ func init() {
5151
rootCmd.PersistentFlags().StringP("workdir", "w", "", "Set working directory")
5252
rootCmd.PersistentFlags().StringP("image_tag", "", "", "Overrides the docker image tag in all container definitions. Overrides \"--image-tags\" flag.")
5353
rootCmd.PersistentFlags().StringSliceP("image_tags", "", []string{}, "Modifies the docker image tags in container definitions. Can be specified several times, one for each container definition. Also takes comma-separated values in one tag. I.e. if there are 2 containers and --image-tags is set once to \"new\", then the image tag of the first container will be modified, leaving the second one untouched. Gets overridden by \"--image-tag\". If you have 3 container definitions and want to modify tags for the 1st and the 3rd, but leave the 2nd unchanged, specify it as \"--image_tags first_tag,,last_tag\".")
54+
rootCmd.PersistentFlags().StringP("task_definition", "t", "", "Name of the ECS task definition to use (required)")
55+
56+
5457

5558
viper.BindPFlag("profile", rootCmd.PersistentFlags().Lookup("profile"))
5659
viper.BindPFlag("cluster", rootCmd.PersistentFlags().Lookup("cluster"))
5760
viper.BindPFlag("workdir", rootCmd.PersistentFlags().Lookup("workdir"))
5861
viper.BindPFlag("image_tag", rootCmd.PersistentFlags().Lookup("image_tag"))
5962
viper.BindPFlag("image_tags", rootCmd.PersistentFlags().Lookup("image_tags"))
63+
viper.BindPFlag("task_definition", rootCmd.PersistentFlags().Lookup("task_definition"))
64+
6065

6166
}
6267

cmd/run.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/spf13/cobra"
88
"github.com/spf13/viper"
99
"github.com/springload/ecs-tool/lib"
10+
"fmt"
1011
)
1112

1213
var runCmd = &cobra.Command{
@@ -52,9 +53,9 @@ func init() {
5253
rootCmd.AddCommand(runCmd)
5354
runCmd.PersistentFlags().StringP("log_group", "l", "", "Name of the log group to get output")
5455
runCmd.PersistentFlags().StringP("container_name", "", "", "Name of the container to modify parameters for")
55-
runCmd.PersistentFlags().StringP("task_definition", "t", "", "name of task definition to use (required)")
5656
viper.BindPFlag("log_group", runCmd.PersistentFlags().Lookup("log_group"))
5757
viper.BindPFlag("container_name", runCmd.PersistentFlags().Lookup("container_name"))
58-
viper.BindPFlag("task_definition", runCmd.PersistentFlags().Lookup("task_definition"))
58+
//viper.BindPFlag("task_definition", runCmd.PersistentFlags().Lookup("task_definition"))
5959
viper.SetDefault("run.launch_type", "EC2")
60+
fmt.Println("Default launch_type set to:", viper.GetString("run.launch_type"))
6061
}

cmd/runFargate.go

Lines changed: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,57 @@
11
package cmd
22

33
import (
4-
"os"
4+
"os"
55

6-
"github.com/apex/log"
7-
"github.com/spf13/cobra"
8-
"github.com/spf13/viper"
9-
"github.com/springload/ecs-tool/lib"
6+
"github.com/apex/log"
7+
"github.com/spf13/cobra"
8+
"github.com/spf13/viper"
9+
"github.com/springload/ecs-tool/lib"
1010
)
1111

12-
var runCmd = &cobra.Command{
13-
Use: "runFargate",
14-
Short: "Runs a command",
15-
Long: `Runs the specified command on an ECS cluster, optionally catching its output.
16-
17-
It can modify the container command.
18-
`,
19-
Args: cobra.MinimumNArgs(1),
20-
Run: func(cmd *cobra.Command, args []string) {
21-
var containerName string
22-
var commandArgs []string
23-
if name := viper.GetString("container_name"); name == "" {
24-
containerName = args[0]
25-
commandArgs = args[1:]
26-
} else {
27-
containerName = name
28-
commandArgs = args
29-
}
30-
31-
exitCode, err := lib.RunTask(
32-
viper.GetString("profile"),
33-
viper.GetString("cluster"),
34-
viper.GetString("run.service"),
35-
viper.GetString("task_definition"),
36-
viper.GetString("image_tag"),
37-
viper.GetStringSlice("image_tags"),
38-
viper.GetString("workdir"),
39-
containerName,
40-
viper.GetString("log_group"),
41-
viper.GetString("run.launch_type"),
42-
commandArgs,
43-
)
44-
if err != nil {
45-
log.WithError(err).Error("Can't run task")
46-
}
47-
os.Exit(exitCode)
48-
},
12+
var runFargateCmd = &cobra.Command{
13+
Use: "runFargate",
14+
Short: "Runs a command in Fargate mode",
15+
Long: `Runs the specified command on an ECS cluster, optionally catching its output.
16+
17+
This command is specifically tailored for future Fargate-specific functionality but currently duplicates the 'run' command.`,
18+
Args: cobra.MinimumNArgs(1),
19+
Run: func(cmd *cobra.Command, args []string) {
20+
var containerName string
21+
var commandArgs []string
22+
if name := viper.GetString("container_name"); name == "" {
23+
containerName = args[0]
24+
commandArgs = args[1:]
25+
} else {
26+
containerName = name
27+
commandArgs = args
28+
}
29+
30+
exitCode, err := lib.RunFargate(
31+
viper.GetString("profile"),
32+
viper.GetString("cluster"),
33+
viper.GetString("run.service"),
34+
viper.GetString("task_definition"),
35+
viper.GetString("image_tag"),
36+
viper.GetStringSlice("image_tags"),
37+
viper.GetString("workdir"),
38+
containerName,
39+
viper.GetString("log_group"),
40+
viper.GetString("run.launch_type"),
41+
viper.GetString("run.security_group_filter"),
42+
commandArgs,
43+
)
44+
if err != nil {
45+
log.WithError(err).Error("Can't run task in Fargate mode")
46+
}
47+
os.Exit(exitCode)
48+
},
4949
}
5050

5151
func init() {
52-
rootCmd.AddCommand(runCmd)
53-
runCmd.PersistentFlags().StringP("log_group", "l", "", "Name of the log group to get output")
54-
runCmd.PersistentFlags().StringP("container_name", "", "", "Name of the container to modify parameters for")
55-
runCmd.PersistentFlags().StringP("task_definition", "t", "", "name of task definition to use (required)")
56-
viper.BindPFlag("log_group", runCmd.PersistentFlags().Lookup("log_group"))
57-
viper.BindPFlag("container_name", runCmd.PersistentFlags().Lookup("container_name"))
58-
viper.BindPFlag("task_definition", runCmd.PersistentFlags().Lookup("task_definition"))
59-
viper.SetDefault("run.launch_type", "EC2")
52+
rootCmd.AddCommand(runFargateCmd)
53+
viper.SetDefault("run.security_group_filter", "*ec2*")
54+
viper.SetDefault("run.launch_type", "FARGATE")
55+
56+
6057
}

lib/exec.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package lib
2+
3+
import (
4+
"fmt"
5+
"github.com/aws/aws-sdk-go/aws"
6+
//"github.com/aws/aws-sdk-go/aws/session"
7+
"github.com/aws/aws-sdk-go/service/ecs"
8+
"github.com/aws/aws-sdk-go/service/ecs/ecsiface"
9+
)
10+
11+
// FindLatestTaskArn finds the latest task ARN for the specified service within a cluster
12+
func FindLatestTaskArn(svc ecsiface.ECSAPI, clusterName, serviceName string) (string, error) {
13+
input := &ecs.ListTasksInput{
14+
Cluster: aws.String(clusterName),
15+
ServiceName: aws.String(serviceName),
16+
DesiredStatus: aws.String("RUNNING"),
17+
MaxResults: aws.Int64(1),
18+
}
19+
20+
result, err := svc.ListTasks(input)
21+
if err != nil || len(result.TaskArns) == 0 {
22+
return "", fmt.Errorf("no running tasks found for service %s on cluster %s", serviceName, clusterName)
23+
}
24+
25+
return aws.StringValue(result.TaskArns[0]), nil
26+
}
27+
28+
// ExecuteCommandInContainer executes a specified command in a running container on an ECS Fargate cluster.
29+
func ExecuteCommandInContainer(svc ecsiface.ECSAPI, cluster, serviceName, containerName, command string) error {
30+
taskArn, err := FindLatestTaskArn(svc, cluster, serviceName)
31+
if err != nil {
32+
return err
33+
}
34+
35+
input := &ecs.ExecuteCommandInput{
36+
Cluster: aws.String(cluster),
37+
Task: aws.String(taskArn),
38+
Container: aws.String(containerName),
39+
Interactive: aws.Bool(true),
40+
Command: aws.String(command),
41+
}
42+
43+
_, err = svc.ExecuteCommand(input)
44+
if err != nil {
45+
return fmt.Errorf("failed to execute command: %v", err)
46+
}
47+
48+
return nil
49+
}

lib/run.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ import (
1010

1111
// RunTask runs the specified one-off task in the cluster using the task definition
1212
func RunTask(profile, cluster, service, taskDefinitionName, imageTag string, imageTags []string, workDir, containerName, awslogGroup, launchType string, args []string) (exitCode int, err error) {
13+
ctx := log.WithFields(log.Fields{
14+
"task_definition": taskDefinitionName,
15+
"launch_type": launchType,
16+
})
1317
err = makeSession(profile)
1418
if err != nil {
1519
return 1, err
1620
}
17-
ctx := log.WithFields(&log.Fields{"task_definition": taskDefinitionName})
18-
21+
1922
svc := ecs.New(localSession)
2023

2124
describeResult, err := svc.DescribeTaskDefinition(&ecs.DescribeTaskDefinitionInput{

0 commit comments

Comments
 (0)