Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6

# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
# - run: git fetch --force --tags
- name: Setup GO
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Release
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version-file: go.mod

Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ go 1.26
require (
github.com/BurntSushi/toml v1.4.0
github.com/atotto/clipboard v0.1.4
github.com/aws/aws-sdk-go-v2 v1.41.7
github.com/aws/aws-sdk-go-v2 v1.42.0
github.com/aws/aws-sdk-go-v2/config v1.32.17
github.com/aws/aws-sdk-go-v2/service/account v1.31.0
github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.41.13
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.55.2
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.71.1
github.com/aws/aws-sdk-go-v2/service/ecs v1.74.0
github.com/aws/aws-sdk-go-v2/service/ecs v1.83.0
github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3
github.com/gdamore/tcell/v2 v2.13.9
github.com/keidarcy/aws-regions/v3 v3.0.0-20260309105808-fbc1ba25ea42
Expand All @@ -26,16 +26,16 @@ require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.16 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 // indirect
github.com/aws/smithy-go v1.25.1 // indirect
github.com/aws/smithy-go v1.27.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8=
github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc=
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 h1:gx1AwW1Iyk9Z9dD9F4akX5gnN3QZwUB20GGKH/I+Rho=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10/go.mod h1:qqY157uZoqm5OXq/amuaBJyC9hgBCBQnsaWnPe905GY=
github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU=
Expand All @@ -12,10 +12,10 @@ github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60Qb
github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE=
github.com/aws/aws-sdk-go-v2/service/account v1.31.0 h1:AdQH4Jtf/2gu8h1lyUpwJmhtW/3ppyj4emi/PThI9Zw=
Expand All @@ -26,8 +26,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.55.2 h1:mleWBVIxwceEzyItUVoqM
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.55.2/go.mod h1:cMApt548kNgu87UsBTNWVv+fpzjbUTFRSFjD1688SBs=
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.71.1 h1:p0A8HO2B++3LfOTRxQScOPc3QhFWgyAXQQ6W92RT7Yk=
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.71.1/go.mod h1:MLJu3PUd8fp5Qvj4CiLvyY5H8y7kxHKlTp060Wsd+Vc=
github.com/aws/aws-sdk-go-v2/service/ecs v1.74.0 h1:YS5TXaEvzDb+sV+wdQFUtuCAk0GeFR9Ai6HFdxpz6q8=
github.com/aws/aws-sdk-go-v2/service/ecs v1.74.0/go.mod h1:10kBgdaNJz0FO/+JWDUH+0rtSjkn5yafgavDDmmhFzs=
github.com/aws/aws-sdk-go-v2/service/ecs v1.83.0 h1:LQKIHuVHqdbU9LUt5c2G9f+CcQAzolxQmAch3RTORMc=
github.com/aws/aws-sdk-go-v2/service/ecs v1.83.0/go.mod h1:0vahPCh3slyORHbSuAP8YDyJKLEUQAMX7+bzYGxEnVI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw=
Expand All @@ -42,8 +42,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg=
github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk=
github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio=
github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI=
github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8=
github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
112 changes: 112 additions & 0 deletions internal/api/daemon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package api

import (
"context"
"log/slog"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs"
"github.com/aws/aws-sdk-go-v2/service/ecs/types"
)

// Equivalent to
// aws ecs list-daemons --cluster ${cluster}
func (store *Store) ListDaemons(clusterArn *string) ([]types.DaemonSummary, error) {
limit := int32(100)
params := &ecs.ListDaemonsInput{
ClusterArn: clusterArn,
MaxResults: &limit,
}

results := []types.DaemonSummary{}

for {
output, err := store.ecs.ListDaemons(context.Background(), params)
if err != nil {
slog.Warn("failed to run aws api to list daemons", "error", err)
if len(results) == 0 {
return nil, err
}
break
}

results = append(results, output.DaemonSummariesList...)

if output.NextToken != nil {
params.NextToken = output.NextToken
} else {
break
}
}

return results, nil
}

// Equivalent to
// aws ecs describe-daemon --daemon-arn ${daemonArn}
func (store *Store) DescribeDaemon(daemonArn *string) (*types.DaemonDetail, error) {
output, err := store.ecs.DescribeDaemon(context.Background(), &ecs.DescribeDaemonInput{
DaemonArn: daemonArn,
})
if err != nil {
slog.Warn("failed to run aws api to describe daemon", "error", err)
return nil, err
}
return output.Daemon, nil
}

// Equivalent to
// aws ecs list-daemon-task-definitions --family ${family}
// aws ecs describe-daemon-task-definition --daemon-task-definition ${arn}
func (store *Store) ListDaemonTaskDefinitions(family *string) ([]types.DaemonTaskDefinition, error) {
output, err := store.ecs.ListDaemonTaskDefinitions(context.Background(), &ecs.ListDaemonTaskDefinitionsInput{
Family: family,
MaxResults: aws.Int32(MaxTaskDefinitionRevision),
Sort: types.SortOrderDesc,
})
if err != nil {
slog.Warn("failed to run aws api to list daemon task definitions", "error", err)
return nil, err
}

results := []types.DaemonTaskDefinition{}
for _, summary := range output.DaemonTaskDefinitions {
if summary.Arn == nil {
continue
}
td, err := store.DescribeDaemonTaskDefinition(summary.Arn)
if err != nil {
slog.Warn("failed to describe daemon task definition", "arn", *summary.Arn, "error", err)
continue
}
results = append(results, *td)
}

return results, nil
}

// Equivalent to
// aws ecs describe-daemon-task-definition --daemon-task-definition ${arn}
func (store *Store) DescribeDaemonTaskDefinition(arn *string) (*types.DaemonTaskDefinition, error) {
output, err := store.ecs.DescribeDaemonTaskDefinition(context.Background(), &ecs.DescribeDaemonTaskDefinitionInput{
DaemonTaskDefinition: arn,
})
if err != nil {
slog.Warn("failed to run aws api to describe daemon task definition", "error", err)
return nil, err
}
return output.DaemonTaskDefinition, nil
}

// Equivalent to
// aws ecs describe-daemon-revisions --daemon-revision-arns ${arns}
func (store *Store) DescribeDaemonRevisions(arns []string) ([]types.DaemonRevision, error) {
output, err := store.ecs.DescribeDaemonRevisions(context.Background(), &ecs.DescribeDaemonRevisionsInput{
DaemonRevisionArns: arns,
})
if err != nil {
slog.Warn("failed to run aws api to describe daemon revisions", "error", err)
return nil, err
}
return output.DaemonRevisions, nil
}
38 changes: 38 additions & 0 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const (
serviceURLFmt = clusterFmt + serviceFmt + regionFmt
taskURLFmt = clusterFmt + serviceFmt + taskFmt + regionFmt
taskDefinitionURLFmt = "https://%s.console.aws.amazon.com/ecs/v2/task-definitions/%s/%s/containers?region=%s"
daemonURLFmt = clusterFmt + "/daemons/%s/health" + regionFmt
daemonTaskDefinitionFmt = "https://%s.console.aws.amazon.com/ecs/v2/daemon-task-definitions/%s/%s/containers?region=%s"
serviceDeploymentURLFmt = "https://%s.console.aws.amazon.com/ecs/v2/clusters/%s/services/%s/service-deployments/%s?region=%s"
)

Expand Down Expand Up @@ -96,6 +98,10 @@ func ShowGreenGrey(inputStr *string, greenStr string) string {
// TaskARN not contains service but need service name as second argument
func ArnToUrl(arn string, taskService string) string {
components := strings.Split(arn, ":")
if len(components) < 6 {
return ""
}

resources := components[len(components)-1]
names := strings.Split(resources, "/")
_, err := strconv.Atoi(resources)
Expand All @@ -105,6 +111,9 @@ func ArnToUrl(arn string, taskService string) string {
names = strings.Split(resources, "/")
names = append(names, components[len(components)-1])
}
if len(names) == 0 {
return ""
}

region := components[3]
clusterName := ""
Expand All @@ -113,25 +122,54 @@ func ArnToUrl(arn string, taskService string) string {

switch names[0] {
case "cluster":
if len(names) < 2 {
return ""
}
clusterName = names[1]
return fmt.Sprintf(clusterURLFmt, region, clusterName, region)
case "service":
if len(names) < 3 {
return ""
}
clusterName = names[1]
serviceName = names[2]
return fmt.Sprintf(serviceURLFmt, region, clusterName, serviceName, region)
case "service-deployment":
if len(names) < 4 {
return ""
}
clusterName = names[1]
serviceName = names[2]
deploymentId := names[3]
return fmt.Sprintf(serviceDeploymentURLFmt, region, clusterName, serviceName, deploymentId, region)
case "task", "container":
if len(names) < 3 {
return ""
}
clusterName = names[1]
taskName = names[2]
return fmt.Sprintf(taskURLFmt, region, clusterName, taskService, taskName, region)
case "task-definition":
if len(names) < 3 {
return ""
}
taskDefName := names[1]
revision := names[2]
return fmt.Sprintf(taskDefinitionURLFmt, region, taskDefName, revision, region)
case "daemon":
if len(names) < 3 {
return ""
}
clusterName = names[1]
daemonName := names[2]
return fmt.Sprintf(daemonURLFmt, region, clusterName, daemonName, region)
case "daemon-task-definition":
if len(names) < 3 {
return ""
}
taskDefName := names[1]
revision := names[2]
return fmt.Sprintf(daemonTaskDefinitionFmt, region, taskDefName, revision, region)
default:
return ""
}
Expand Down
48 changes: 48 additions & 0 deletions internal/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const (
serviceArnFmt = "arn:aws:ecs:%s:111111:service/%s/%s"
taskArnFmt = "arn:aws:ecs:%s:111111:task/%s/%s"
taskDefinitionArnFmt = "arn:aws:ecs:%s:111111:task-definition/%s:%s"
daemonArnFmt = "arn:aws:ecs:%s:111111:daemon/%s/%s"
daemonTdArnFmt = "arn:aws:ecs:%s:111111:daemon-task-definition/%s:%s"
)

func TestArnToURL(t *testing.T) {
Expand All @@ -25,6 +27,8 @@ func TestArnToURL(t *testing.T) {
taskService1 := "taskService1"
taskDef1 := "my-task-def"
revision1 := "1"
daemon1 := "daemon1"
daemonTaskDef1 := "my-daemon-task-def"
arn1 := fmt.Sprintf(clusterArnFmt, testRegion, cluster1)
url1 := fmt.Sprintf(clusterURLFmt, testRegion, cluster1, testRegion)
arn2 := fmt.Sprintf(serviceArnFmt, testRegion, cluster1, service1)
Expand All @@ -33,6 +37,10 @@ func TestArnToURL(t *testing.T) {
url3 := fmt.Sprintf(taskURLFmt, testRegion, cluster1, taskService1, task1, testRegion)
arn4 := fmt.Sprintf(taskDefinitionArnFmt, testRegion, taskDef1, revision1)
url4 := fmt.Sprintf(taskDefinitionURLFmt, testRegion, taskDef1, revision1, testRegion)
arn5 := fmt.Sprintf(daemonArnFmt, testRegion, cluster1, daemon1)
url5 := fmt.Sprintf(daemonURLFmt, testRegion, cluster1, daemon1, testRegion)
arn6 := fmt.Sprintf(daemonTdArnFmt, testRegion, daemonTaskDef1, revision1)
url6 := fmt.Sprintf(daemonTaskDefinitionFmt, testRegion, daemonTaskDef1, revision1, testRegion)

testCases := []struct {
name string
Expand Down Expand Up @@ -71,6 +79,46 @@ func TestArnToURL(t *testing.T) {
},
want: url4,
},
{
name: "daemon arn convert",
args: Args{
arn: arn5,
taskService: "",
},
want: url5,
},
{
name: "daemon task definition arn convert",
args: Args{
arn: arn6,
taskService: "",
},
want: url6,
},
{
name: "empty arn",
args: Args{
arn: "",
taskService: "",
},
want: "",
},
{
name: "malformed arn",
args: Args{
arn: "not-an-arn",
taskService: "",
},
want: "",
},
{
name: "incomplete cluster arn",
args: Args{
arn: "arn:aws:ecs:us-east-1:111111:cluster",
taskService: "",
},
want: "",
},
}

for _, tc := range testCases {
Expand Down
Loading