Skip to content

Commit 42a405b

Browse files
committed
Add proxy support for GCS buckets
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
1 parent 218af57 commit 42a405b

File tree

8 files changed

+80
-29
lines changed

8 files changed

+80
-29
lines changed

api/v1beta2/bucket_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ type BucketSpec struct {
103103
// ProxySecretRef specifies the Secret containing the proxy configuration
104104
// to use while communicating with the Bucket server.
105105
//
106-
// Only supported for the generic provider.
106+
// Only supported for the `generic` and `gcp` providers.
107107
// +optional
108108
ProxySecretRef *meta.LocalObjectReference `json:"proxySecretRef,omitempty"`
109109

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ spec:
397397
to use while communicating with the Bucket server.
398398
399399
400-
Only supported for the generic provider.
400+
Only supported for the `generic` and `gcp` providers.
401401
properties:
402402
name:
403403
description: Name of the referent.

docs/api/v1beta2/source.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
202202
<em>(Optional)</em>
203203
<p>ProxySecretRef specifies the Secret containing the proxy configuration
204204
to use while communicating with the Bucket server.</p>
205-
<p>Only supported for the generic provider.</p>
205+
<p>Only supported for the <code>generic</code> and <code>gcp</code> providers.</p>
206206
</td>
207207
</tr>
208208
<tr>
@@ -1568,7 +1568,7 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
15681568
<em>(Optional)</em>
15691569
<p>ProxySecretRef specifies the Secret containing the proxy configuration
15701570
to use while communicating with the Bucket server.</p>
1571-
<p>Only supported for the generic provider.</p>
1571+
<p>Only supported for the <code>generic</code> and <code>gcp</code> providers.</p>
15721572
</td>
15731573
</tr>
15741574
<tr>

docs/spec/v1beta2/buckets.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ The Secret can contain three keys:
837837
- `password`, to specify the password to use if the proxy server is protected by
838838
basic authentication. This is an optional key.
839839

840-
This API is only supported for the `generic` [provider](#provider).
840+
This API is only supported for the `generic` and `gcp` [providers](#provider).
841841

842842
Example:
843843

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ require (
6060
github.com/sirupsen/logrus v1.9.3
6161
github.com/spf13/pflag v1.0.5
6262
golang.org/x/crypto v0.22.0
63+
golang.org/x/oauth2 v0.19.0
6364
golang.org/x/sync v0.7.0
6465
google.golang.org/api v0.177.0
6566
gotest.tools v2.2.0+incompatible
@@ -360,7 +361,6 @@ require (
360361
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
361362
golang.org/x/mod v0.17.0 // indirect
362363
golang.org/x/net v0.24.0 // indirect
363-
golang.org/x/oauth2 v0.19.0 // indirect
364364
golang.org/x/sys v0.19.0 // indirect
365365
golang.org/x/term v0.19.0 // indirect
366366
golang.org/x/text v0.14.0 // indirect

internal/controller/bucket_controller.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,12 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
431431
// Return error as the world as observed may change
432432
return sreconcile.ResultEmpty, e
433433
}
434+
proxyURL, err := r.getProxyURL(ctx, obj)
435+
if err != nil {
436+
e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason)
437+
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Error())
438+
return sreconcile.ResultEmpty, e
439+
}
434440

435441
// Construct provider client
436442
var provider BucketProvider
@@ -441,7 +447,14 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
441447
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
442448
return sreconcile.ResultEmpty, e
443449
}
444-
if provider, err = gcp.NewClient(ctx, secret); err != nil {
450+
var opts []gcp.Option
451+
if secret != nil {
452+
opts = append(opts, gcp.WithSecret(secret))
453+
}
454+
if proxyURL != nil {
455+
opts = append(opts, gcp.WithProxyURL(proxyURL))
456+
}
457+
if provider, err = gcp.NewClient(ctx, opts...); err != nil {
445458
e := serror.NewGeneric(err, "ClientError")
446459
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
447460
return sreconcile.ResultEmpty, e
@@ -469,12 +482,6 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial
469482
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e)
470483
return sreconcile.ResultEmpty, e
471484
}
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-
}
478485
var opts []minio.Option
479486
if secret != nil {
480487
opts = append(opts, minio.WithSecret(secret))

pkg/gcp/gcp.go

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ import (
2121
"errors"
2222
"fmt"
2323
"io"
24+
"net/http"
25+
"net/url"
2426
"os"
2527
"path/filepath"
2628

2729
gcpstorage "cloud.google.com/go/storage"
2830
"github.com/go-logr/logr"
31+
"golang.org/x/oauth2/google"
2932
"google.golang.org/api/iterator"
3033
"google.golang.org/api/option"
34+
htransport "google.golang.org/api/transport/http"
3135
corev1 "k8s.io/api/core/v1"
3236
ctrl "sigs.k8s.io/controller-runtime"
3337
)
@@ -48,24 +52,64 @@ type GCSClient struct {
4852
*gcpstorage.Client
4953
}
5054

51-
// NewClient creates a new GCP storage client. The Client will automatically look for the Google Application
55+
// Option is a functional option for configuring the GCS client.
56+
type Option func(*options)
57+
58+
// WithSecret sets the secret to use for authenticating with GCP.
59+
func WithSecret(secret *corev1.Secret) Option {
60+
return func(o *options) {
61+
o.secret = secret
62+
}
63+
}
64+
65+
// WithProxyURL sets the proxy URL to use for the GCS client.
66+
func WithProxyURL(proxyURL *url.URL) Option {
67+
return func(o *options) {
68+
o.proxyURL = proxyURL
69+
}
70+
}
71+
72+
type options struct {
73+
secret *corev1.Secret
74+
proxyURL *url.URL
75+
}
76+
77+
// NewClient creates a new GCP storage client. The Client will automatically look for the Google Application
5278
// Credential environment variable or look for the Google Application Credential file.
53-
func NewClient(ctx context.Context, secret *corev1.Secret) (*GCSClient, error) {
54-
c := &GCSClient{}
79+
func NewClient(ctx context.Context, opts ...Option) (*GCSClient, error) {
80+
var o options
81+
for _, opt := range opts {
82+
opt(&o)
83+
}
84+
secret := o.secret
85+
proxyURL := o.proxyURL
86+
87+
var creds *google.Credentials
88+
var err error
5589
if secret != nil {
56-
client, err := gcpstorage.NewClient(ctx, option.WithCredentialsJSON(secret.Data["serviceaccount"]))
57-
if err != nil {
58-
return nil, err
59-
}
60-
c.Client = client
90+
creds, err = google.CredentialsFromJSON(ctx, secret.Data["serviceaccount"], gcpstorage.ScopeReadOnly)
6191
} else {
62-
client, err := gcpstorage.NewClient(ctx)
63-
if err != nil {
64-
return nil, err
65-
}
66-
c.Client = client
92+
creds, err = google.FindDefaultCredentials(ctx, gcpstorage.ScopeReadOnly)
93+
}
94+
if err != nil {
95+
return nil, fmt.Errorf("failed to get Google credentials: %w", err)
96+
}
97+
98+
baseTransport := http.DefaultTransport.(*http.Transport).Clone()
99+
if proxyURL != nil {
100+
baseTransport.Proxy = http.ProxyURL(proxyURL)
67101
}
68-
return c, nil
102+
transport, err := htransport.NewTransport(ctx, baseTransport, option.WithCredentials(creds))
103+
if err != nil {
104+
return nil, fmt.Errorf("failed to create Google HTTP transport: %w", err)
105+
}
106+
107+
client, err := gcpstorage.NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: transport}))
108+
if err != nil {
109+
return nil, err
110+
}
111+
112+
return &GCSClient{Client: client}, nil
69113
}
70114

71115
// ValidateSecret validates the credential secret. The provided Secret may

pkg/gcp/gcp_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ func TestMain(m *testing.M) {
140140
}
141141

142142
func TestNewClientWithSecretErr(t *testing.T) {
143-
gcpClient, err := NewClient(context.Background(), secret.DeepCopy())
143+
gcpClient, err := NewClient(context.Background(), WithSecret(secret.DeepCopy()))
144144
t.Log(err)
145-
assert.Error(t, err, "dialing: invalid character 'e' looking for beginning of value")
145+
assert.Error(t, err, "failed to get Google credentials: invalid character 'e' looking for beginning of value")
146146
assert.Assert(t, gcpClient == nil)
147147
}
148148

0 commit comments

Comments
 (0)