Skip to content

Commit da66e44

Browse files
committed
Add support for .spec.proxySecretRef for generic provider of Bucket API
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
1 parent 59ad5a7 commit da66e44

File tree

9 files changed

+145
-10
lines changed

9 files changed

+145
-10
lines changed

api/v1beta2/bucket_types.go

+5
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ type BucketSpec struct {
100100
// +optional
101101
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
102102

103+
// ProxySecretRef specifies the Secret containing the proxy configuration
104+
// to use while communicating with the Bucket server.
105+
// +optional
106+
ProxySecretRef *meta.LocalObjectReference `json:"proxySecretRef,omitempty"`
107+
103108
// Interval at which the Bucket Endpoint is checked for updates.
104109
// This interval is approximate and may be subject to jitter to ensure
105110
// efficient use of resources.

api/v1beta2/zz_generated.deepcopy.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,17 @@ spec:
391391
- gcp
392392
- azure
393393
type: string
394+
proxySecretRef:
395+
description: |-
396+
ProxySecretRef specifies the Secret containing the proxy configuration
397+
to use while communicating with the Bucket server.
398+
properties:
399+
name:
400+
description: Name of the referent.
401+
type: string
402+
required:
403+
- name
404+
type: object
394405
region:
395406
description: Region of the Endpoint where the BucketName is located
396407
in.

docs/api/v1beta2/source.md

+30
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,21 @@ be of type <code>Opaque</code> or <code>kubernetes.io/tls</code>.</p>
191191
</tr>
192192
<tr>
193193
<td>
194+
<code>proxySecretRef</code><br>
195+
<em>
196+
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
197+
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
198+
</a>
199+
</em>
200+
</td>
201+
<td>
202+
<em>(Optional)</em>
203+
<p>ProxySecretRef specifies the Secret containing the proxy configuration
204+
to use while communicating with the Bucket server.</p>
205+
</td>
206+
</tr>
207+
<tr>
208+
<td>
194209
<code>interval</code><br>
195210
<em>
196211
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
@@ -1541,6 +1556,21 @@ be of type <code>Opaque</code> or <code>kubernetes.io/tls</code>.</p>
15411556
</tr>
15421557
<tr>
15431558
<td>
1559+
<code>proxySecretRef</code><br>
1560+
<em>
1561+
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
1562+
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
1563+
</a>
1564+
</em>
1565+
</td>
1566+
<td>
1567+
<em>(Optional)</em>
1568+
<p>ProxySecretRef specifies the Secret containing the proxy configuration
1569+
to use while communicating with the Bucket server.</p>
1570+
</td>
1571+
</tr>
1572+
<tr>
1573+
<td>
15441574
<code>interval</code><br>
15451575
<em>
15461576
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/distribution/distribution/v3 v3.0.0-alpha.1
2020
github.com/docker/cli v24.0.9+incompatible
2121
github.com/docker/go-units v0.5.0
22+
github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5
2223
github.com/fluxcd/cli-utils v0.36.0-flux.7
2324
github.com/fluxcd/pkg/apis/event v0.9.0
2425
github.com/fluxcd/pkg/apis/meta v1.5.0

go.sum

+3
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
311311
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
312312
github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5 h1:m62nsMU279qRD9PQSWD1l66kmkXzuYcnVJqL4XLeV2M=
313313
github.com/elazarl/goproxy v0.0.0-20231117061959-7cc037d33fb5/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
314+
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
315+
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
314316
github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
315317
github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
316318
github.com/emicklei/proto v1.12.1 h1:6n/Z2pZAnBwuhU66Gs8160B8rrrYKo7h2F2sCOnNceE=
@@ -831,6 +833,7 @@ github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0
831833
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
832834
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
833835
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
836+
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
834837
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
835838
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
836839
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=

internal/controller/bucket_controller.go

+32-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
stdtls "crypto/tls"
2222
"errors"
2323
"fmt"
24+
"net/url"
2425
"os"
2526
"path/filepath"
2627
"strings"
@@ -468,7 +469,13 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
468469
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Error())
469470
return sreconcile.ResultEmpty, e
470471
}
471-
if provider, err = minio.NewClient(obj, secret, tlsConfig); err != nil {
472+
proxyURL, err := r.getProxyURL(ctx, obj)
473+
if err != nil {
474+
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
475+
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Error())
476+
return sreconcile.ResultEmpty, e
477+
}
478+
if provider, err = minio.NewClient(obj, secret, tlsConfig, proxyURL); err != nil {
472479
e := serror.NewGeneric(err, "ClientError")
473480
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Error())
474481
return sreconcile.ResultEmpty, e
@@ -703,6 +710,30 @@ func (r *BucketReconciler) getTLSConfig(ctx context.Context, obj *bucketv1.Bucke
703710
return tlsConfig, nil
704711
}
705712

