Skip to content

Commit f4be9ed

Browse files
committed
chore: migrate to AWS SDK v2, likely fixes #51
1 parent a6b1843 commit f4be9ed

File tree

8 files changed

+179
-158
lines changed

8 files changed

+179
-158
lines changed

blade/blade.go

Lines changed: 80 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package blade
22

33
import (
4+
"context"
45
"encoding/json"
56
"errors"
67
"fmt"
@@ -9,10 +10,9 @@ import (
910

1011
"github.com/TylerBrock/colorjson"
1112
"github.com/TylerBrock/saw/config"
12-
"github.com/aws/aws-sdk-go/aws"
13-
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
14-
"github.com/aws/aws-sdk-go/aws/session"
15-
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
13+
awsconfig "github.com/aws/aws-sdk-go-v2/config"
14+
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
15+
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
1616
"github.com/fatih/color"
1717
)
1818

@@ -21,70 +21,62 @@ type Blade struct {
2121
config *config.Configuration
2222
aws *config.AWSConfiguration
2323
output *config.OutputConfiguration
24-
cwl *cloudwatchlogs.CloudWatchLogs
24+
cwl *cloudwatchlogs.Client
2525
}
2626

2727
// NewBlade creates a new Blade with CloudWatchLogs instance from provided config
2828
func NewBlade(
29+
ctx context.Context,
2930
config *config.Configuration,
3031
awsConfig *config.AWSConfiguration,
3132
outputConfig *config.OutputConfiguration,
32-
) *Blade {
33+
) (*Blade, error) {
3334
blade := Blade{}
34-
awsCfg := aws.Config{}
35-
36-
if awsConfig.Region != "" {
37-
awsCfg.Region = &awsConfig.Region
38-
}
39-
40-
awsSessionOpts := session.Options{
41-
Config: awsCfg,
42-
AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
43-
SharedConfigState: session.SharedConfigEnable,
44-
}
4535

36+
var opts []func(*awsconfig.LoadOptions) error
4637
if awsConfig.Profile != "" {
47-
awsSessionOpts.Profile = awsConfig.Profile
38+
opts = append(opts, awsconfig.WithSharedConfigProfile(awsConfig.Profile))
4839
}
40+
if awsConfig.Region != "" {
41+
opts = append(opts, awsconfig.WithRegion(awsConfig.Region))
42+
}
43+
awsCfg, err := awsconfig.LoadDefaultConfig(ctx, opts...)
4944

50-
sess := session.Must(session.NewSessionWithOptions(awsSessionOpts))
51-
52-
blade.cwl = cloudwatchlogs.New(sess)
45+
blade.cwl = cloudwatchlogs.NewFromConfig(awsCfg)
5346
blade.config = config
5447
blade.output = outputConfig
5548

56-
return &blade
49+
return &blade, err
5750
}
5851

5952
// GetLogGroups gets the log groups from AWS given the blade configuration
60-
func (b *Blade) GetLogGroups() []*cloudwatchlogs.LogGroup {
53+
func (b *Blade) GetLogGroups(ctx context.Context) (groups []types.LogGroup, err error) {
6154
input := b.config.DescribeLogGroupsInput()
62-
groups := make([]*cloudwatchlogs.LogGroup, 0)
63-
b.cwl.DescribeLogGroupsPages(input, func(
64-
out *cloudwatchlogs.DescribeLogGroupsOutput,
65-
lastPage bool,
66-
) bool {
67-
for _, group := range out.LogGroups {
68-
if b.config.Fuzzy && !groupNameMatches(*group.LogGroupName, b.config.Group) {
69-
continue
70-
}
71-
groups = append(groups, group)
55+
logGroupsPaginator := cloudwatchlogs.NewDescribeLogGroupsPaginator(b.cwl, input)
56+
var page *cloudwatchlogs.DescribeLogGroupsOutput
57+
for logGroupsPaginator.HasMorePages() {
58+
page, err = logGroupsPaginator.NextPage(ctx)
59+
if err != nil {
60+
return
7261
}
73-
return !lastPage
74-
})
75-
return groups
62+
groups = append(groups, page.LogGroups...)
63+
}
64+
return
7665
}
7766

7867
func groupNameMatches(s, substr string) bool {
7968
return strings.Contains(s, substr)
8069
}
8170

82-
func (b *Blade) ResolveFuzzyGroupName() (err error) {
71+
func (b *Blade) ResolveFuzzyGroupName(ctx context.Context) (err error) {
8372
if !b.config.Fuzzy {
8473
return
8574
}
8675
b.config.Fuzzy = false
87-
groups := b.GetLogGroups()
76+
groups, err := b.GetLogGroups(ctx)
77+
if err != nil {
78+
return
79+
}
8880
if len(groups) == 0 {
8981
return errors.New("no log groups found")
9082
}
@@ -99,15 +91,15 @@ func (b *Blade) ResolveFuzzyGroupName() (err error) {
9991
return
10092
}
10193

102-
func getGroupNames(groups []*cloudwatchlogs.LogGroup) (op []string) {
94+
func getGroupNames(groups []types.LogGroup) (op []string) {
10395
op = make([]string, len(groups))
10496
for i := 0; i < len(groups); i++ {
10597
op[i] = *groups[i].LogGroupName
10698
}
10799
return
108100
}
109101

110-
func filterGroupNames(groups []*cloudwatchlogs.LogGroup, group string) (op []string) {
102+
func filterGroupNames(groups []types.LogGroup, group string) (op []string) {
111103
for i := 0; i < len(groups); i++ {
112104
if groupNameMatches(*groups[i].LogGroupName, group) {
113105
op = append(op, *groups[i].LogGroupName)
@@ -117,46 +109,50 @@ func filterGroupNames(groups []*cloudwatchlogs.LogGroup, group string) (op []str
117109
}
118110

119111
// GetLogStreams gets the log streams from AWS given the blade configuration
120-
func (b *Blade) GetLogStreams() (streams []*cloudwatchlogs.LogStream, err error) {
121-
if err := b.ResolveFuzzyGroupName(); err != nil {
112+
func (b *Blade) GetLogStreams(ctx context.Context) (streams []types.LogStream, err error) {
113+
if err := b.ResolveFuzzyGroupName(ctx); err != nil {
122114
return nil, err
123115
}
124116
input := b.config.DescribeLogStreamsInput()
125-
b.cwl.DescribeLogStreamsPages(input, func(
126-
out *cloudwatchlogs.DescribeLogStreamsOutput,
127-
lastPage bool,
128-
) bool {
129-
for _, stream := range out.LogStreams {
130-
streams = append(streams, stream)
117+
logStreamsPaginator := cloudwatchlogs.NewDescribeLogStreamsPaginator(b.cwl, input)
118+
var page *cloudwatchlogs.DescribeLogStreamsOutput
119+
for logStreamsPaginator.HasMorePages() {
120+
page, err = logStreamsPaginator.NextPage(ctx)
121+
if err != nil {
122+
return
131123
}
132-
return !lastPage
133-
})
134-
return streams, err
124+
streams = append(streams, page.LogStreams...)
125+
}
126+
return
135127
}
136128

137129
// GetEvents gets events from AWS given the blade configuration
138-
func (b *Blade) GetEvents() (err error) {
139-
if err := b.ResolveFuzzyGroupName(); err != nil {
130+
func (b *Blade) GetEvents(ctx context.Context) (err error) {
131+
if err := b.ResolveFuzzyGroupName(ctx); err != nil {
140132
return err
141133
}
142134
formatter := b.output.Formatter()
143135
input := b.config.FilterLogEventsInput()
144-
145-
handlePage := func(page *cloudwatchlogs.FilterLogEventsOutput, lastPage bool) bool {
136+
logEventsPaginator := cloudwatchlogs.NewFilterLogEventsPaginator(b.cwl, input)
137+
var page *cloudwatchlogs.FilterLogEventsOutput
138+
for logEventsPaginator.HasMorePages() {
139+
page, err = logEventsPaginator.NextPage(ctx)
140+
if err != nil {
141+
return
142+
}
146143
for _, event := range page.Events {
147144
if b.output.Pretty {
148145
fmt.Println(strings.TrimRight(formatEvent(formatter, event), "\n"))
149146
} else {
150147
fmt.Println(strings.TrimRight(*event.Message, "\n"))
151148
}
152149
}
153-
return !lastPage
154150
}
155-
return b.cwl.FilterLogEventsPages(input, handlePage)
151+
return
156152
}
157153

158154
// StreamEvents continuously prints log events to the console
159-
func (b *Blade) StreamEvents() (err error) {
155+
func (b *Blade) StreamEvents(ctx context.Context) (err error) {
160156
var lastSeenTime *int64
161157
var seenEventIDs map[string]bool
162158
formatter := b.output.Formatter()
@@ -177,46 +173,45 @@ func (b *Blade) StreamEvents() (err error) {
177173
}
178174
}
179175

180-
handlePage := func(page *cloudwatchlogs.FilterLogEventsOutput, lastPage bool) bool {
181-
for _, event := range page.Events {
182-
updateLastSeenTime(event.Timestamp)
183-
if _, seen := seenEventIDs[*event.EventId]; !seen {
184-
var message string
185-
if b.output.Raw {
186-
message = *event.Message
187-
} else {
188-
message = formatEvent(formatter, event)
176+
for {
177+
logEventsPaginator := cloudwatchlogs.NewFilterLogEventsPaginator(b.cwl, input)
178+
var page *cloudwatchlogs.FilterLogEventsOutput
179+
for logEventsPaginator.HasMorePages() {
180+
page, err = logEventsPaginator.NextPage(ctx)
181+
if err != nil {
182+
return
183+
}
184+
for _, event := range page.Events {
185+
updateLastSeenTime(event.Timestamp)
186+
if _, seen := seenEventIDs[*event.EventId]; !seen {
187+
var message string
188+
if b.output.Raw {
189+
message = *event.Message
190+
} else {
191+
message = formatEvent(formatter, event)
192+
}
193+
message = strings.TrimRight(message, "\n")
194+
fmt.Println(message)
195+
addSeenEventIDs(event.EventId)
189196
}
190-
message = strings.TrimRight(message, "\n")
191-
fmt.Println(message)
192-
addSeenEventIDs(event.EventId)
193197
}
194198
}
195-
return !lastPage
196-
}
197-
198-
for {
199-
err = b.cwl.FilterLogEventsPages(input, handlePage)
200-
if err != nil {
201-
return
202-
}
203199
if lastSeenTime != nil {
204-
input.SetStartTime(*lastSeenTime)
200+
input.StartTime = lastSeenTime
205201
}
206202
time.Sleep(1 * time.Second)
207203
}
208204
}
209205

210206
// formatEvent returns a CloudWatch log event as a formatted string using the provided formatter
211-
func formatEvent(formatter *colorjson.Formatter, event *cloudwatchlogs.FilteredLogEvent) string {
207+
func formatEvent(formatter *colorjson.Formatter, event types.FilteredLogEvent) string {
212208
red := color.New(color.FgRed).SprintFunc()
213209
white := color.New(color.FgWhite).SprintFunc()
214210

215-
str := aws.StringValue(event.Message)
216-
bytes := []byte(str)
217-
date := aws.MillisecondsTimeValue(event.Timestamp)
218-
dateStr := date.Format(time.RFC3339)
219-
streamStr := aws.StringValue(event.LogStreamName)
211+
str := *event.Message
212+
bytes := []byte(*event.Message)
213+
dateStr := time.UnixMilli(*event.Timestamp).Format(time.RFC3339)
214+
streamStr := *event.LogStreamName
220215
jl := map[string]interface{}{}
221216

222217
if err := json.Unmarshal(bytes, &jl); err != nil {

cmd/get.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ var getCommand = &cobra.Command{
2525
},
2626
RunE: func(cmd *cobra.Command, args []string) (err error) {
2727
getConfig.Group = args[0]
28-
b := blade.NewBlade(&getConfig, &awsConfig, &getOutputConfig)
28+
b, err := blade.NewBlade(cmd.Context(), &getConfig, &awsConfig, &getOutputConfig)
29+
if err != nil {
30+
return
31+
}
2932
if getConfig.Prefix != "" {
30-
streams, err := b.GetLogStreams()
33+
streams, err := b.GetLogStreams(cmd.Context())
3134
if err != nil {
32-
return err
35+
return fmt.Errorf("failed to get log streams: %w", err)
3336
}
3437
if len(streams) == 0 {
3538
fmt.Printf("No streams found in %s with prefix %s\n", getConfig.Group, getConfig.Prefix)
@@ -38,7 +41,7 @@ var getCommand = &cobra.Command{
3841
}
3942
getConfig.Streams = streams
4043
}
41-
return b.GetEvents()
44+
return b.GetEvents(cmd.Context())
4245
},
4346
}
4447

cmd/groups.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,25 @@ var groupsCommand = &cobra.Command{
1616
Use: "groups",
1717
Short: "List log groups",
1818
Long: "",
19-
RunE: func(cmd *cobra.Command, args []string) error {
19+
RunE: func(cmd *cobra.Command, args []string) (err error) {
2020
if groupsConfig.Fuzzy {
2121
if len(args) < 1 {
2222
return errors.New("listing groups with fuzzy search requires log group argument")
2323
}
2424
groupsConfig.Group = args[0]
2525
}
26-
b := blade.NewBlade(&groupsConfig, &awsConfig, nil)
27-
logGroups := b.GetLogGroups()
26+
b, err := blade.NewBlade(cmd.Context(), &groupsConfig, &awsConfig, nil)
27+
if err != nil {
28+
return
29+
}
30+
logGroups, err := b.GetLogGroups(cmd.Context())
31+
if err != nil {
32+
return fmt.Errorf("failed to get log groups: %w", err)
33+
}
2834
for _, group := range logGroups {
2935
fmt.Println(*group.LogGroupName)
3036
}
31-
return nil
37+
return
3238
},
3339
}
3440

cmd/streams.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ var streamsCommand = &cobra.Command{
2323
},
2424
RunE: func(cmd *cobra.Command, args []string) (err error) {
2525
streamsConfig.Group = args[0]
26-
b := blade.NewBlade(&streamsConfig, &awsConfig, nil)
26+
b, err := blade.NewBlade(cmd.Context(), &streamsConfig, &awsConfig, nil)
27+
if err != nil {
28+
return
29+
}
2730

28-
logStreams, err := b.GetLogStreams()
31+
logStreams, err := b.GetLogStreams(cmd.Context())
2932
if err != nil {
30-
return err
33+
return fmt.Errorf("failed to get log streams: %w", err)
3134
}
3235
for _, stream := range logStreams {
3336
fmt.Println(*stream.LogStreamName)

cmd/watch.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ var watchCommand = &cobra.Command{
2626
},
2727
RunE: func(cmd *cobra.Command, args []string) (err error) {
2828
watchConfig.Group = args[0]
29-
b := blade.NewBlade(&watchConfig, &awsConfig, &watchOutputConfig)
29+
b, err := blade.NewBlade(cmd.Context(), &watchConfig, &awsConfig, &watchOutputConfig)
30+
if err != nil {
31+
return
32+
}
3033
if watchConfig.Prefix != "" {
31-
streams, err := b.GetLogStreams()
34+
streams, err := b.GetLogStreams(cmd.Context())
3235
if err != nil {
33-
return err
36+
return fmt.Errorf("failed to get log streams: %w", err)
3437
}
3538
if len(streams) == 0 {
3639
fmt.Printf("No streams found in %s with prefix %s\n", watchConfig.Group, watchConfig.Prefix)
@@ -39,8 +42,7 @@ var watchCommand = &cobra.Command{
3942
}
4043
watchConfig.Streams = streams
4144
}
42-
b.StreamEvents()
43-
return
45+
return b.StreamEvents(cmd.Context())
4446
},
4547
}
4648

0 commit comments

Comments
 (0)