Skip to content

Commit 63c48a6

Browse files
authored
feat(transport): add support for setting quota project with envvar (#1892)
Fixes: https://github.com/googleapis/google-api-go-client/issues/1891
1 parent 225fa6b commit 63c48a6

File tree

4 files changed

+70
-25
lines changed

4 files changed

+70
-25
lines changed

internal/creds.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"io/ioutil"
1414
"net"
1515
"net/http"
16+
"os"
1617
"time"
1718

1819
"golang.org/x/oauth2"
@@ -21,6 +22,8 @@ import (
2122
"golang.org/x/oauth2/google"
2223
)
2324

25+
const quotaProjectEnvVar = "GOOGLE_CLOUD_QUOTA_PROJECT"
26+
2427
// Creds returns credential information obtained from DialSettings, or if none, then
2528
// it returns default credential information.
2629
func Creds(ctx context.Context, ds *DialSettings) (*google.Credentials, error) {
@@ -152,14 +155,22 @@ func selfSignedJWTTokenSource(data []byte, ds *DialSettings) (oauth2.TokenSource
152155
}
153156
}
154157

155-
// QuotaProjectFromCreds returns the quota project from the JSON blob in the provided credentials.
156-
//
157-
// NOTE(cbro): consider promoting this to a field on google.Credentials.
158-
func QuotaProjectFromCreds(cred *google.Credentials) string {
158+
// GetQuotaProject retrieves quota project with precedence being: client option,
159+
// environment variable, creds file.
160+
func GetQuotaProject(creds *google.Credentials, clientOpt string) string {
161+
if clientOpt != "" {
162+
return clientOpt
163+
}
164+
if env := os.Getenv(quotaProjectEnvVar); env != "" {
165+
return env
166+
}
167+
if creds == nil {
168+
return ""
169+
}
159170
var v struct {
160171
QuotaProject string `json:"quota_project_id"`
161172
}
162-
if err := json.Unmarshal(cred.JSON, &v); err != nil {
173+
if err := json.Unmarshal(creds.JSON, &v); err != nil {
163174
return ""
164175
}
165176
return v.QuotaProject

internal/creds_test.go

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package internal
66

77
import (
88
"context"
9+
"os"
910
"testing"
1011

1112
"github.com/google/go-cmp/cmp"
@@ -199,10 +200,9 @@ const validServiceAccountJSON = `{
199200
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dumba-504%40appspot.gserviceaccount.com"
200201
}`
201202

202-
func TestQuotaProjectFromCreds(t *testing.T) {
203+
func TestGetQuotaProject(t *testing.T) {
203204
ctx := context.Background()
204-
205-
cred, err := credentialsFromJSON(
205+
emptyCred, err := credentialsFromJSON(
206206
ctx,
207207
[]byte(validServiceAccountJSON),
208208
&DialSettings{
@@ -212,17 +212,13 @@ func TestQuotaProjectFromCreds(t *testing.T) {
212212
if err != nil {
213213
t.Fatalf("got %v, wanted no error", err)
214214
}
215-
if want, got := "", QuotaProjectFromCreds(cred); want != got {
216-
t.Errorf("QuotaProjectFromCreds(validServiceAccountJSON): want %q, got %q", want, got)
217-
}
218-
219215
quotaProjectJSON := []byte(`
220216
{
221217
"type": "authorized_user",
222218
"quota_project_id": "foobar"
223219
}`)
224220

225-
cred, err = credentialsFromJSON(
221+
quotaCred, err := credentialsFromJSON(
226222
ctx,
227223
[]byte(quotaProjectJSON),
228224
&DialSettings{
@@ -232,8 +228,53 @@ func TestQuotaProjectFromCreds(t *testing.T) {
232228
if err != nil {
233229
t.Fatalf("got %v, wanted no error", err)
234230
}
235-
if want, got := "foobar", QuotaProjectFromCreds(cred); want != got {
236-
t.Errorf("QuotaProjectFromCreds(quotaProjectJSON): want %q, got %q", want, got)
231+
232+
tests := []struct {
233+
name string
234+
cred *google.Credentials
235+
clientOpt string
236+
env string
237+
want string
238+
}{
239+
{
240+
name: "empty all",
241+
cred: nil,
242+
want: "",
243+
},
244+
{
245+
name: "empty cred",
246+
cred: emptyCred,
247+
want: "",
248+
},
249+
{
250+
name: "from cred",
251+
cred: quotaCred,
252+
want: "foobar",
253+
},
254+
{
255+
name: "from opt",
256+
cred: quotaCred,
257+
clientOpt: "clientopt",
258+
want: "clientopt",
259+
},
260+
{
261+
name: "from env",
262+
cred: quotaCred,
263+
env: "envProject",
264+
want: "envProject",
265+
},
266+
}
267+
for _, tt := range tests {
268+
t.Run(tt.name, func(t *testing.T) {
269+
oldEnv := os.Getenv(quotaProjectEnvVar)
270+
if tt.env != "" {
271+
os.Setenv(quotaProjectEnvVar, tt.env)
272+
}
273+
if want, got := tt.want, GetQuotaProject(tt.cred, tt.clientOpt); want != got {
274+
t.Errorf("GetQuotaProject(%v, %q): want %q, got %q", tt.cred, tt.clientOpt, want, got)
275+
}
276+
os.Setenv(quotaProjectEnvVar, oldEnv)
277+
})
237278
}
238279
}
239280

transport/grpc/dial.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,10 @@ func dial(ctx context.Context, insecure bool, o *internal.DialSettings) (*grpc.C
154154
return nil, err
155155
}
156156

157-
if o.QuotaProject == "" {
158-
o.QuotaProject = internal.QuotaProjectFromCreds(creds)
159-
}
160-
161157
grpcOpts = append(grpcOpts,
162158
grpc.WithPerRPCCredentials(grpcTokenSource{
163159
TokenSource: oauth.TokenSource{creds.TokenSource},
164-
quotaProject: o.QuotaProject,
160+
quotaProject: internal.GetQuotaProject(creds, o.QuotaProject),
165161
requestReason: o.RequestReason,
166162
}),
167163
)

transport/http/dial.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ func newTransport(ctx context.Context, base http.RoundTripper, settings *interna
6565
paramTransport := &parameterTransport{
6666
base: base,
6767
userAgent: settings.UserAgent,
68-
quotaProject: settings.QuotaProject,
6968
requestReason: settings.RequestReason,
7069
}
7170
var trans http.RoundTripper = paramTransport
@@ -74,6 +73,7 @@ func newTransport(ctx context.Context, base http.RoundTripper, settings *interna
7473
case settings.NoAuth:
7574
// Do nothing.
7675
case settings.APIKey != "":
76+
paramTransport.quotaProject = internal.GetQuotaProject(nil, settings.QuotaProject)
7777
trans = &transport.APIKey{
7878
Transport: trans,
7979
Key: settings.APIKey,
@@ -83,10 +83,7 @@ func newTransport(ctx context.Context, base http.RoundTripper, settings *interna
8383
if err != nil {
8484
return nil, err
8585
}
86-
if paramTransport.quotaProject == "" {
87-
paramTransport.quotaProject = internal.QuotaProjectFromCreds(creds)
88-
}
89-
86+
paramTransport.quotaProject = internal.GetQuotaProject(creds, settings.QuotaProject)
9087
ts := creds.TokenSource
9188
if settings.ImpersonationConfig == nil && settings.TokenSource != nil {
9289
ts = settings.TokenSource

0 commit comments

Comments
 (0)