@@ -12,6 +12,7 @@ import (
1212 "testing"
1313 "time"
1414
15+ "github.com/aws/aws-sdk-go-v2/aws"
1516 "github.com/aws/aws-sdk-go-v2/service/s3"
1617 "github.com/aws/aws-sdk-go-v2/service/s3/types"
1718 "github.com/stretchr/testify/require"
@@ -185,6 +186,7 @@ func Test_s3Reader_getObjectPrefixForTime(t *testing.T) {
185186
186187type mockSingleObjectAPI struct {
187188 getObjectFunc func (ctx context.Context , params * s3.GetObjectInput , optFns ... func (* s3.Options )) (* s3.GetObjectOutput , error )
189+ getObjectTaggingFunc func (ctx context.Context , params * s3.GetObjectTaggingInput , optFns ... func (* s3.Options )) (* s3.GetObjectTaggingOutput , error )
188190 putObjectTaggingFunc func (ctx context.Context , params * s3.PutObjectTaggingInput , optFns ... func (* s3.Options )) (* s3.PutObjectTaggingOutput , error )
189191}
190192
@@ -195,6 +197,14 @@ func (m *mockSingleObjectAPI) GetObject(ctx context.Context, params *s3.GetObjec
195197 return nil , errors .New ("GetObject not mocked" )
196198}
197199
200+ func (m * mockSingleObjectAPI ) GetObjectTagging (ctx context.Context , params * s3.GetObjectTaggingInput , optFns ... func (* s3.Options )) (* s3.GetObjectTaggingOutput , error ) {
201+ if m .getObjectTaggingFunc != nil {
202+ return m .getObjectTaggingFunc (ctx , params , optFns ... )
203+ }
204+ // Default to empty tag set if no mock function provided
205+ return & s3.GetObjectTaggingOutput {TagSet : []types.Tag {}}, nil
206+ }
207+
198208func (m * mockSingleObjectAPI ) PutObjectTagging (ctx context.Context , params * s3.PutObjectTaggingInput , optFns ... func (* s3.Options )) (* s3.PutObjectTaggingOutput , error ) {
199209 if m .putObjectTaggingFunc != nil {
200210 return m .putObjectTaggingFunc (ctx , params , optFns ... )
@@ -294,6 +304,93 @@ func Test_readTelemetryForTime(t *testing.T) {
294304 require .NoError (t , err )
295305}
296306
307+ func Test_readTelemetryForTime_skipTaggedObjects (t * testing.T ) {
308+ testKey1 := "year=2021/month=02/day=01/hour=17/minute=32/traces_1"
309+ testKey2 := "year=2021/month=02/day=01/hour=17/minute=32/traces_2"
310+ testKey3 := "year=2021/month=02/day=01/hour=17/minute=32/traces_3"
311+ reader := s3TimeBasedReader {
312+ listObjectsClient : mockListObjectsAPI (func (params * s3.ListObjectsV2Input ) ListObjectsV2Pager {
313+ t .Helper ()
314+ require .Equal (t , "bucket" , * params .Bucket )
315+ require .Equal (t , "year=2021/month=02/day=01/hour=17/minute=32/traces_" , * params .Prefix )
316+
317+ return & mockListObjectsV2Pager {
318+ Pages : []* s3.ListObjectsV2Output {
319+ {
320+ Contents : []types.Object {
321+ {
322+ // Not tagged
323+ Key : & testKey1 ,
324+ },
325+ },
326+ },
327+ {
328+ Contents : []types.Object {
329+ {
330+ // Already tagged by the receiver as ingested
331+ Key : & testKey2 ,
332+ },
333+ },
334+ },
335+ {
336+ Contents : []types.Object {
337+ {
338+ // Has a tag, but not one set by the receiver
339+ Key : & testKey3 ,
340+ },
341+ },
342+ },
343+ },
344+ }
345+ }),
346+ singleObjectClient : & mockSingleObjectAPI {
347+ getObjectFunc : func (_ context.Context , params * s3.GetObjectInput , _ ... func (* s3.Options )) (* s3.GetObjectOutput , error ) {
348+ t .Helper ()
349+ require .Equal (t , "bucket" , * params .Bucket )
350+ // testKey2 should not be fetched because it has the ingested tag
351+ require .Contains (t , []string {testKey1 , testKey3 }, * params .Key )
352+ return & s3.GetObjectOutput {
353+ Body : io .NopCloser (bytes .NewReader ([]byte ("this is the body of the object" ))),
354+ }, nil
355+ },
356+ getObjectTaggingFunc : func (_ context.Context , params * s3.GetObjectTaggingInput , _ ... func (* s3.Options )) (* s3.GetObjectTaggingOutput , error ) {
357+ t .Helper ()
358+ require .Equal (t , "bucket" , * params .Bucket )
359+ require .Contains (t , []string {testKey1 , testKey2 , testKey3 }, * params .Key )
360+ var tagSet []types.Tag
361+ switch * params .Key {
362+ case testKey2 :
363+ tagSet = []types.Tag {{Key : aws .String (ingestedTag ), Value : aws .String (ingestedStatus )}}
364+ case testKey3 :
365+ tagSet = []types.Tag {{Key : aws .String ("env" ), Value : aws .String ("dev" )}}
366+ }
367+ return & s3.GetObjectTaggingOutput {TagSet : tagSet }, nil
368+ },
369+ },
370+ logger : zap .NewNop (),
371+ s3Bucket : "bucket" ,
372+ s3PartitionFormat : s3PartitionFormatDefault ,
373+ S3PartitionTimeLocation : time .UTC ,
374+ s3Prefix : "" ,
375+ filePrefix : "" ,
376+ filePrefixIncludeTelemetryType : true ,
377+ startTime : testTime ,
378+ endTime : testTime .Add (time .Minute ),
379+ skipIngestingTaggedObjects : true ,
380+ }
381+
382+ dataCallbackKeys := make ([]string , 0 )
383+
384+ err := reader .readTelemetryForTime (t .Context (), testTime , "traces" , func (_ context.Context , key string , data []byte ) error {
385+ t .Helper ()
386+ require .Equal (t , "this is the body of the object" , string (data ))
387+ dataCallbackKeys = append (dataCallbackKeys , key )
388+ return nil
389+ })
390+ require .Equal (t , []string {testKey1 , testKey3 }, dataCallbackKeys )
391+ require .NoError (t , err )
392+ }
393+
297394func Test_readTelemetryForTime_GetObjectError (t * testing.T ) {
298395 testKey := "year=2021/month=02/day=01/hour=17/minute=32/traces_1"
299396 testError := errors .New ("test error" )
@@ -757,6 +854,65 @@ func Test_readTelemetryForTime_TagFailure(t *testing.T) {
757854 require .Equal (t , []string {testKey }, dataCallbackKeys , "Data should still be processed" )
758855}
759856
857+ func Test_readTelemetryForTime_GetTagError (t * testing.T ) {
858+ testKey := "year=2023/month=01/day=02/hour=03/minute=04/traces_test"
859+
860+ reader := & s3TimeBasedReader {
861+ listObjectsClient : mockListObjectsAPI (func (params * s3.ListObjectsV2Input ) ListObjectsV2Pager {
862+ require .Equal (t , "bucket" , * params .Bucket )
863+ require .Equal (t , "year=2023/month=01/day=02/hour=03/minute=04/traces_" , * params .Prefix )
864+
865+ return & mockListObjectsV2Pager {
866+ Pages : []* s3.ListObjectsV2Output {
867+ {
868+ Contents : []types.Object {
869+ {Key : & testKey },
870+ },
871+ },
872+ },
873+ }
874+ }),
875+ singleObjectClient : & mockSingleObjectAPI {
876+ getObjectFunc : func (_ context.Context , params * s3.GetObjectInput , _ ... func (* s3.Options )) (* s3.GetObjectOutput , error ) {
877+ t .Helper ()
878+ require .Equal (t , "bucket" , * params .Bucket )
879+ require .Equal (t , testKey , * params .Key )
880+ return & s3.GetObjectOutput {
881+ Body : io .NopCloser (bytes .NewReader ([]byte ("this is the body of the object" ))),
882+ }, nil
883+ },
884+ getObjectTaggingFunc : func (_ context.Context , params * s3.GetObjectTaggingInput , _ ... func (* s3.Options )) (* s3.GetObjectTaggingOutput , error ) {
885+ t .Helper ()
886+ require .Equal (t , "bucket" , * params .Bucket )
887+ require .Equal (t , testKey , * params .Key )
888+ return nil , errors .New ("failed to get object tags" )
889+ },
890+ },
891+ logger : zap .NewNop (),
892+ s3Bucket : "bucket" ,
893+ s3PartitionFormat : s3PartitionFormatDefault ,
894+ S3PartitionTimeLocation : time .UTC ,
895+ filePrefix : "" ,
896+ filePrefixIncludeTelemetryType : true ,
897+ tagObjectAfterIngestion : true ,
898+ skipIngestingTaggedObjects : true ,
899+ }
900+
901+ testTime , err := time .Parse (time .RFC3339 , "2023-01-02T03:04:05Z" )
902+ require .NoError (t , err )
903+
904+ dataCallbackKeys := make ([]string , 0 )
905+ err = reader .readTelemetryForTime (t .Context (), testTime , "traces" , func (_ context.Context , key string , _ []byte ) error {
906+ t .Helper ()
907+ dataCallbackKeys = append (dataCallbackKeys , key )
908+ return nil
909+ })
910+
911+ require .Error (t , err )
912+ require .Contains (t , err .Error (), "failed to get object tags" )
913+ require .Empty (t , dataCallbackKeys , "No data should be processed when GetObjectTagging fails" )
914+ }
915+
760916func Test_determineTimestep (t * testing.T ) {
761917 tests := []struct {
762918 name string
0 commit comments