Skip to content

Commit

Permalink
Closes #49 - Upgrade to AWS SDK v2 (#50)
Browse files Browse the repository at this point in the history
* feat: upgrade AWS SDK to v2

Signed-off-by: Theo Bob Massard <tbobm@protonmail.com>

* tests: adapt tests to SDK v2

Signed-off-by: Theo Bob Massard <tbobm@protonmail.com>

* feat: add ECSClient interface and replace usage of concrete ecs.Client type in App

* tests: update tests to use ECSClient interface and update mocks

* feat: create EC2Client interface

* tests: fix remaining tests

* feat: swap use of ecs.client for ECSClient in getContainerPort

---------

Signed-off-by: Theo Bob Massard <tbobm@protonmail.com>
Co-authored-by: Theo Bob Massard <tbobm@protonmail.com>
  • Loading branch information
tedsmitt and tbobm authored Oct 10, 2024
1 parent c3beef8 commit 7260d05
Show file tree
Hide file tree
Showing 10 changed files with 515 additions and 433 deletions.
17 changes: 16 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,29 @@ go 1.18

require (
github.com/AlecAivazis/survey/v2 v2.2.9
github.com/aws/aws-sdk-go v1.50.6
github.com/aws/aws-sdk-go-v2 v1.24.0
github.com/aws/aws-sdk-go-v2/config v1.26.2
github.com/aws/aws-sdk-go-v2/service/ec2 v1.142.0
github.com/aws/aws-sdk-go-v2/service/ecs v1.35.6
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.6
github.com/fatih/color v1.10.0
github.com/spf13/cobra v1.1.3
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.3.0
)

