object: add support for aws sdk v2 in s3agent createbucket

initialize both aws sdk v1 and v2 clients in s3agent.
update createbucket to use sdk v2 while keeping all
other methods on sdk v1.

Signed-off-by: Oded Viner <oviner@redhat.com>
This commit is contained in:
Oded Viner committed 2026-03-31 17:41:51 +03:00
1 parent fb400b9d29
commit 86c920fa79
4 files changed
+89 -41

No files matched your search

+6 -1
View File
@@ -15,8 +15,10 @@ replace (
require (
github.com/IBM/keyprotect-go-client v0.15.1
github.com/aws/aws-sdk-go v1.55.8
github.com/aws/aws-sdk-go-v2 v1.41.5
github.com/aws/aws-sdk-go-v2/config v1.32.13
github.com/aws/aws-sdk-go-v2/credentials v1.19.13
github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0
github.com/aws/aws-sdk-go-v2/service/sns v1.39.15
github.com/aws/smithy-go v1.24.2
github.com/banzaicloud/k8s-objectmatcher v1.8.0
@@ -70,13 +72,16 @@ require (
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
github.com/ansel1/merry v1.8.1 // indirect
github.com/ansel1/merry/v2 v2.2.2 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect
+52 -17
View File
@@ -18,11 +18,17 @@ package object
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"net/http"
"net/url"
"strings"
v2aws "github.com/aws/aws-sdk-go-v2/aws"
v2creds "github.com/aws/aws-sdk-go-v2/credentials"
s3v2 "github.com/aws/aws-sdk-go-v2/service/s3"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
@@ -36,7 +42,8 @@ const CephRegion = "us-east-1"
// S3Agent wraps the s3.S3 structure to allow for wrapper methods
type S3Agent struct {
Client *s3.S3
Client *s3.S3 // v1 client
ClientV2 *s3v2.Client // v2 client
}
func NewS3Agent(accessKey, secretKey, endpoint string, debug bool, tlsCert []byte, insecure bool, httpClient *http.Client) (*S3Agent, error) {
@@ -44,6 +51,7 @@ func NewS3Agent(accessKey, secretKey, endpoint string, debug bool, tlsCert []byt
if debug {
logLevel = aws.LogDebug
}
tlsEnabled := false
if len(tlsCert) > 0 || insecure {
tlsEnabled = true
@@ -57,7 +65,10 @@ func NewS3Agent(accessKey, secretKey, endpoint string, debug bool, tlsCert []byt
}
}
session, err := awssession.NewSession(
// -----------------------------
// SDK v1 client initialization
// -----------------------------
v1Session, err := awssession.NewSession(
aws.NewConfig().
WithRegion(CephRegion).
WithCredentials(credentials.NewStaticCredentials(accessKey, secretKey, "")).
@@ -69,11 +80,39 @@ func NewS3Agent(accessKey, secretKey, endpoint string, debug bool, tlsCert []byt
WithLogLevel(logLevel),
)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "failed to create v1 session")
}
svc := s3.New(session)
v1Client := s3.New(v1Session)
// -----------------------------
// SDK v2 client initialization
// -----------------------------
scheme := "http"
if tlsEnabled {
scheme = "https"
}
u, perr := url.Parse(endpoint)
if perr != nil || (u.Scheme != "http" && u.Scheme != "https") {
u, _ = url.Parse(scheme + "://" + endpoint)
}
// Use the officially-supported v2 endpoint customization mechanism.
// This keeps S3's internal endpoint customizations (including hostname handling)
// consistent with the SDK's expectations, which is especially important for TLS.
baseEndpoint := u.String()
v2Cfg := v2aws.Config{
Region: CephRegion,
Credentials: v2aws.NewCredentialsCache(v2creds.NewStaticCredentialsProvider(accessKey, secretKey, "")),
HTTPClient: httpClient,
BaseEndpoint: &baseEndpoint,
RetryMaxAttempts: 5,
RetryMode: v2aws.RetryModeStandard,
}
v2Client := s3v2.NewFromConfig(v2Cfg, func(o *s3v2.Options) {
o.UsePathStyle = true
})
return &S3Agent{
Client: svc,
Client: v1Client,
ClientV2: v2Client,
}, nil
}
@@ -93,22 +132,18 @@ func (s *S3Agent) createBucket(name string, infoLogging bool) error {
} else {
logger.Debugf("creating bucket %q", name)
}
bucketInput := &s3.CreateBucketInput{
input := &s3v2.CreateBucketInput{
Bucket: &name,
}
_, err := s.Client.CreateBucket(bucketInput)
_, err := s.ClientV2.CreateBucket(context.TODO(), input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
logger.Debugf("DEBUG: after s3 call, ok=%v, aerr=%v", ok, aerr)
switch aerr.Code() {
case s3.ErrCodeBucketAlreadyExists:
logger.Debugf("bucket %q already exists", name)
return nil
case s3.ErrCodeBucketAlreadyOwnedByYou:
logger.Debugf("bucket %q already owned by you", name)
return nil
}
var alreadyExists *s3types.BucketAlreadyExists
var alreadyOwned *s3types.BucketAlreadyOwnedByYou
if errors.As(err, &alreadyExists) || errors.As(err, &alreadyOwned) {
logger.Debugf("bucket %q already exists or is owned by you", name)
return nil
}
return errors.Wrapf(err, "failed to create bucket %q", name)
}
+30 -1
View File
@@ -37,6 +37,8 @@ func TestNewS3Agent(t *testing.T) {
assert.NotEqual(t, aws.LogDebug, s3Agent.Client.Config.LogLevel)
assert.Equal(t, nil, s3Agent.Client.Config.HTTPClient.Transport)
assert.True(t, *s3Agent.Client.Config.DisableSSL)
assert.NotNil(t, s3Agent.ClientV2)
assert.Equal(t, "http://endpoint", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("test with debug without tls", func(t *testing.T) {
debug := true
@@ -47,6 +49,8 @@ func TestNewS3Agent(t *testing.T) {
assert.Equal(t, &logLevel, s3Agent.Client.Config.LogLevel)
assert.Nil(t, s3Agent.Client.Config.HTTPClient.Transport)
assert.True(t, *s3Agent.Client.Config.DisableSSL)
assert.NotNil(t, s3Agent.ClientV2)
assert.Equal(t, "http://endpoint", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("test without tls client cert but insecure tls", func(t *testing.T) {
debug := true
@@ -56,6 +60,8 @@ func TestNewS3Agent(t *testing.T) {
assert.NotNil(t, s3Agent.Client.Config.HTTPClient.Transport.(*http.Transport).TLSClientConfig.RootCAs) // still includes sys certs
assert.True(t, s3Agent.Client.Config.HTTPClient.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify)
assert.False(t, *s3Agent.Client.Config.DisableSSL)
assert.NotNil(t, s3Agent.ClientV2)
assert.Equal(t, "https://endpoint", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("test with secure tls client cert", func(t *testing.T) {
debug := true
@@ -65,6 +71,8 @@ func TestNewS3Agent(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, s3Agent.Client.Config.HTTPClient.Transport.(*http.Transport).TLSClientConfig.RootCAs)
assert.False(t, *s3Agent.Client.Config.DisableSSL)
assert.NotNil(t, s3Agent.ClientV2)
assert.Equal(t, "https://endpoint", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("test with insecure tls client cert", func(t *testing.T) {
debug := true
@@ -75,6 +83,8 @@ func TestNewS3Agent(t *testing.T) {
assert.NotNil(t, s3Agent.Client.Config.HTTPClient.Transport)
assert.True(t, s3Agent.Client.Config.HTTPClient.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify)
assert.False(t, *s3Agent.Client.Config.DisableSSL)
assert.NotNil(t, s3Agent.ClientV2)
assert.Equal(t, "https://endpoint", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("test with custom http.Client", func(t *testing.T) {
debug := true
@@ -82,7 +92,6 @@ func TestNewS3Agent(t *testing.T) {
insecure := false
httpClient := &http.Client{
Transport: &http.Transport{
// set some values to check if they are passed through
MaxIdleConns: 7,
MaxIdleConnsPerHost: 13,
MaxConnsPerHost: 17,
@@ -97,5 +106,25 @@ func TestNewS3Agent(t *testing.T) {
assert.Equal(t, 7, transport.(*http.Transport).MaxIdleConns)
assert.Equal(t, 13, transport.(*http.Transport).MaxIdleConnsPerHost)
assert.Equal(t, 17, transport.(*http.Transport).MaxConnsPerHost)
assert.NotNil(t, s3Agent.ClientV2)
assert.Equal(t, "http://endpoint", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("v2 endpoint with host:port for TLS", func(t *testing.T) {
ep := "rook-ceph-rgw-store.test-ns.svc:443"
s3Agent, err := NewS3Agent(accessKey, secretKey, ep, false, nil, true, nil)
assert.NoError(t, err)
assert.Equal(t, "https://rook-ceph-rgw-store.test-ns.svc:443", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("v2 endpoint with host:port without TLS", func(t *testing.T) {
ep := "rook-ceph-rgw-store.test-ns.svc:80"
s3Agent, err := NewS3Agent(accessKey, secretKey, ep, false, nil, false, nil)
assert.NoError(t, err)
assert.Equal(t, "http://rook-ceph-rgw-store.test-ns.svc:80", *s3Agent.ClientV2.Options().BaseEndpoint)
})
t.Run("v2 endpoint with full URL", func(t *testing.T) {
ep := "https://rook-ceph-rgw-store.test-ns.svc:443"
s3Agent, err := NewS3Agent(accessKey, secretKey, ep, false, nil, true, nil)
assert.NoError(t, err)
assert.Equal(t, "https://rook-ceph-rgw-store.test-ns.svc:443", *s3Agent.ClientV2.Options().BaseEndpoint)
})
}
+1 -22
View File
@@ -33,27 +33,7 @@ openssl req -new -key "${DIR}"/"${SERVICE}".key -subj "/CN=system:node:${SERVICE
export CSR_NAME=${SERVICE}-csr
# Minimum 1.19.0 kubernetes version is required for certificates.k8s.io/v1 version
SERVER_VERSION=$(kubectl version --short | awk -F "." '/Server Version/ {print $2}')
MINIMUM_VERSION=19
if [ "${SERVER_VERSION}" -lt "${MINIMUM_VERSION}" ]
then
cat <<EOF >"${DIR}"/csr.yaml
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: ${CSR_NAME}
spec:
groups:
- system:authenticated
request: $(cat "${DIR}"/server.csr | base64 | tr -d '\n')
usages:
- digital signature
- key encipherment
- server auth
EOF
else
cat <<EOF >"${DIR}"/csr.yaml
cat <<EOF >"${DIR}"/csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
@@ -68,7 +48,6 @@ else
- key encipherment
- server auth
EOF
fi
kubectl create -f "${DIR}/"csr.yaml