Skip to content

Commit 8eef991

Browse files
committed
add compare-amis.go and updates to quotas.go for finding and resolving issues
1 parent 542f649 commit 8eef991

File tree

4 files changed

+237
-6
lines changed

4 files changed

+237
-6
lines changed

mangle/compare-amis.go

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"sort"
7+
"sync"
8+
9+
"github.com/aws/aws-sdk-go/aws"
10+
"github.com/aws/aws-sdk-go/aws/session"
11+
"github.com/aws/aws-sdk-go/service/ec2"
12+
)
13+
14+
type AMIInfo struct {
15+
ID string
16+
Name string
17+
}
18+
19+
type RegionComparison struct {
20+
Region string
21+
TotalAMIs int
22+
MissingAMIs []string
23+
MissingCount int
24+
}
25+
26+
func getPublicAMIs(sess *session.Session, region string) ([]AMIInfo, error) {
27+
ec2Svc := ec2.New(sess, &aws.Config{Region: aws.String(region)})
28+
29+
input := &ec2.DescribeImagesInput{
30+
Owners: []*string{aws.String("self")},
31+
Filters: []*ec2.Filter{
32+
{
33+
Name: aws.String("is-public"),
34+
Values: []*string{aws.String("true")},
35+
},
36+
},
37+
}
38+
39+
output, err := ec2Svc.DescribeImages(input)
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
var amis []AMIInfo
45+
for _, image := range output.Images {
46+
amis = append(amis, AMIInfo{
47+
ID: aws.StringValue(image.ImageId),
48+
Name: aws.StringValue(image.Name),
49+
})
50+
}
51+
52+
return amis, nil
53+
}
54+
55+
func compareRegions(sess *session.Session, sourceRegion string, regions []*string) []RegionComparison {
56+
// Get AMIs from source region (us-east-1)
57+
fmt.Printf("Fetching AMIs from source region: %s...\n", sourceRegion)
58+
sourceAMIs, err := getPublicAMIs(sess, sourceRegion)
59+
if err != nil {
60+
log.Fatalf("Error getting AMIs from %s: %v", sourceRegion, err)
61+
}
62+
63+
// Create a map of AMI names for quick lookup
64+
sourceAMINames := make(map[string]bool)
65+
for _, ami := range sourceAMIs {
66+
sourceAMINames[ami.Name] = true
67+
}
68+
69+
fmt.Printf("Source region %s has %d public AMIs\n\n", sourceRegion, len(sourceAMIs))
70+
71+
// Compare each region
72+
var comparisons []RegionComparison
73+
var mu sync.Mutex
74+
var wg sync.WaitGroup
75+
76+
for _, region := range regions {
77+
regionName := aws.StringValue(region)
78+
if regionName == sourceRegion {
79+
continue // Skip source region
80+
}
81+
82+
wg.Add(1)
83+
go func(regionName string) {
84+
defer wg.Done()
85+
86+
regionAMIs, err := getPublicAMIs(sess, regionName)
87+
if err != nil {
88+
log.Printf("Error getting AMIs from %s: %v", regionName, err)
89+
return
90+
}
91+
92+
// Create map of region AMI names
93+
regionAMINames := make(map[string]bool)
94+
for _, ami := range regionAMIs {
95+
regionAMINames[ami.Name] = true
96+
}
97+
98+
// Find missing AMIs
99+
var missing []string
100+
for name := range sourceAMINames {
101+
if !regionAMINames[name] {
102+
missing = append(missing, name)
103+
}
104+
}
105+
106+
sort.Strings(missing)
107+
108+
mu.Lock()
109+
comparisons = append(comparisons, RegionComparison{
110+
Region: regionName,
111+
TotalAMIs: len(regionAMIs),
112+
MissingAMIs: missing,
113+
MissingCount: len(missing),
114+
})
115+
mu.Unlock()
116+
}(regionName)
117+
}
118+
119+
wg.Wait()
120+
121+
// Sort comparisons by missing count (descending)
122+
sort.Slice(comparisons, func(i, j int) bool {
123+
return comparisons[i].MissingCount > comparisons[j].MissingCount
124+
})
125+
126+
return comparisons
127+
}
128+
129+
func getRegions(ec2Svc *ec2.EC2) ([]*string, error) {
130+
input := &ec2.DescribeRegionsInput{}
131+
132+
output, err := ec2Svc.DescribeRegions(input)
133+
if err != nil {
134+
return nil, err
135+
}
136+
137+
var regions []*string
138+
for _, region := range output.Regions {
139+
regions = append(regions, region.RegionName)
140+
}
141+
142+
return regions, nil
143+
}
144+
145+
func main() {
146+
// Create session
147+
sess := session.Must(session.NewSessionWithOptions(session.Options{
148+
SharedConfigState: session.SharedConfigEnable,
149+
}))
150+
151+
// Create EC2 client
152+
ec2Svc := ec2.New(sess, &aws.Config{Region: aws.String("us-east-1")})
153+
154+
// Get all regions
155+
regions, err := getRegions(ec2Svc)
156+
if err != nil {
157+
log.Println("Error getting regions:", err)
158+
return
159+
}
160+
161+
// Compare all regions against us-east-1
162+
sourceRegion := "us-east-1"
163+
comparisons := compareRegions(sess, sourceRegion, regions)
164+
165+
// Print results
166+
fmt.Println("=== AMI Comparison Results ===")
167+
fmt.Printf("Source: %s\n\n", sourceRegion)
168+
169+
for _, comp := range comparisons {
170+
if comp.MissingCount > 0 {
171+
fmt.Printf("%s: %d AMIs (missing %d)\n", comp.Region, comp.TotalAMIs, comp.MissingCount)
172+
for _, amiName := range comp.MissingAMIs {
173+
fmt.Printf(" - %s\n", amiName)
174+
}
175+
fmt.Println()
176+
} else {
177+
fmt.Printf("%s: %d AMIs (complete ✓)\n", comp.Region, comp.TotalAMIs)
178+
}
179+
}
180+
}

