Skip to content

Commit 03ba730

Browse files
committed
refactor(go-forwarder): add client package
1 parent 58781b6 commit 03ba730

21 files changed

Lines changed: 598 additions & 497 deletions
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2026-Present Datadog, Inc.
5+
6+
package client
7+
8+
//go:generate go tool mockgen -source=kms.go -package=client -destination=kms_mockgen.go
9+
10+
import (
11+
"context"
12+
"encoding/base64"
13+
"fmt"
14+
"log/slog"
15+
"strings"
16+
17+
"github.com/aws/aws-sdk-go-v2/aws"
18+
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
19+
awsconfig "github.com/aws/aws-sdk-go-v2/config"
20+
"github.com/aws/aws-sdk-go-v2/service/kms"
21+
)
22+
23+
type KMS interface {
24+
Decrypt(ctx context.Context, params *kms.DecryptInput, optFns ...func(*kms.Options)) (*kms.DecryptOutput, error)
25+
}
26+
27+
func NewKMS(ctx context.Context, useFIPS bool) (KMS, error) {
28+
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithHTTPClient(awshttp.NewBuildableClient().WithTimeout(timeout)))
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
resolver := kms.NewDefaultEndpointResolverV2()
34+
params := kms.EndpointParameters{
35+
Region: aws.String(cfg.Region),
36+
UseFIPS: aws.Bool(useFIPS),
37+
}
38+
39+
endpoint, err := resolver.ResolveEndpoint(ctx, params)
40+
if err != nil && useFIPS {
41+
slog.Warn("FIPS endpoint not available, falling back to standard endpoint", slog.String("service", "kms"), slog.String("region", cfg.Region))
42+
params.UseFIPS = aws.Bool(false)
43+
endpoint, err = resolver.ResolveEndpoint(ctx, params)
44+
}
45+
if err != nil {
46+
return nil, fmt.Errorf("resolve endpoint: %w", err)
47+
}
48+
49+
return kms.NewFromConfig(cfg, func(o *kms.Options) {
50+
o.BaseEndpoint = aws.String(endpoint.URI.String())
51+
}), nil
52+
}
53+
54+
func DecryptKMSCiphertext(ctx context.Context, kmsClient KMS, ciphertext string) (string, error) {
55+
decoded, err := base64.StdEncoding.DecodeString(ciphertext)
56+
if err != nil {
57+
return "", fmt.Errorf("base64-decoding ciphertext: %w", err)
58+
}
59+
60+
result, err := kmsClient.Decrypt(ctx, &kms.DecryptInput{
61+
CiphertextBlob: decoded,
62+
})
63+
if err != nil {
64+
return "", fmt.Errorf("decrypting KMS ciphertext: %w", err)
65+
}
66+
67+
if result.Plaintext == nil {
68+
return "", fmt.Errorf("KMS decryption returned no plaintext")
69+
}
70+
71+
return strings.TrimSpace(string(result.Plaintext)), nil
72+
}

aws/logs_monitoring_go/internal/client/kms_mockgen.go

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aws/logs_monitoring_go/internal/config/kms_test.go renamed to aws/logs_monitoring_go/internal/client/kms_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// This product includes software developed at Datadog (https://www.datadoghq.com/).
44
// Copyright 2026-Present Datadog, Inc.
55

6-
package config
6+
package client
77