713+
func (r *BucketReconciler) getProxyURL(ctx context.Context, obj *bucketv1.Bucket) (*url.URL, error) {
714+
namespace := obj.GetNamespace()
715+
proxySecret, err := r.getSecret(ctx, obj.Spec.ProxySecretRef, namespace)
716+
if err != nil || proxySecret == nil {
717+
return nil, err
718+
}
719+
proxyData := proxySecret.Data
720+
address, ok := proxyData["address"]
721+
if !ok {
722+
return nil, fmt.Errorf("invalid proxy secret '%s/%s': key 'address' is missing",
723+
obj.Spec.ProxySecretRef.Name, namespace)
724+
}
725+
proxyURL, err := url.Parse(string(address))
726+
if err != nil {
727+
return nil, fmt.Errorf("failed to parse proxy address '%s': %w", address, err)
728+
}
729+
user, hasUser := proxyData["username"]
730+
password, hasPassword := proxyData["password"]
731+
if hasUser || hasPassword {
732+
proxyURL.User = url.UserPassword(string(user), string(password))
733+
}
734+
return proxyURL, nil
735+
}
736+
706737
// eventLogf records events, and logs at the same time.
707738
//
708739
// This log is different from the debug log in the EventRecorder, in the sense

pkg/minio/minio.go

+19-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"crypto/tls"
2222
"errors"
2323
"fmt"
24+
"net/http"
25+
"net/url"
2426

2527
"github.com/minio/minio-go/v7"
2628
"github.com/minio/minio-go/v7/pkg/credentials"
@@ -37,7 +39,9 @@ type MinioClient struct {
3739
}
3840

