@@ -2,17 +2,45 @@ package fdr
2
2
3
3
import (
4
4
"context"
5
+ "encoding/json"
6
+ "errors"
7
+ "io"
5
8
6
9
"github.com/aws/aws-sdk-go/aws"
7
10
"github.com/aws/aws-sdk-go/aws/credentials"
8
11
"github.com/aws/aws-sdk-go/aws/session"
12
+ "github.com/aws/aws-sdk-go/service/s3"
9
13
"github.com/aws/aws-sdk-go/service/sqs"
10
14
"github.com/m-mizutani/goerr"
11
15
"github.com/m-mizutani/hatchery/pkg/domain/config"
16
+ "github.com/m-mizutani/hatchery/pkg/domain/interfaces"
17
+ "github.com/m-mizutani/hatchery/pkg/domain/model"
12
18
"github.com/m-mizutani/hatchery/pkg/infra"
13
19
"github.com/m-mizutani/hatchery/pkg/utils"
14
20
)
15
21
22
+ type fdrMessage struct {
23
+ Bucket string `json:"bucket"`
24
+ Cid string `json:"cid"`
25
+ FileCount int64 `json:"fileCount"`
26
+ Files []file `json:"files"`
27
+ PathPrefix string `json:"pathPrefix"`
28
+ Timestamp int64 `json:"timestamp"`
29
+ TotalSize int64 `json:"totalSize"`
30
+ }
31
+
32
+ type file struct {
33
+ Checksum string `json:"checksum"`
34
+ Path string `json:"path"`
35
+ Size int64 `json:"size"`
36
+ }
37
+
38
+ type fdrClients struct {
39
+ infra * infra.Clients
40
+ sqs interfaces.SQS
41
+ s3 interfaces.S3
42
+ }
43
+
16
44
func Exec (ctx context.Context , clients * infra.Clients , req * config.FalconDataReplicatorImpl ) error {
17
45
// Create an AWS session
18
46
awsSession , err := session .NewSession (& aws.Config {
@@ -28,71 +56,91 @@ func Exec(ctx context.Context, clients *infra.Clients, req *config.FalconDataRep
28
56
return goerr .Wrap (err , "failed to create AWS session" ).With ("req" , req )
29
57
}
30
58
31
- // Create an SQS client
32
- sqsClient := sqs .New (awsSession )
59
+ // Create AWS service clients
60
+ sqsClient := clients .NewSQS (awsSession )
61
+ s3Client := clients .NewS3 (awsSession )
62
+ prefix := config .LogObjNamePrefix (req , utils .CtxNow (ctx ))
33
63
34
64
// Receive messages from SQS queue
35
- result , err := sqsClient .ReceiveMessageWithContext (ctx , & sqs.ReceiveMessageInput {
36
- QueueUrl : aws .String (req .SqsUrl ),
37
- MaxNumberOfMessages : aws .Int64 (10 ),
38
- })
39
- if err != nil {
40
- return goerr .Wrap (err , "failed to receive messages from SQS" ).With ("req" , req )
65
+ input := & sqs.ReceiveMessageInput {
66
+ QueueUrl : aws .String (req .SqsUrl ),
67
+ }
68
+ if req .MaxMessages != nil {
69
+ input .MaxNumberOfMessages = aws .Int64 (int64 (* req .MaxMessages ))
41
70
}
42
71
43
- utils .CtxLogger (ctx ).Info ("FDR: received messages from SQS" , "count" , len (result .Messages ))
44
- /*
45
- prefix := config.ToObjNamePrefix(req, utils.CtxNow(ctx))
72
+ for i := 0 ; ; i ++ {
73
+ if req .MaxPulls != nil && i >= * req .MaxPulls {
74
+ break
75
+ }
76
+
77
+ c := & fdrClients {infra : clients , sqs : sqsClient , s3 : s3Client }
78
+ if err := copy (ctx , c , input , model .CSBucket (req .Bucket ), prefix ); err != nil {
79
+ if err == errNoMoreMessage {
80
+ break
81
+ }
82
+ return err
83
+ }
84
+ }
85
+
86
+ return nil
87
+ }
88
+
89
+ var (
90
+ errNoMoreMessage = errors .New ("no more message" )
91
+ )
46
92
47
- // Iterate over received messages
48
- for _, message := range result.Messages {
49
- // Get the S3 object key from the message
50
- s3ObjectKey := *message.Body
93
+ func copy (ctx context.Context , clients * fdrClients , input * sqs.ReceiveMessageInput , bucket model.CSBucket , prefix model.CSObjectName ) error {
94
+ result , err := clients .sqs .ReceiveMessageWithContext (ctx , input )
95
+ if err != nil {
96
+ return goerr .Wrap (err , "failed to receive messages from SQS" ).With ("input" , input )
97
+ }
98
+ if len (result .Messages ) == 0 {
99
+ return errNoMoreMessage
100
+ }
51
101
102
+ // Iterate over received messages
103
+ for _ , message := range result .Messages {
104
+ // Get the S3 object key from the message
105
+ var msg fdrMessage
106
+ if err := json .Unmarshal ([]byte (* message .Body ), & msg ); err != nil {
107
+ return goerr .Wrap (err , "failed to unmarshal message" ).With ("message" , * message .Body )
108
+ }
109
+
110
+ for _ , file := range msg .Files {
52
111
// Download the object from S3
53
- s3Client := s3.New(awsSession)
54
- s3ObjectOutput, err := s3Client.GetObjectWithContext(ctx, &s3.GetObjectInput{
55
- Bucket: aws.String(req.S3Bucket),
56
- Key: aws.String(s3ObjectKey),
57
- })
58
- if err != nil {
59
- log.Printf("failed to download object from S3: %v", err)
60
- continue
112
+ s3Input := & s3.GetObjectInput {
113
+ Bucket : aws .String (msg .Bucket ),
114
+ Key : aws .String (file .Path ),
61
115
}
62
-
63
- w := clients.CloudStorage().NewObjectWriter(ctx, model.CSBucket(req.GetBucket()), objName)
64
- // Upload the object to Google Cloud Storage
65
- gcsClient, err := storage.NewService(ctx, option.WithCredentialsFile(req.GCSCredentialsFile))
116
+ s3Obj , err := clients .s3 .GetObjectWithContext (ctx , s3Input )
66
117
if err != nil {
67
- log.Printf("failed to create GCS client: %v", err)
68
- continue
118
+ return goerr .Wrap (err , "failed to download object from S3" ).With ("msg" , msg )
69
119
}
120
+ defer utils .SafeClose (s3Obj .Body )
70
121
71
- objectName := uuid.New().String() // Generate a unique object name
72
- gcsObject := &storage.Object{
73
- Name: objectName,
74
- Bucket: req.Bucket,
75
- Metadata: make(map[string]string),
76
- }
122
+ csObj := prefix + model .CSObjectName (file .Path )
123
+ w := clients .infra .CloudStorage ().NewObjectWriter (ctx , bucket , csObj )
77
124
78
- // Copy the object data to GCS
79
- _, err = gcsClient.Objects.Insert(req.Bucket, gcsObject).Media(s3ObjectOutput.Body).Do()
80
- if err != nil {
81
- log.Printf("failed to upload object to GCS: %v", err)
82
- continue
125
+ if _ , err := io .Copy (w , s3Obj .Body ); err != nil {
126
+ return goerr .Wrap (err , "failed to write object to GCS" ).With ("msg" , msg )
83
127
}
84
-
85
- // Delete the message from SQS
86
- _, err = sqsClient.DeleteMessageWithContext(ctx, &sqs.DeleteMessageInput{
87
- QueueUrl: aws.String(req.SqsUrl),
88
- ReceiptHandle: message.ReceiptHandle,
89
- })
90
- if err != nil {
91
- return goerr.Wrap(err, "failed to delete message from SQS").With("req", req)
128
+ if err := w .Close (); err != nil {
129
+ return goerr .Wrap (err , "failed to close object writer" ).With ("msg" , msg )
92
130
}
93
131
94
- utils.CtxLogger(ctx).Info("FDR: object forwarded from S3 to GCS", "s3ObjectKey ", s3ObjectKey , "gcsObjectName ", objectName )
132
+ utils .CtxLogger (ctx ).Info ("FDR: object forwarded from S3 to GCS" , "s3 " , s3Input , "gcsObj " , csObj )
95
133
}
96
- */
134
+
135
+ // Delete the message from SQS
136
+ _ , err = clients .sqs .DeleteMessageWithContext (ctx , & sqs.DeleteMessageInput {
137
+ QueueUrl : input .QueueUrl ,
138
+ ReceiptHandle : message .ReceiptHandle ,
139
+ })
140
+ if err != nil {
141
+ return goerr .Wrap (err , "failed to delete message from SQS" )
142
+ }
143
+ }
144
+
97
145
return nil
98
146
}
0 commit comments