88
import (
99
"encoding/base64"
@@ -16,17 +16,17 @@ import (
1616
"go.uber.org/mock/gomock"
1717
)
1818

19-
func TestResolveFromKMS(t *testing.T) {
19+
func TestDecryptKMSCiphertext(t *testing.T) {
2020
validCiphertext := base64.StdEncoding.EncodeToString([]byte("encrypted-blob"))
2121

2222
tests := map[string]struct {
23-
mockSetup func(m *MockKMSAPIClient)
23+
mockSetup func(m *MockKMS)
2424
ciphertext string
2525
wantKey string
2626
wantErr bool
2727
}{
2828
"success": {
29-
mockSetup: func(m *MockKMSAPIClient) {
29+
mockSetup: func(m *MockKMS) {
3030
m.EXPECT().
3131
Decrypt(gomock.Any(), gomock.Any()).
3232
Return(&kms.DecryptOutput{
@@ -37,7 +37,7 @@ func TestResolveFromKMS(t *testing.T) {
3737
wantKey: "abcdef1234567890abcdef1234567890",
3838
},
3939
"whitespace_trimmed": {
40-
mockSetup: func(m *MockKMSAPIClient) {
40+
mockSetup: func(m *MockKMS) {
4141
m.EXPECT().
4242
Decrypt(gomock.Any(), gomock.Any()).
4343
Return(&kms.DecryptOutput{
@@ -48,12 +48,12 @@ func TestResolveFromKMS(t *testing.T) {
4848
wantKey: "abcdef1234567890abcdef1234567890",
4949
},
5050
"invalid_base64": {
51-
mockSetup: func(m *MockKMSAPIClient) {},
51+
mockSetup: func(m *MockKMS) {},
5252
ciphertext: "not-valid-base64!@#$",
5353
wantErr: true,
5454
},
5555
"aws_error": {
56-
mockSetup: func(m *MockKMSAPIClient) {
56+
mockSetup: func(m *MockKMS) {
5757
m.EXPECT().
5858
Decrypt(gomock.Any(), gomock.Any()).
5959
Return(nil, errors.New("InvalidCiphertextException: invalid ciphertext"))
@@ -62,7 +62,7 @@ func TestResolveFromKMS(t *testing.T) {
6262
wantErr: true,
6363
},
6464
"nil_plaintext": {
65-
mockSetup: func(m *MockKMSAPIClient) {
65+
mockSetup: func(m *MockKMS) {
6666
m.EXPECT().
6767
Decrypt(gomock.Any(), gomock.Any()).
6868
Return(&kms.DecryptOutput{
@@ -77,10 +77,10 @@ func TestResolveFromKMS(t *testing.T) {
7777
for name, tc := range tests {
7878
t.Run(name, func(t *testing.T) {
7979
ctrl := gomock.NewController(t)
80-
mock := NewMockKMSAPIClient(ctrl)
80+
mock := NewMockKMS(ctrl)
8181
tc.mockSetup(mock)
8282

83-
got, err := decryptKMSCiphertext(t.Context(), mock, tc.ciphertext)
83+
got, err := DecryptKMSCiphertext(t.Context(), mock, tc.ciphertext)
8484
if tc.wantErr {
8585
require.Error(t, err)
8686
return

aws/logs_monitoring_go/internal/handling/s3_client.go renamed to aws/logs_monitoring_go/internal/client/s3.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
// This product includes software developed at Datadog (https://www.datadoghq.com/).
44
// Copyright 2026-Present Datadog, Inc.
55

6-
package handling
6+
package client
77

8-
//go:generate go tool mockgen -source=s3_client.go -package=handling -destination=s3_client_mockgen.go
8+
//go:generate go tool mockgen -source=s3.go -package=client -destination=s3_mockgen.go
99

1010
import (
1111
"context"
@@ -25,22 +25,25 @@ const timeout = 10 * time.Second
2525

2626
var (
2727
s3ClientOnce sync.Once
28-
s3Client S3APIClient
28+
s3Client S3
2929
s3ClientErr error
3030
)
3131

32-
type S3APIClient interface {
32+
type S3 interface {
3333
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
34+
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
35+
DeleteObject(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error)
36+
ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error)
3437
}
3538

36-
func getS3APIClient(ctx context.Context, useFIPS bool) (S3APIClient, error) {
39+
func GetS3(ctx context.Context, useFIPS bool) (S3, error) {
3740
s3ClientOnce.Do(func() {
38-
s3Client, s3ClientErr = createS3APIClient(ctx, useFIPS)
41+
s3Client, s3ClientErr = newS3(ctx, useFIPS)
3942
})
4043
return s3Client, s3ClientErr
4144
}
4245

43-
func createS3APIClient(ctx context.Context, useFIPS bool) (S3APIClient, error) {
46+
func newS3(ctx context.Context, useFIPS bool) (S3, error) {
4447
cfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithHTTPClient(awshttp.NewBuildableClient().WithTimeout(timeout)))
4548
if err != nil {
4649
return nil, err
@@ -67,7 +70,7 @@ func createS3APIClient(ctx context.Context, useFIPS bool) (S3APIClient, error) {
6770
}), nil
6871
}
6972

70-
func getS3Object(ctx context.Context, client S3APIClient, bucket, key string) (io.ReadCloser, error) {
73+
func GetS3Object(ctx context.Context, client S3, bucket, key string) (io.ReadCloser, error) {
7174
result, err := client.GetObject(ctx, &s3.GetObjectInput{
7275
Bucket: aws.String(bucket),
7376
Key: aws.String(key),

aws/logs_monitoring_go/internal/client/s3_mockgen.go

Lines changed: 122 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)