3941
// NewClient creates a new Minio storage client.
40-
func NewClient(bucket *sourcev1.Bucket, secret *corev1.Secret, tlsConfig *tls.Config) (*MinioClient, error) {
42+
func NewClient(bucket *sourcev1.Bucket, secret *corev1.Secret,
43+
tlsConfig *tls.Config, proxyURL *url.URL) (*MinioClient, error) {
44+
4145
opt := minio.Options{
4246
Region: bucket.Spec.Region,
4347
Secure: !bucket.Spec.Insecure,
@@ -61,15 +65,25 @@ func NewClient(bucket *sourcev1.Bucket, secret *corev1.Secret, tlsConfig *tls.Co
6165
opt.Creds = credentials.NewIAM("")
6266
}
6367

64-
if opt.Secure && tlsConfig != nil {
68+
secure := opt.Secure && tlsConfig != nil
69+
proxy := proxyURL != nil
70+
if secure || proxy {
6571
// Use the default minio transport, but override the TLS config.
66-
secure := false // true causes the TLS config to be defined internally, but here we have our own so we just pass false.
67-
transport, err := minio.DefaultTransport(secure)
72+
minioSecure := false // true causes the TLS config to be defined internally, but here we have our own so we just pass false.
73+
transport, err := minio.DefaultTransport(minioSecure)
6874
if err != nil {
6975
// The error returned here is always nil, but we keep the check for future compatibility.
7076
return nil, fmt.Errorf("failed to create default minio transport: %w", err)
7177
}
72-
transport.TLSClientConfig = tlsConfig.Clone()
78+
79+
if secure {
80+
transport.TLSClientConfig = tlsConfig.Clone()
81+
}
82+
83+
if proxy {
84+
transport.Proxy = http.ProxyURL(proxyURL)
85+
}
86+
7387
opt.Transport = transport
7488
}
7589

pkg/minio/minio_test.go

+39-4
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@ import (
2323
"errors"
2424
"fmt"
2525
"log"
26+
"net"
27+
"net/http"
28+
"net/url"
2629
"os"
2730
"path/filepath"
2831
"strings"
2932
"testing"
3033
"time"
3134

35+
"github.com/elazarl/goproxy"
3236
"github.com/google/uuid"
3337
miniov7 "github.com/minio/minio-go/v7"
3438
"github.com/ory/dockertest/v3"
@@ -64,6 +68,8 @@ var (
6468
testMinioClient *MinioClient
6569
// testTLSConfig is the TLS configuration used to connect to the Minio server.
6670
testTLSConfig *tls.Config
71+
// testProxyAddr is the address of the proxy server used to test against.
72+
testProxyAddr string
6773
)
6874

6975
var (
@@ -162,7 +168,7 @@ func TestMain(m *testing.M) {
162168
testMinioAddress = fmt.Sprintf("127.0.0.1:%v", resource.GetPort("9000/tcp"))
163169

164170
// Construct a Minio client using the address of the Minio server.
165-
testMinioClient, err = NewClient(bucketStub(bucket, testMinioAddress), secret.DeepCopy(), testTLSConfig)
171+
testMinioClient, err = NewClient(bucketStub(bucket, testMinioAddress), secret.DeepCopy(), testTLSConfig, nil)
166172
if err != nil {
167173
log.Fatalf("cannot create Minio client: %s", err)
168174
}
@@ -184,6 +190,22 @@ func TestMain(m *testing.M) {
184190
log.Fatalf("could not connect to docker: %s", err)
185191
}
186192

193+
// start proxy
194+
proxyHandler := goproxy.NewProxyHttpServer()
195+
proxyLis, err := net.Listen("tcp", ":0")
196+
if err != nil {
197+
log.Fatalf("could not start proxy server: %v", err)
198+
}
199+
defer proxyLis.Close()
200+
proxyPort := proxyLis.Addr().(*net.TCPAddr).Port
201+
testProxyAddr = fmt.Sprintf("localhost:%d", proxyPort)
202+
proxyServer := &http.Server{
203+
Addr: testProxyAddr,
204+
Handler: proxyHandler,
205+
}
206+
go proxyServer.Serve(proxyLis)
207+
defer proxyServer.Shutdown(context.Background())
208+
187209
ctx := context.Background()
188210
createBucket(ctx)
189211
addObjectToBucket(ctx)
@@ -195,19 +217,19 @@ func TestMain(m *testing.M) {
195217
}
196218

197219
func TestNewClient(t *testing.T) {
198-
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), secret.DeepCopy(), testTLSConfig)
220+
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), secret.DeepCopy(), testTLSConfig, nil)
199221
assert.NilError(t, err)
200222
assert.Assert(t, minioClient != nil)
201223
}
202224

203225
func TestNewClientEmptySecret(t *testing.T) {
204-
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), emptySecret.DeepCopy(), testTLSConfig)
226+
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), emptySecret.DeepCopy(), testTLSConfig, nil)
205227
assert.NilError(t, err)
206228
assert.Assert(t, minioClient != nil)
207229
}
208230

209231
func TestNewClientAwsProvider(t *testing.T) {
210-
minioClient, err := NewClient(bucketStub(bucketAwsProvider, testMinioAddress), nil, nil)
232+
minioClient, err := NewClient(bucketStub(bucketAwsProvider, testMinioAddress), nil, nil, nil)
211233
assert.NilError(t, err)
212234
assert.Assert(t, minioClient != nil)
213235
}
@@ -234,6 +256,19 @@ func TestFGetObject(t *testing.T) {
234256
assert.NilError(t, err)
235257
}
236258

259+
func TestNewClientAndFGetObjectWithProxy(t *testing.T) {
260+
proxyURL, err := url.Parse("http://" + testProxyAddr)
261+
assert.NilError(t, err)
262+
minioClient, err := NewClient(bucketStub(bucket, testMinioAddress), secret.DeepCopy(), testTLSConfig, proxyURL)
263+
assert.NilError(t, err)
264+
assert.Assert(t, minioClient != nil)
265+
ctx := context.Background()
266+
tempDir := t.TempDir()
267+
path := filepath.Join(tempDir, sourceignore.IgnoreFile)
268+
_, err = minioClient.FGetObject(ctx, bucketName, objectName, path)
269+
assert.NilError(t, err)
270+
}
271+
237272
func TestFGetObjectNotExists(t *testing.T) {
238273
ctx := context.Background()
239274
tempDir := t.TempDir()

0 commit comments

Comments
 (0)