mangle/go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module git.resf.org/sig_core/toolkit/mangle
2+
3+
go 1.23.9
4+
5+
require github.com/aws/aws-sdk-go v1.55.7
6+
7+
require github.com/jmespath/go-jmespath v0.4.0 // indirect

mangle/go.sum

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
2+
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
3+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
4+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
6+
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
7+
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
8+
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
9+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
10+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
12+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
13+
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
14+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

mangle/quotas.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,36 @@ func getRegions(ec2Svc *ec2.EC2) ([]*string, error) {
5858
return regions, nil
5959
}
6060

61+
func countPublicAMIs(sess *session.Session, region string) (int, error) {
62+
ec2Svc := ec2.New(sess, &aws.Config{Region: aws.String(region)})
63+
64+
input := &ec2.DescribeImagesInput{
65+
Owners: []*string{aws.String("self")},
66+
Filters: []*ec2.Filter{
67+
{
68+
Name: aws.String("is-public"),
69+
Values: []*string{aws.String("true")},
70+
},
71+
},
72+
}
73+
74+
output, err := ec2Svc.DescribeImages(input)
75+
if err != nil {
76+
return 0, err
77+
}
78+
79+
return len(output.Images), nil
80+
}
81+
6182
type QuotaInfo struct {
6283
CurrentQuota float64
6384
DesiredValue float64
6485
Status string
6586
CaseId string
87+
UsageCount int
6688
}
6789

68-
func getQuotaInfo(sqSvc *servicequotas.ServiceQuotas, quotaCode string, region string) *QuotaInfo {
90+
func getQuotaInfo(sess *session.Session, sqSvc *servicequotas.ServiceQuotas, quotaCode string, region string) *QuotaInfo {
6991
input := &servicequotas.GetServiceQuotaInput{
7092
ServiceCode: aws.String("ec2"),
7193
QuotaCode: aws.String(quotaCode),
@@ -110,11 +132,19 @@ func getQuotaInfo(sqSvc *servicequotas.ServiceQuotas, quotaCode string, region s
110132
caseId = *lastQuota.CaseId
111133
}
112134
}
113-
return &QuotaInfo{currentValue, desiredValue, status, caseId}
135+
136+
// Count public AMIs in use
137+
usageCount, err := countPublicAMIs(sess, region)
138+
if err != nil {
139+
log.Printf("Error counting public AMIs in %s: %v", region, err)
140+
usageCount = -1 // Use -1 to indicate error
141+
}
142+
143+
return &QuotaInfo{currentValue, desiredValue, status, caseId, usageCount}
114144
}
115145

116146
func listQuotas(sess *session.Session, quotaCode string, regions []*string) {
117-
fmt.Println("Region\tQuota\tDesired\tStatus\tCaseId")
147+
fmt.Println("Region\tUsage\tQuota\tDesired\tStatus\tCaseId")
118148

119149
var wg sync.WaitGroup
120150
wg.Add(len(regions))
@@ -123,9 +153,9 @@ func listQuotas(sess *session.Session, quotaCode string, regions []*string) {
123153
go func(region string) {
124154
defer wg.Done()
125155
regionSqSvc := servicequotas.New(sess, &aws.Config{Region: aws.String(region)})
126-
quotaInfo := getQuotaInfo(regionSqSvc, quotaCode, region)
156+
quotaInfo := getQuotaInfo(sess, regionSqSvc, quotaCode, region)
127157
if quotaInfo != nil {
128-
fmt.Printf("%s\t%.0f\t%.0f\t%s\t%s\n", region, quotaInfo.CurrentQuota, quotaInfo.DesiredValue, quotaInfo.Status, quotaInfo.CaseId)
158+
fmt.Printf("%s\t%d\t%.0f\t%.0f\t%s\t%s\n", region, quotaInfo.UsageCount, quotaInfo.CurrentQuota, quotaInfo.DesiredValue, quotaInfo.Status, quotaInfo.CaseId)
129159
}
130160
}(aws.StringValue(region))
131161
}
@@ -140,7 +170,7 @@ func requestQuotaIncrease(sess *session.Session, quotaCode string, regions []*st
140170
go func(region string) {
141171
defer wg.Done()
142172
regionSqSvc := servicequotas.New(sess, &aws.Config{Region: aws.String(region)})
143-
quotaInfo := getQuotaInfo(regionSqSvc, quotaCode, region)
173+
quotaInfo := getQuotaInfo(sess, regionSqSvc, quotaCode, region)
144174
if quotaInfo.CurrentQuota >= quota {
145175
fmt.Printf("Quota for Public AMIs in region %s is already set to %.0f, skipping request.\n", region, quotaInfo.CurrentQuota)
146176
} else {

0 commit comments

Comments
 (0)