Skip to content

Commit a67eef4

Browse files
Slachaider-chat-bot
andcommitted
fix: refactor GCS Connect to avoid conflicting client options
Co-authored-by: aider (gemini/gemini-3-flash-preview) <[email protected]>
1 parent 86ea7f3 commit a67eef4

File tree

1 file changed

+64
-57
lines changed

1 file changed

+64
-57
lines changed

pkg/storage/gcs.go

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,20 @@ func (r rewriteTransport) RoundTrip(req *http.Request) (*http.Response, error) {
8383
// Connect - connect to GCS
8484
func (gcs *GCS) Connect(ctx context.Context) error {
8585
var err error
86-
clientOptions := make([]option.ClientOption, 0)
87-
clientOptions = append(clientOptions, option.WithTelemetryDisabled())
8886
endpoint := "https://storage.googleapis.com/storage/v1/"
89-
9087
if gcs.Config.Endpoint != "" {
9188
endpoint = gcs.Config.Endpoint
92-
clientOptions = append(clientOptions, option.WithEndpoint(endpoint))
9389
}
9490

91+
// 1. Build the credential option
92+
var credOption option.ClientOption
9593
if gcs.Config.CredentialsJSON != "" {
96-
clientOptions = append(clientOptions, option.WithCredentialsJSON([]byte(gcs.Config.CredentialsJSON)))
94+
credOption = option.WithCredentialsJSON([]byte(gcs.Config.CredentialsJSON))
9795
} else if gcs.Config.CredentialsJSONEncoded != "" {
9896
d, _ := base64.StdEncoding.DecodeString(gcs.Config.CredentialsJSONEncoded)
99-
clientOptions = append(clientOptions, option.WithCredentialsJSON(d))
97+
credOption = option.WithCredentialsJSON(d)
10098
} else if gcs.Config.CredentialsFile != "" {
101-
clientOptions = append(clientOptions, option.WithCredentialsFile(gcs.Config.CredentialsFile))
99+
credOption = option.WithCredentialsFile(gcs.Config.CredentialsFile)
102100
} else if gcs.Config.SAEmail != "" {
103101
ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
104102
TargetPrincipal: gcs.Config.SAEmail,
@@ -110,66 +108,75 @@ func (gcs *GCS) Connect(ctx context.Context) error {
110108
if err != nil {
111109
return errors.Wrap(err, "failed to create impersonation token source")
112110
}
113-
clientOptions = append(clientOptions, option.WithTokenSource(ts))
111+
credOption = option.WithTokenSource(ts)
114112
} else if gcs.Config.SkipCredentials {
115-
clientOptions = append(clientOptions, option.WithoutAuthentication())
113+
credOption = option.WithoutAuthentication()
116114
}
117115

118-
storageClientOptions := clientOptions
119-
120-
if gcs.Config.ForceHttp || gcs.Config.Debug {
121-
dialOptions := make([]option.ClientOption, len(clientOptions))
122-
copy(dialOptions, clientOptions)
123-
if gcs.Config.Endpoint == "" && !gcs.Config.SkipCredentials {
124-
dialOptions = append(dialOptions, option.WithScopes(storage.ScopeFullControl))
125-
}
116+
// 2. Build dial options for the HTTP client
117+
dialOptions := []option.ClientOption{option.WithTelemetryDisabled()}
118+
if gcs.Config.Endpoint != "" {
119+
dialOptions = append(dialOptions, option.WithEndpoint(endpoint))
120+
}
121+
if credOption != nil {
122+
dialOptions = append(dialOptions, credOption)
123+
}
124+
// Scopes are required when dialing manually, but conflict with NoAuth in newer library versions
125+
if !gcs.Config.SkipCredentials {
126+
dialOptions = append(dialOptions, option.WithScopes(storage.ScopeFullControl))
127+
}
126128

127-
var httpClient *http.Client
128-
if gcs.Config.ForceHttp {
129-
customTransport := &http.Transport{
130-
WriteBufferSize: 128 * 1024,
131-
Proxy: http.ProxyFromEnvironment,
132-
DialContext: (&net.Dialer{
133-
Timeout: 30 * time.Second,
134-
KeepAlive: 30 * time.Second,
135-
}).DialContext,
136-
MaxIdleConns: 1,
137-
MaxIdleConnsPerHost: 1,
138-
IdleConnTimeout: 90 * time.Second,
139-
TLSHandshakeTimeout: 10 * time.Second,
140-
ExpectContinueTimeout: 1 * time.Second,
141-
}
142-
// must set ForceAttemptHTTP2 to false so that when a custom TLSClientConfig
143-
// is provided Golang does not setup HTTP/2 transport
144-
customTransport.ForceAttemptHTTP2 = false
145-
customTransport.TLSClientConfig = &tls.Config{
146-
NextProtos: []string{"http/1.1"},
147-
}
148-
customRoundTripper := &rewriteTransport{base: customTransport}
149-
transport, err := googleHTTPTransport.NewTransport(ctx, customRoundTripper, dialOptions...)
150-
if err != nil {
151-
return errors.Wrap(err, "failed to create GCP transport")
152-
}
153-
httpClient = &http.Client{Transport: transport}
154-
} else {
155-
httpClient, _, err = googleHTTPTransport.NewClient(ctx, dialOptions...)
156-
if err != nil {
157-
return errors.Wrap(err, "googleHTTPTransport.NewClient error")
158-
}
129+
// 3. Create the HTTP client
130+
var httpClient *http.Client
131+
if gcs.Config.ForceHttp {
132+
customTransport := &http.Transport{
133+
WriteBufferSize: 128 * 1024,
134+
Proxy: http.ProxyFromEnvironment,
135+
DialContext: (&net.Dialer{
136+
Timeout: 30 * time.Second,
137+
KeepAlive: 30 * time.Second,
138+
}).DialContext,
139+
MaxIdleConns: 1,
140+
MaxIdleConnsPerHost: 1,
141+
IdleConnTimeout: 90 * time.Second,
142+
TLSHandshakeTimeout: 10 * time.Second,
143+
ExpectContinueTimeout: 1 * time.Second,
159144
}
160-
161-
if gcs.Config.Debug {
162-
httpClient.Transport = debugGCSTransport{base: httpClient.Transport}
145+
// must set ForceAttemptHTTP2 to false so that when a custom TLSClientConfig
146+
// is provided Golang does not setup HTTP/2 transport
147+
customTransport.ForceAttemptHTTP2 = false
148+
customTransport.TLSClientConfig = &tls.Config{
149+
NextProtos: []string{"http/1.1"},
163150
}
164-
storageClientOptions = []option.ClientOption{
165-
option.WithTelemetryDisabled(),
166-
option.WithHTTPClient(httpClient),
151+
customRoundTripper := &rewriteTransport{base: customTransport}
152+
// Use NewTransport to get an authenticated transport using dialOptions
153+
transport, err := googleHTTPTransport.NewTransport(ctx, customRoundTripper, dialOptions...)
154+
if err != nil {
155+
return errors.Wrap(err, "failed to create GCP transport")
167156
}
168-
if gcs.Config.Endpoint != "" {
169-
storageClientOptions = append(storageClientOptions, option.WithEndpoint(endpoint))
157+
httpClient = &http.Client{Transport: transport}
158+
} else {
159+
// Use NewClient to get an authenticated client using dialOptions
160+
httpClient, _, err = googleHTTPTransport.NewClient(ctx, dialOptions...)
161+
if err != nil {
162+
return errors.Wrap(err, "googleHTTPTransport.NewClient error")
170163
}
171164
}
172165

166+
if gcs.Config.Debug {
167+
httpClient.Transport = debugGCSTransport{base: httpClient.Transport}
168+
}
169+
170+
// 4. Build storage client options using our custom httpClient
171+
// Credentials and scopes are already handled by the httpClient.
172+
storageClientOptions := []option.ClientOption{
173+
option.WithHTTPClient(httpClient),
174+
option.WithTelemetryDisabled(),
175+
}
176+
if gcs.Config.Endpoint != "" {
177+
storageClientOptions = append(storageClientOptions, option.WithEndpoint(endpoint))
178+
}
179+
173180
factory := pool.NewPooledObjectFactorySimple(
174181
func(context.Context) (interface{}, error) {
175182
sClient, err := storage.NewClient(ctx, storageClientOptions...)

0 commit comments

Comments
 (0)