require (
github.com/aws/aws-sdk-go-v2/credentials v1.16.13 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.26.6 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
35 changes: 33 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,38 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.50.6 h1:FaXvNwHG3Ri1paUEW16Ahk9zLVqSAdqa1M3phjZR35Q=
github.com/aws/aws-sdk-go v1.50.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk=
github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
github.com/aws/aws-sdk-go-v2/config v1.26.2 h1:+RWLEIWQIGgrz2pBPAUoGgNGs1TOyF4Hml7hCnYj2jc=
github.com/aws/aws-sdk-go-v2/config v1.26.2/go.mod h1:l6xqvUxt0Oj7PI/SUXYLNyZ9T/yBPn3YTQcJLLOdtR8=
github.com/aws/aws-sdk-go-v2/credentials v1.16.13 h1:WLABQ4Cp4vXtXfOWOS3MEZKr6AAYUpMczLhgKtAjQ/8=
github.com/aws/aws-sdk-go-v2/credentials v1.16.13/go.mod h1:Qg6x82FXwW0sJHzYruxGiuApNo31UEtJvXVSZAXeWiw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.142.0 h1:VrFC1uEZjX4ghkm/et8ATVGb1mT75Iv8aPKPjUE+F8A=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.142.0/go.mod h1:qjhtI9zjpUHRc6khtrIM9fb48+ii6+UikL3/b+MKYn0=
github.com/aws/aws-sdk-go-v2/service/ecs v1.35.6 h1:Sc2mLjyA1R8z2l705AN7Wr7QOlnUxVnGPJeDIVyUSrs=
github.com/aws/aws-sdk-go-v2/service/ecs v1.35.6/go.mod h1:LzHcyOEvaLjbc5e+fP/KmPWBr+h/Ef+EHvnf1Pzo368=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0=
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.6 h1:EZw+TRx/4qlfp6VJ0P1sx04Txd9yGNK+NiO1upaXmh4=
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.6/go.mod h1:uXndCJoDO9gpuK24rNWVCnrGNUydKFEAYAZ7UU9S0rQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.6 h1:HJeiuZ2fldpd0WqngyMR6KW7ofkXNLyOaHwEIGm39Cs=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.6/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
Expand Down Expand Up @@ -68,6 +98,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
Expand Down
122 changes: 68 additions & 54 deletions internal/app.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,58 @@
package app

import (
"context"
"errors"
"fmt"
"os/exec"
"sort"
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/aws/aws-sdk-go/service/ecs/ecsiface"
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/spf13/viper"
)

type EC2Client interface {
DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
}

type ECSClient interface {
ListClusters(ctx context.Context, params *ecs.ListClustersInput, optFns ...func(*ecs.Options)) (*ecs.ListClustersOutput, error)
ListServices(ctx context.Context, params *ecs.ListServicesInput, optFns ...func(*ecs.Options)) (*ecs.ListServicesOutput, error)
ListTasks(ctx context.Context, params *ecs.ListTasksInput, optFns ...func(*ecs.Options)) (*ecs.ListTasksOutput, error)
DescribeTasks(ctx context.Context, params *ecs.DescribeTasksInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error)
DescribeTaskDefinition(ctx context.Context, params *ecs.DescribeTaskDefinitionInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTaskDefinitionOutput, error)
DescribeContainerInstances(ctx context.Context, params *ecs.DescribeContainerInstancesInput, optFns ...func(*ecs.Options)) (*ecs.DescribeContainerInstancesOutput, error)
ExecuteCommand(ctx context.Context, params *ecs.ExecuteCommandInput, optFns ...func(*ecs.Options)) (*ecs.ExecuteCommandOutput, error)
}

// App is a struct that contains information about our command state
type App struct {
input chan string
err chan error
exit chan error
ecsClient ecsiface.ECSAPI
ssmClient ssmiface.SSMAPI
region string
endpoint string
cluster string
service string
task *ecs.Task
tasks map[string]*ecs.Task
container *ecs.Container
containers []*ecs.Container
input chan string
err chan error
exit chan error
client ECSClient
region string
endpoint string
cluster string
service string
task *ecsTypes.Task
tasks map[string]*ecsTypes.Task
container *ecsTypes.Container
}

// CreateApp initialises a new App struct with the required initial values
func CreateApp() *App {
ecsClient := createEcsClient()
ssmClient := createSSMClient()
client := createEcsClient()
e := &App{
input: make(chan string, 1),
err: make(chan error, 1),
exit: make(chan error, 1),
ecsClient: ecsClient,
ssmClient: ssmClient,
region: ecsClient.SigningRegion,
endpoint: ecsClient.Endpoint,
input: make(chan string, 1),
err: make(chan error, 1),
exit: make(chan error, 1),
client: client,
region: client.Options().Region,
}

return e
Expand Down Expand Up @@ -99,7 +109,7 @@ func (e *App) Start() error {

// Lists available clusters and prompts the user to select one
func (e *App) getCluster() {
var clusters []*string
var clusters []string
var nextToken *string

if cluster := viper.GetString("cluster"); cluster != "" {
Expand All @@ -116,7 +126,11 @@ func (e *App) getCluster() {
return
}

list, err := e.ecsClient.ListClusters(&ecs.ListClustersInput{
if e.client == nil {

panic("oh no")
}
list, err := e.client.ListClusters(context.TODO(), &ecs.ListClustersInput{
MaxResults: awsMaxResults,
})
if err != nil {
Expand All @@ -128,7 +142,7 @@ func (e *App) getCluster() {

if nextToken != nil {
for {
list, err := e.ecsClient.ListClusters(&ecs.ListClustersInput{
list, err := e.client.ListClusters(context.TODO(), &ecs.ListClustersInput{
MaxResults: awsMaxResults,
NextToken: nextToken,
})
Expand All @@ -147,13 +161,13 @@ func (e *App) getCluster() {

// Sort the list of clusters alphabetically
sort.Slice(clusters, func(i, j int) bool {
return *clusters[i] < *clusters[j]
return clusters[i] < clusters[j]
})

if len(clusters) > 0 {
var clusterNames []string
for _, c := range clusters {
arnSplit := strings.Split(*c, "/")
arnSplit := strings.Split(c, "/")
name := arnSplit[len(arnSplit)-1]
clusterNames = append(clusterNames, name)
}
Expand All @@ -177,7 +191,7 @@ func (e *App) getCluster() {

// Lists available services and prompts the user to select one
func (e *App) getService() {
var services []*string
var services []string
var nextToken *string

cliArg := viper.GetString("service")
Expand All @@ -188,7 +202,7 @@ func (e *App) getService() {
return
}

list, err := e.ecsClient.ListServices(&ecs.ListServicesInput{
list, err := e.client.ListServices(context.TODO(), &ecs.ListServicesInput{
Cluster: aws.String(e.cluster),
MaxResults: awsMaxResults,
})
Expand All @@ -201,7 +215,7 @@ func (e *App) getService() {

if nextToken != nil {
for {
list, err := e.ecsClient.ListServices(&ecs.ListServicesInput{
list, err := e.client.ListServices(context.TODO(), &ecs.ListServicesInput{
Cluster: aws.String(e.cluster),
MaxResults: awsMaxResults,
NextToken: nextToken,
Expand All @@ -221,14 +235,14 @@ func (e *App) getService() {

// Sort the list of services alphabetically
sort.Slice(services, func(i, j int) bool {
return *services[i] < *services[j]
return services[i] < services[j]
})

if len(services) > 0 {
var serviceNames []string

for _, c := range services {
arnSplit := strings.Split(*c, "/")
for _, s := range services {
arnSplit := strings.Split(s, "/")
name := arnSplit[len(arnSplit)-1]
serviceNames = append(serviceNames, name)
}
Expand Down Expand Up @@ -259,29 +273,29 @@ func (e *App) getService() {

// Lists tasks in a cluster and prompts the user to select one
func (e *App) getTask() {
var taskArns []*string
var taskArns []string
var nextToken *string

var input *ecs.ListTasksInput

cliArg := viper.GetString("task")
if cliArg != "" {
describe, err := e.ecsClient.DescribeTasks(&ecs.DescribeTasksInput{
describe, err := e.client.DescribeTasks(context.TODO(), &ecs.DescribeTasksInput{
Cluster: aws.String(e.cluster),
Tasks: []*string{aws.String(cliArg)},
Tasks: []string{*aws.String(cliArg)},
})
if err != nil {
e.err <- err
return
}
if len(describe.Tasks) > 0 {
e.task = describe.Tasks[0]
e.task = &describe.Tasks[0]
e.getContainerOS()
e.input <- "getContainer"
viper.Set("task", "") // Reset the cli arg so user can navigate
return
} else {
fmt.Printf(Red(fmt.Sprintf("\nTask with ID %s not found in cluster %s\n", cliArg, e.cluster)))
fmt.Println(Red(fmt.Sprintf("\nTask with ID %s not found in cluster %s\n", cliArg, e.cluster)))
e.input <- "getService"
return
}
Expand All @@ -302,7 +316,7 @@ func (e *App) getTask() {
}
}

list, err := e.ecsClient.ListTasks(input)
list, err := e.client.ListTasks(context.TODO(), input)
if err != nil {
e.err <- err
return
Expand All @@ -313,7 +327,7 @@ func (e *App) getTask() {

if nextToken != nil {
for {
list, err := e.ecsClient.ListTasks(&ecs.ListTasksInput{
list, err := e.client.ListTasks(context.TODO(), &ecs.ListTasksInput{
Cluster: aws.String(e.cluster),
MaxResults: awsMaxResults,
NextToken: nextToken,
Expand All @@ -331,9 +345,9 @@ func (e *App) getTask() {
}
}

e.tasks = make(map[string]*ecs.Task)
e.tasks = make(map[string]*ecsTypes.Task)
if len(taskArns) > 0 {
describe, err := e.ecsClient.DescribeTasks(&ecs.DescribeTasksInput{
describe, err := e.client.DescribeTasks(context.TODO(), &ecs.DescribeTasksInput{
Cluster: aws.String(e.cluster),
Tasks: taskArns,
})
Expand All @@ -344,7 +358,7 @@ func (e *App) getTask() {

for _, t := range describe.Tasks {
taskId := strings.Split(*t.TaskArn, "/")[2]
e.tasks[taskId] = t
e.tasks[taskId] = &t
}

selection, err := selectTask(e.tasks)
Expand All @@ -354,7 +368,7 @@ func (e *App) getTask() {
}

if *selection.TaskArn == backOpt {
e.task = nil
// e.task = nil
if e.service == "" {
e.input <- "getCluster"
return
Expand Down Expand Up @@ -387,7 +401,7 @@ func (e *App) getContainer() {
if cliArg != "" {
for _, c := range e.task.Containers {
if *c.Name == cliArg {
e.container = c
e.container = &c
e.input <- "execute"
return
}
Expand All @@ -396,7 +410,7 @@ func (e *App) getContainer() {
}

if len(e.task.Containers) > 1 {
selection, err := selectContainer(e.task.Containers)
selection, err := selectContainer(&e.task.Containers)
if err != nil {
e.err <- err
return
Expand All @@ -413,7 +427,7 @@ func (e *App) getContainer() {

} else {
// There is only one container in the task, return it
e.container = e.task.Containers[0]
e.container = &e.task.Containers[0]
e.input <- "execute"
return
}
Expand All @@ -422,8 +436,8 @@ func (e *App) getContainer() {
// Determines the OS family of the container instance the task is running on
func (e *App) getContainerOS() {
// Get associated task definition and determine OS family if EC2 launch-type
if *e.task.LaunchType == "EC2" {
family, err := getPlatformFamily(e.ecsClient, e.cluster, e.task)
if e.task.LaunchType == "EC2" {
family, err := getPlatformFamily(e.client, e.cluster, e.task)
if err != nil {
e.err <- err
return
Expand All @@ -432,7 +446,7 @@ func (e *App) getContainerOS() {
// then we refer to the container instance to determine the OS
if family == "" {
ec2Client := createEc2Client()
family, err = getContainerInstanceOS(e.ecsClient, ec2Client, e.cluster, *e.task.ContainerInstanceArn)
family, err = getContainerInstanceOS(e.client, ec2Client, e.cluster, *e.task.ContainerInstanceArn)
if err != nil {
e.err <- err
return
Expand Down
Loading

0 comments on commit 7260d05

Please sign in to comment.