[RFC-0010] Add multi-tenant workload identity support for AWS Bucket

Signed-off-by: cappyzawa <cappyzawa@gmail.com>
This commit is contained in:
cappyzawa 2025-08-13 23:42:11 +09:00
parent 48da00dba2
commit 041aa6c993
No known key found for this signature in database
7 changed files with 152 additions and 115 deletions

View File

@ -33,7 +33,8 @@ const (
// BucketProviderGeneric for any S3 API compatible storage Bucket. // BucketProviderGeneric for any S3 API compatible storage Bucket.
BucketProviderGeneric string = "generic" BucketProviderGeneric string = "generic"
// BucketProviderAmazon for an AWS S3 object storage Bucket. // BucketProviderAmazon for an AWS S3 object storage Bucket.
// Provides support for retrieving credentials from the AWS EC2 service. // Provides support for retrieving credentials from the AWS EC2 service
// and workload identity authentication.
BucketProviderAmazon string = "aws" BucketProviderAmazon string = "aws"
// BucketProviderGoogle for a Google Cloud Storage Bucket. // BucketProviderGoogle for a Google Cloud Storage Bucket.
// Provides support for authentication using a workload identity. // Provides support for authentication using a workload identity.
@ -51,7 +52,7 @@ const (
// +kubebuilder:validation:XValidation:rule="self.provider != 'generic' || !has(self.sts) || self.sts.provider == 'ldap'", message="'ldap' is the only supported STS provider for the 'generic' Bucket provider" // +kubebuilder:validation:XValidation:rule="self.provider != 'generic' || !has(self.sts) || self.sts.provider == 'ldap'", message="'ldap' is the only supported STS provider for the 'generic' Bucket provider"
// +kubebuilder:validation:XValidation:rule="!has(self.sts) || self.sts.provider != 'aws' || !has(self.sts.secretRef)", message="spec.sts.secretRef is not required for the 'aws' STS provider" // +kubebuilder:validation:XValidation:rule="!has(self.sts) || self.sts.provider != 'aws' || !has(self.sts.secretRef)", message="spec.sts.secretRef is not required for the 'aws' STS provider"
// +kubebuilder:validation:XValidation:rule="!has(self.sts) || self.sts.provider != 'aws' || !has(self.sts.certSecretRef)", message="spec.sts.certSecretRef is not required for the 'aws' STS provider" // +kubebuilder:validation:XValidation:rule="!has(self.sts) || self.sts.provider != 'aws' || !has(self.sts.certSecretRef)", message="spec.sts.certSecretRef is not required for the 'aws' STS provider"
// +kubebuilder:validation:XValidation:rule="self.provider == 'gcp' || !has(self.serviceAccountName)", message="ServiceAccountName is only supported for the 'gcp' Bucket provider" // +kubebuilder:validation:XValidation:rule="self.provider == 'gcp' || self.provider == 'aws' || !has(self.serviceAccountName)", message="ServiceAccountName is only supported for the 'gcp' and 'aws' Bucket providers"
// +kubebuilder:validation:XValidation:rule="!has(self.secretRef) || !has(self.serviceAccountName)", message="cannot set both .spec.secretRef and .spec.serviceAccountName" // +kubebuilder:validation:XValidation:rule="!has(self.secretRef) || !has(self.serviceAccountName)", message="cannot set both .spec.secretRef and .spec.serviceAccountName"
type BucketSpec struct { type BucketSpec struct {
// Provider of the object storage bucket. // Provider of the object storage bucket.
@ -96,7 +97,8 @@ type BucketSpec struct {
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"` SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
// ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate // ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
// the bucket. For more information about workload identity: // the bucket. This field is only supported for the 'gcp' and 'aws' providers.
// For more information about workload identity:
// https://fluxcd.io/flux/components/source/buckets/#workload-identity // https://fluxcd.io/flux/components/source/buckets/#workload-identity
// +optional // +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"` ServiceAccountName string `json:"serviceAccountName,omitempty"`

View File

@ -145,7 +145,8 @@ spec:
serviceAccountName: serviceAccountName:
description: |- description: |-
ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
the bucket. For more information about workload identity: the bucket. This field is only supported for the 'gcp' and 'aws' providers.
For more information about workload identity:
https://fluxcd.io/flux/components/source/buckets/#workload-identity https://fluxcd.io/flux/components/source/buckets/#workload-identity
type: string type: string
sts: sts:
@ -238,8 +239,9 @@ spec:
rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)' rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)'
- message: spec.sts.certSecretRef is not required for the 'aws' STS provider - message: spec.sts.certSecretRef is not required for the 'aws' STS provider
rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)' rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)'
- message: ServiceAccountName is only supported for the 'gcp' Bucket provider - message: ServiceAccountName is only supported for the 'gcp' and 'aws'
rule: self.provider == 'gcp' || !has(self.serviceAccountName) Bucket providers
rule: self.provider == 'gcp' || self.provider == 'aws' || !has(self.serviceAccountName)
- message: cannot set both .spec.secretRef and .spec.serviceAccountName - message: cannot set both .spec.secretRef and .spec.serviceAccountName
rule: '!has(self.secretRef) || !has(self.serviceAccountName)' rule: '!has(self.secretRef) || !has(self.serviceAccountName)'
status: status:

View File

@ -190,7 +190,8 @@ string
<td> <td>
<em>(Optional)</em> <em>(Optional)</em>
<p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate <p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
the bucket. For more information about workload identity: the bucket. This field is only supported for the &lsquo;gcp&rsquo; and &lsquo;aws&rsquo; providers.
For more information about workload identity:
<a href="https://fluxcd.io/flux/components/source/buckets/#workload-identity">https://fluxcd.io/flux/components/source/buckets/#workload-identity</a></p> <a href="https://fluxcd.io/flux/components/source/buckets/#workload-identity">https://fluxcd.io/flux/components/source/buckets/#workload-identity</a></p>
</td> </td>
</tr> </tr>
@ -1646,7 +1647,8 @@ string
<td> <td>
<em>(Optional)</em> <em>(Optional)</em>
<p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate <p>ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate
the bucket. For more information about workload identity: the bucket. This field is only supported for the &lsquo;gcp&rsquo; and &lsquo;aws&rsquo; providers.
For more information about workload identity:
<a href="https://fluxcd.io/flux/components/source/buckets/#workload-identity">https://fluxcd.io/flux/components/source/buckets/#workload-identity</a></p> <a href="https://fluxcd.io/flux/components/source/buckets/#workload-identity">https://fluxcd.io/flux/components/source/buckets/#workload-identity</a></p>
</td> </td>
</tr> </tr>

View File

@ -199,6 +199,8 @@ The Provider allows for specifying the
[Amazon AWS Region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions) [Amazon AWS Region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions)
using the [`.spec.region` field](#region). using the [`.spec.region` field](#region).
For detailed setup instructions, see: https://fluxcd.io/flux/integrations/aws/#for-amazon-simple-storage-service
##### AWS EC2 example ##### AWS EC2 example
**Note:** On EKS you have to create an [IAM role](#aws-iam-role-example) for **Note:** On EKS you have to create an [IAM role](#aws-iam-role-example) for
@ -273,6 +275,55 @@ data:
secretkey: <BASE64> secretkey: <BASE64>
``` ```
##### AWS Controller-Level Workload Identity example
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: Bucket
metadata:
name: aws-controller-level-workload-identity
namespace: default
spec:
interval: 5m0s
provider: aws
bucketName: podinfo
endpoint: s3.amazonaws.com
region: us-east-1
timeout: 30s
```
##### AWS Object-Level Workload Identity example
**Note:** To use Object-Level Workload Identity (`.spec.serviceAccountName` with
cloud providers), the controller feature gate `ObjectLevelWorkloadIdentity` must
be enabled.
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: Bucket
metadata:
name: aws-object-level-workload-identity
namespace: default
spec:
interval: 5m0s
provider: aws
bucketName: podinfo
endpoint: s3.amazonaws.com
region: us-east-1
serviceAccountName: aws-workload-identity-sa
timeout: 30s
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: aws-workload-identity-sa
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/flux-bucket-role
```
#### Azure #### Azure
When a Bucket's `.spec.provider` is set to `azure`, the source-controller will When a Bucket's `.spec.provider` is set to `azure`, the source-controller will

View File

@ -30,6 +30,9 @@ import (
"github.com/minio/minio-go/v7/pkg/s3utils" "github.com/minio/minio-go/v7/pkg/s3utils"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"github.com/fluxcd/pkg/auth"
awsauth "github.com/fluxcd/pkg/auth/aws"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
) )
@ -46,6 +49,7 @@ type options struct {
tlsConfig *tls.Config tlsConfig *tls.Config
stsTLSConfig *tls.Config stsTLSConfig *tls.Config
proxyURL *url.URL proxyURL *url.URL
authOpts []auth.Option
} }
// Option is a function that configures the Minio client. // Option is a function that configures the Minio client.
@ -86,8 +90,15 @@ func WithSTSTLSConfig(tlsConfig *tls.Config) Option {
} }
} }
// WithAuth sets the auth options for workload identity authentication.
func WithAuth(authOpts ...auth.Option) Option {
return func(o *options) {
o.authOpts = authOpts
}
}
// NewClient creates a new Minio storage client. // NewClient creates a new Minio storage client.
func NewClient(bucket *sourcev1.Bucket, opts ...Option) (*MinioClient, error) { func NewClient(ctx context.Context, bucket *sourcev1.Bucket, opts ...Option) (*MinioClient, error) {
var o options var o options
for _, opt := range opts { for _, opt := range opts {
opt(&o) opt(&o)
@ -105,7 +116,11 @@ func NewClient(bucket *sourcev1.Bucket, opts ...Option) (*MinioClient, error) {
case o.secret != nil: case o.secret != nil:
minioOpts.Creds = newCredsFromSecret(o.secret) minioOpts.Creds = newCredsFromSecret(o.secret)
case bucketProvider == sourcev1.BucketProviderAmazon: case bucketProvider == sourcev1.BucketProviderAmazon:
minioOpts.Creds = newAWSCreds(bucket, o.proxyURL) creds, err := newAWSCreds(ctx, &o)
if err != nil {
return nil, err
}
minioOpts.Creds = creds
case bucketProvider == sourcev1.BucketProviderGeneric: case bucketProvider == sourcev1.BucketProviderGeneric:
minioOpts.Creds = newGenericCreds(bucket, &o) minioOpts.Creds = newGenericCreds(bucket, &o)
} }
@ -159,23 +174,30 @@ func newCredsFromSecret(secret *corev1.Secret) *credentials.Credentials {
} }
// newAWSCreds creates a new Minio credentials object for `aws` bucket provider. // newAWSCreds creates a new Minio credentials object for `aws` bucket provider.
func newAWSCreds(bucket *sourcev1.Bucket, proxyURL *url.URL) *credentials.Credentials { //
stsEndpoint := "" // This function is only called when Secret authentication is not available.
if sts := bucket.Spec.STS; sts != nil { //
stsEndpoint = sts.Endpoint // Uses AWS SDK's config.LoadDefaultConfig() which supports:
// - Workload Identity (IRSA/EKS Pod Identity)
// - EC2 instance profiles
// - Environment variables
// - Shared credentials files
// - All other AWS SDK authentication methods
func newAWSCreds(ctx context.Context, o *options) (*credentials.Credentials, error) {
var opts auth.Options
opts.Apply(o.authOpts...)
awsCredsProvider := awsauth.NewCredentialsProvider(ctx, o.authOpts...)
awsCreds, err := awsCredsProvider.Retrieve(ctx)
if err != nil {
return nil, fmt.Errorf("AWS authentication failed: %w", err)
} }
creds := credentials.NewIAM(stsEndpoint) return credentials.NewStaticV4(
if proxyURL != nil { awsCreds.AccessKeyID,
transport := http.DefaultTransport.(*http.Transport).Clone() awsCreds.SecretAccessKey,
transport.Proxy = http.ProxyURL(proxyURL) awsCreds.SessionToken,
client := &http.Client{Transport: transport} ), nil
creds = credentials.New(&credentials.IAM{
Client: client,
Endpoint: stsEndpoint,
})
}
return creds
} }
// newGenericCreds creates a new Minio credentials object for the `generic` bucket provider. // newGenericCreds creates a new Minio credentials object for the `generic` bucket provider.

View File

@ -20,7 +20,6 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt" "fmt"
@ -76,6 +75,8 @@ var (
testServerCert string testServerCert string
// testServerKey is the path to the server key used to start the Minio and STS servers. // testServerKey is the path to the server key used to start the Minio and STS servers.
testServerKey string testServerKey string
// ctx is the common context used in tests.
ctx context.Context
) )
var ( var (
@ -126,6 +127,9 @@ var (
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
// Initialize common test context
ctx = context.Background()
// Uses a sensible default on Windows (TCP/HTTP) and Linux/MacOS (socket) // Uses a sensible default on Windows (TCP/HTTP) and Linux/MacOS (socket)
pool, err := dockertest.NewPool("") pool, err := dockertest.NewPool("")
if err != nil { if err != nil {
@ -173,7 +177,7 @@ func TestMain(m *testing.M) {
testMinioAddress = fmt.Sprintf("127.0.0.1:%v", resource.GetPort("9000/tcp")) testMinioAddress = fmt.Sprintf("127.0.0.1:%v", resource.GetPort("9000/tcp"))
// Construct a Minio client using the address of the Minio server. // Construct a Minio client using the address of the Minio server.
testMinioClient, err = NewClient(bucketStub(bucket, testMinioAddress), testMinioClient, err = NewClient(ctx, bucketStub(bucket, testMinioAddress),
WithSecret(secret.DeepCopy()), WithSecret(secret.DeepCopy()),
WithTLSConfig(testTLSConfig)) WithTLSConfig(testTLSConfig))
if err != nil { if err != nil {
@ -197,7 +201,6 @@ func TestMain(m *testing.M) {
log.Fatalf("could not connect to docker: %s", err) log.Fatalf("could not connect to docker: %s", err)
} }
ctx := context.Background()
createBucket(ctx) createBucket(ctx)
addObjectToBucket(ctx) addObjectToBucket(ctx)
run := m.Run() run := m.Run()
@ -208,7 +211,7 @@ func TestMain(m *testing.M) {
} }
func TestNewClient(t *testing.T) { func TestNewClient(t *testing.T) {
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), minioClient, err := NewClient(ctx, bucketStub(bucket, testMinioAddress),
WithSecret(secret.DeepCopy()), WithSecret(secret.DeepCopy()),
WithTLSConfig(testTLSConfig)) WithTLSConfig(testTLSConfig))
assert.NilError(t, err) assert.NilError(t, err)
@ -216,35 +219,54 @@ func TestNewClient(t *testing.T) {
} }
func TestNewClientEmptySecret(t *testing.T) { func TestNewClientEmptySecret(t *testing.T) {
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), minioClient, err := NewClient(ctx, bucketStub(bucket, testMinioAddress),
WithSecret(emptySecret.DeepCopy()), WithSecret(emptySecret.DeepCopy()),
WithTLSConfig(testTLSConfig)) WithTLSConfig(testTLSConfig))
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, minioClient != nil) assert.Assert(t, minioClient != nil)
} }
func TestNewClientAwsProvider(t *testing.T) { func TestNewClientAWSProvider(t *testing.T) {
minioClient, err := NewClient(bucketStub(bucketAwsProvider, testMinioAddress)) t.Run("with secret", func(t *testing.T) {
assert.NilError(t, err) validSecret := corev1.Secret{
assert.Assert(t, minioClient != nil) ObjectMeta: v1.ObjectMeta{
Name: "valid-secret",
Namespace: "default",
},
Data: map[string][]byte{
"accesskey": []byte(testMinioRootUser),
"secretkey": []byte(testMinioRootPassword),
},
Type: "Opaque",
}
bucket := bucketStub(bucketAwsProvider, testMinioAddress)
minioClient, err := NewClient(ctx, bucket, WithSecret(&validSecret))
assert.NilError(t, err)
assert.Assert(t, minioClient != nil)
})
t.Run("without secret", func(t *testing.T) {
bucket := bucketStub(bucketAwsProvider, testMinioAddress)
minioClient, err := NewClient(ctx, bucket)
assert.ErrorContains(t, err, "AWS authentication failed")
assert.Assert(t, minioClient == nil)
})
} }
func TestBucketExists(t *testing.T) { func TestBucketExists(t *testing.T) {
ctx := context.Background()
exists, err := testMinioClient.BucketExists(ctx, bucketName) exists, err := testMinioClient.BucketExists(ctx, bucketName)
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, exists) assert.Assert(t, exists)
} }
func TestBucketNotExists(t *testing.T) { func TestBucketNotExists(t *testing.T) {
ctx := context.Background()
exists, err := testMinioClient.BucketExists(ctx, "notexistsbucket") exists, err := testMinioClient.BucketExists(ctx, "notexistsbucket")
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, !exists) assert.Assert(t, !exists)
} }
func TestFGetObject(t *testing.T) { func TestFGetObject(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir() tempDir := t.TempDir()
path := filepath.Join(tempDir, sourceignore.IgnoreFile) path := filepath.Join(tempDir, sourceignore.IgnoreFile)
_, err := testMinioClient.FGetObject(ctx, bucketName, objectName, path) _, err := testMinioClient.FGetObject(ctx, bucketName, objectName, path)
@ -252,41 +274,7 @@ func TestFGetObject(t *testing.T) {
} }
func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) { func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) {
// start a mock AWS STS server
awsSTSListener, awsSTSAddr, awsSTSPort := testlistener.New(t)
awsSTSEndpoint := fmt.Sprintf("http://%s", awsSTSAddr)
awsSTSHandler := http.NewServeMux()
awsSTSHandler.HandleFunc("PUT "+credentials.TokenPath,
func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("mock-token"))
assert.NilError(t, err)
})
awsSTSHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath,
func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get(credentials.TokenRequestHeader)
assert.Equal(t, token, "mock-token")
_, err := w.Write([]byte("mock-role"))
assert.NilError(t, err)
})
var credsRetrieved bool var credsRetrieved bool
awsSTSHandler.HandleFunc("GET "+credentials.DefaultIAMSecurityCredsPath+"mock-role",
func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get(credentials.TokenRequestHeader)
assert.Equal(t, token, "mock-token")
err := json.NewEncoder(w).Encode(map[string]any{
"Code": "Success",
"AccessKeyID": testMinioRootUser,
"SecretAccessKey": testMinioRootPassword,
})
assert.NilError(t, err)
credsRetrieved = true
})
awsSTSServer := &http.Server{
Addr: awsSTSAddr,
Handler: awsSTSHandler,
}
go awsSTSServer.Serve(awsSTSListener)
defer awsSTSServer.Shutdown(context.Background())
// start a mock LDAP STS server // start a mock LDAP STS server
ldapSTSListener, ldapSTSAddr, ldapSTSPort := testlistener.New(t) ldapSTSListener, ldapSTSAddr, ldapSTSPort := testlistener.New(t)
@ -313,7 +301,7 @@ func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) {
Handler: ldapSTSHandler, Handler: ldapSTSHandler,
} }
go ldapSTSServer.ServeTLS(ldapSTSListener, testServerCert, testServerKey) go ldapSTSServer.ServeTLS(ldapSTSListener, testServerCert, testServerKey)
defer ldapSTSServer.Shutdown(context.Background()) defer ldapSTSServer.Shutdown(ctx)
// start proxy // start proxy
proxyAddr, proxyPort := testproxy.New(t) proxyAddr, proxyPort := testproxy.New(t)
@ -327,42 +315,6 @@ func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) {
ldapPassword string ldapPassword string
err string err string
}{ }{
{
name: "with correct aws endpoint",
provider: "aws",
stsSpec: &sourcev1.BucketSTSSpec{
Provider: "aws",
Endpoint: awsSTSEndpoint,
},
},
{
name: "with incorrect aws endpoint",
provider: "aws",
stsSpec: &sourcev1.BucketSTSSpec{
Provider: "aws",
Endpoint: fmt.Sprintf("http://localhost:%d", awsSTSPort+1),
},
err: "connection refused",
},
{
name: "with correct aws endpoint and proxy",
provider: "aws",
stsSpec: &sourcev1.BucketSTSSpec{
Provider: "aws",
Endpoint: awsSTSEndpoint,
},
opts: []Option{WithProxyURL(&url.URL{Scheme: "http", Host: proxyAddr})},
},
{
name: "with correct aws endpoint and incorrect proxy",
provider: "aws",
stsSpec: &sourcev1.BucketSTSSpec{
Provider: "aws",
Endpoint: awsSTSEndpoint,
},
opts: []Option{WithProxyURL(&url.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", proxyPort+1)})},
err: "connection refused",
},
{ {
name: "with correct ldap endpoint", name: "with correct ldap endpoint",
provider: "generic", provider: "generic",
@ -448,11 +400,10 @@ func TestNewClientAndFGetObjectWithSTSEndpoint(t *testing.T) {
opts := tt.opts opts := tt.opts
opts = append(opts, WithTLSConfig(testTLSConfig)) opts = append(opts, WithTLSConfig(testTLSConfig))
minioClient, err := NewClient(bucket, opts...) minioClient, err := NewClient(ctx, bucket, opts...)
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, minioClient != nil) assert.Assert(t, minioClient != nil)
ctx := context.Background()
path := filepath.Join(t.TempDir(), sourceignore.IgnoreFile) path := filepath.Join(t.TempDir(), sourceignore.IgnoreFile)
_, err = minioClient.FGetObject(ctx, bucketName, objectName, path) _, err = minioClient.FGetObject(ctx, bucketName, objectName, path)
if tt.err != "" { if tt.err != "" {
@ -487,13 +438,12 @@ func TestNewClientAndFGetObjectWithProxy(t *testing.T) {
// run test // run test
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), minioClient, err := NewClient(ctx, bucketStub(bucket, testMinioAddress),
WithSecret(secret.DeepCopy()), WithSecret(secret.DeepCopy()),
WithTLSConfig(testTLSConfig), WithTLSConfig(testTLSConfig),
WithProxyURL(tt.proxyURL)) WithProxyURL(tt.proxyURL))
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, minioClient != nil) assert.Assert(t, minioClient != nil)
ctx := context.Background()
tempDir := t.TempDir() tempDir := t.TempDir()
path := filepath.Join(tempDir, sourceignore.IgnoreFile) path := filepath.Join(tempDir, sourceignore.IgnoreFile)
_, err = minioClient.FGetObject(ctx, bucketName, objectName, path) _, err = minioClient.FGetObject(ctx, bucketName, objectName, path)
@ -507,7 +457,6 @@ func TestNewClientAndFGetObjectWithProxy(t *testing.T) {
} }
func TestFGetObjectNotExists(t *testing.T) { func TestFGetObjectNotExists(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir() tempDir := t.TempDir()
badKey := "invalid.txt" badKey := "invalid.txt"
path := filepath.Join(tempDir, badKey) path := filepath.Join(tempDir, badKey)
@ -530,7 +479,6 @@ func TestVisitObjects(t *testing.T) {
} }
func TestVisitObjectsErr(t *testing.T) { func TestVisitObjectsErr(t *testing.T) {
ctx := context.Background()
badBucketName := "bad-bucket" badBucketName := "bad-bucket"
err := testMinioClient.VisitObjects(ctx, badBucketName, prefix, func(string, string) error { err := testMinioClient.VisitObjects(ctx, badBucketName, prefix, func(string, string) error {
return nil return nil

View File

@ -884,6 +884,14 @@ func (r *BucketReconciler) createBucketProvider(ctx context.Context, obj *source
authOpts = append(authOpts, auth.WithProxyURL(*creds.proxyURL)) authOpts = append(authOpts, auth.WithProxyURL(*creds.proxyURL))
} }
if obj.Spec.Region != "" {
authOpts = append(authOpts, auth.WithSTSRegion(obj.Spec.Region))
}
if sts := obj.Spec.STS; sts != nil {
authOpts = append(authOpts, auth.WithSTSEndpoint(sts.Endpoint))
}
switch obj.Spec.Provider { switch obj.Spec.Provider {
case sourcev1.BucketProviderGoogle: case sourcev1.BucketProviderGoogle:
var opts []gcp.Option var opts []gcp.Option
@ -933,6 +941,8 @@ func (r *BucketReconciler) createBucketProvider(ctx context.Context, obj *source
var opts []minio.Option var opts []minio.Option
if creds.secret != nil { if creds.secret != nil {
opts = append(opts, minio.WithSecret(creds.secret)) opts = append(opts, minio.WithSecret(creds.secret))
} else if obj.Spec.Provider == sourcev1.BucketProviderAmazon {
opts = append(opts, minio.WithAuth(authOpts...))
} }
if creds.tlsConfig != nil { if creds.tlsConfig != nil {
opts = append(opts, minio.WithTLSConfig(creds.tlsConfig)) opts = append(opts, minio.WithTLSConfig(creds.tlsConfig))
@ -946,7 +956,7 @@ func (r *BucketReconciler) createBucketProvider(ctx context.Context, obj *source
if creds.stsTLSConfig != nil { if creds.stsTLSConfig != nil {
opts = append(opts, minio.WithSTSTLSConfig(creds.stsTLSConfig)) opts = append(opts, minio.WithSTSTLSConfig(creds.stsTLSConfig))
} }
return minio.NewClient(obj, opts...) return minio.NewClient(ctx, obj, opts...)
} }
} }