Skip to content

Commit

Permalink
Allow recovery from a stream being deleted and created by a user
Browse files Browse the repository at this point in the history
  • Loading branch information
usamj committed Jun 9, 2022
1 parent dc0cc54 commit 107f11f
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 1 deletion.
7 changes: 6 additions & 1 deletion cloudwatch/cloudwatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,12 @@ func (output *OutputPlugin) putLogEvents(stream *logStream) error {
} else if awsErr.Code() == cloudwatchlogs.ErrCodeInvalidSequenceTokenException {
// sequence code is bad, grab the correct one and retry
parts := strings.Split(awsErr.Message(), " ")
stream.nextSequenceToken = &parts[len(parts)-1]
nextSequenceToken := &parts[len(parts)-1]
// If this is a new stream then the error will end like "The next expected sequenceToken is: null" and sequenceToken should be nil
if strings.HasPrefix(*nextSequenceToken, "null") {
nextSequenceToken = nil
}
stream.nextSequenceToken = nextSequenceToken

return output.putLogEvents(stream)
} else if awsErr.Code() == cloudwatchlogs.ErrCodeResourceNotFoundException {
Expand Down
40 changes: 40 additions & 0 deletions cloudwatch/cloudwatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,46 @@ func TestAddEventAndFlushDataInvalidSequenceTokenException(t *testing.T) {
output.Flush()
}

func TestAddEventAndFlushDataInvalidSequenceTokenNextNullException(t *testing.T) {
ctrl := gomock.NewController(t)
mockCloudWatch := mock_cloudwatch.NewMockLogsClient(ctrl)

gomock.InOrder(
mockCloudWatch.EXPECT().CreateLogStream(gomock.Any()).Do(func(input *cloudwatchlogs.CreateLogStreamInput) {
assert.Equal(t, aws.StringValue(input.LogGroupName), testLogGroup, "Expected log group name to match")
assert.Equal(t, aws.StringValue(input.LogStreamName), testLogStreamPrefix+testTag, "Expected log stream name to match")
}).Return(&cloudwatchlogs.CreateLogStreamOutput{}, nil),
mockCloudWatch.EXPECT().PutLogEvents(gomock.Any()).Do(func(input *cloudwatchlogs.PutLogEventsInput) {
assert.Equal(t, aws.StringValue(input.LogGroupName), testLogGroup, "Expected log group name to match")
assert.Equal(t, aws.StringValue(input.LogStreamName), testLogStreamPrefix+testTag, "Expected log stream name to match")
}).Return(nil, awserr.New(cloudwatchlogs.ErrCodeInvalidSequenceTokenException, "The given sequenceToken is invalid; The next expected sequenceToken is: null", fmt.Errorf("API Error"))),
mockCloudWatch.EXPECT().PutLogEvents(gomock.Any()).Do(func(input *cloudwatchlogs.PutLogEventsInput) {
assert.Equal(t, aws.StringValue(input.LogGroupName), testLogGroup, "Expected log group name to match")
assert.Equal(t, aws.StringValue(input.LogStreamName), testLogStreamPrefix+testTag, "Expected log stream name to match")
assert.Nil(t, input.SequenceToken, "Expected sequence token to be nil")
}).Return(&cloudwatchlogs.PutLogEventsOutput{
NextSequenceToken: aws.String("token"),
}, nil),
)

output := OutputPlugin{
logGroupName: testTemplate(testLogGroup),
logStreamPrefix: testLogStreamPrefix,
client: mockCloudWatch,
timer: setupTimeout(),
streams: make(map[string]*logStream),
groups: map[string]struct{}{testLogGroup: {}},
}

record := map[interface{}]interface{}{
"somekey": []byte("some value"),
}

retCode := output.AddEvent(&Event{TS: time.Now(), Tag: testTag, Record: record})
assert.Equal(t, retCode, fluentbit.FLB_OK, "Expected return code to FLB_OK")
output.Flush()
}

func TestAddEventAndDataResourceNotFoundExceptionWithNoLogGroup(t *testing.T) {
ctrl := gomock.NewController(t)
mockCloudWatch := mock_cloudwatch.NewMockLogsClient(ctrl)
Expand Down

0 comments on commit 107f11f

Please sign in to comment.