@@ -42,6 +42,8 @@ import (
42
42
autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/applicationautoscaling/types"
43
43
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
44
44
dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
45
+ legacydynamo "github.com/aws/aws-sdk-go/service/dynamodb"
46
+ "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
45
47
"github.com/aws/smithy-go"
46
48
"github.com/google/uuid"
47
49
"github.com/gravitational/trace"
@@ -689,6 +691,24 @@ type checkpointKey struct {
689
691
EventKey string `json:"event_key,omitempty"`
690
692
}
691
693
694
+ // legacyCheckpointKey is the old checkpoint key returned by older auth versions. Used to decode
695
+ // checkpoints originating from old auths. Commonly we don't bother supporting pagination/cursors
696
+ // across teleport versions since the benefit of doing so is usually minimal, but this value is used
697
+ // as on-disk state by long running event export operations, and so must be supported.
698
+ //
699
+ // DELETE IN: 19.0.0
700
+ type legacyCheckpointKey struct {
701
+ // The date that the Dynamo iterator corresponds to.
702
+ Date string `json:"date,omitempty"`
703
+
704
+ // A DynamoDB query iterator. Allows us to resume a partial query.
705
+ Iterator map [string ]* legacydynamo.AttributeValue `json:"iterator,omitempty"`
706
+
707
+ // EventKey is a derived identifier for an event used for resuming
708
+ // sub-page breaks due to size constraints.
709
+ EventKey string `json:"event_key,omitempty"`
710
+ }
711
+
692
712
// SearchEvents is a flexible way to find events.
693
713
//
694
714
// Event types to filter can be specified and pagination is handled by an iterator key that allows
@@ -936,11 +956,49 @@ func getCheckpointFromStartKey(startKey string) (checkpointKey, error) {
936
956
}
937
957
// If a checkpoint key is provided, unmarshal it so we can work with it's parts.
938
958
if err := json .Unmarshal ([]byte (startKey ), & checkpoint ); err != nil {
959
+ // attempt to decode as legacy format.
960
+ if checkpoint , err = getCheckpointFromLegacyStartKey (startKey ); err == nil {
961
+ return checkpoint , nil
962
+ }
939
963
return checkpointKey {}, trace .Wrap (err )
940
964
}
941
965
return checkpoint , nil
942
966
}
943
967
968
+ // getCheckpointFromLegacyStartKey is a helper function that decodes a legacy checkpoint key
969
+ // into the new format. The old format used raw dynamo attribute values for the iterator, where
970
+ // the new format uses a json-serialized map with bare values.
971
+ //
972
+ // DELETE IN: 19.0.0
973
+ func getCheckpointFromLegacyStartKey (startKey string ) (checkpointKey , error ) {
974
+ var checkpoint legacyCheckpointKey
975
+ if startKey == "" {
976
+ return checkpointKey {}, nil
977
+ }
978
+ // If a checkpoint key is provided, unmarshal it so we can work with its parts.
979
+ if err := json .Unmarshal ([]byte (startKey ), & checkpoint ); err != nil {
980
+ return checkpointKey {}, trace .Wrap (err )
981
+ }
982
+
983
+ // decode the dynamo attrs into the go map repr common to the old and new formats.
984
+ m := make (map [string ]any )
985
+ if err := dynamodbattribute .UnmarshalMap (checkpoint .Iterator , & m ); err != nil {
986
+ return checkpointKey {}, trace .Wrap (err )
987
+ }
988
+
989
+ // encode the map into json, making it equivalent to the new format.
990
+ iterator , err := json .Marshal (m )
991
+ if err != nil {
992
+ return checkpointKey {}, trace .Wrap (err )
993
+ }
994
+
995
+ return checkpointKey {
996
+ Date : checkpoint .Date ,
997
+ Iterator : string (iterator ),
998
+ EventKey : checkpoint .EventKey ,
999
+ }, nil
1000
+ }
1001
+
944
1002
func getExprFilter (filter searchEventsFilter ) * string {
945
1003
var filterConds []string
946
1004
if len (filter .eventTypes ) > 0 {
0 commit comments