Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Introduce environment variable for quota project #1478

Merged
merged 8 commits into from Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/auth/authclient.ts
Expand Up @@ -88,7 +88,11 @@ export abstract class AuthClient
extends EventEmitter
implements CredentialsClient
{
protected quotaProjectId?: string;
/**
* The quota project ID. The quota project can be used by client libraries for the billing purpose.
* See {@link https://cloud.google.com/docs/quota| Working with quotas}
*/
quotaProjectId?: string;
transporter = new DefaultTransporter();
credentials: Credentials = {};
projectId?: string | null;
Expand Down
44 changes: 29 additions & 15 deletions src/auth/googleauth.ts
Expand Up @@ -301,16 +301,18 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
private async getApplicationDefaultAsync(
options: RefreshOptions = {}
): Promise<ADCResponse> {
// If we've already got a cached credential, just return it.
// If we've already got a cached credential, return it.
// This will also preserve one's configured quota project, in case they
// set one directly on the credential previously.
if (this.cachedCredential) {
return {
credential: this.cachedCredential,
projectId: await this.getProjectIdOptional(),
};
return await this.prepareAndCacheADC(this.cachedCredential);
}

// Since this is a 'new' ADC to cache we will use the environment variable
// if it's available. We prefer this value over the value from ADC.
const quotaProjectIdOverride = process.env['GOOGLE_CLOUD_QUOTA_PROJECT'];

let credential: JSONClient | null;
let projectId: string | null;
// Check for the existence of a local environment variable pointing to the
// location of the credential file. This is typically used in local
// developer scenarios.
Expand All @@ -322,10 +324,8 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
} else if (credential instanceof BaseExternalAccountClient) {
credential.scopes = this.getAnyScopes();
}
this.cachedCredential = credential;
projectId = await this.getProjectIdOptional();

return {credential, projectId};
return await this.prepareAndCacheADC(credential, quotaProjectIdOverride);
}

// Look in the well-known credential file location.
Expand All @@ -338,9 +338,7 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
} else if (credential instanceof BaseExternalAccountClient) {
credential.scopes = this.getAnyScopes();
}
this.cachedCredential = credential;
projectId = await this.getProjectIdOptional();
return {credential, projectId};
return await this.prepareAndCacheADC(credential, quotaProjectIdOverride);
}

// Determine if we're running on GCE.
Expand All @@ -365,9 +363,25 @@ export class GoogleAuth<T extends AuthClient = JSONClient> {
// For GCE, just return a default ComputeClient. It will take care of
// the rest.
(options as ComputeOptions).scopes = this.getAnyScopes();
this.cachedCredential = new Compute(options);
projectId = await this.getProjectIdOptional();
return {projectId, credential: this.cachedCredential};
return await this.prepareAndCacheADC(
new Compute(options),
quotaProjectIdOverride
);
}

private async prepareAndCacheADC(
credential: JSONClient | Impersonated | Compute | T,
quotaProjectIdOverride?: string
): Promise<ADCResponse> {
const projectId = await this.getProjectIdOptional();

if (quotaProjectIdOverride) {
credential.quotaProjectId = quotaProjectIdOverride;
}

this.cachedCredential = credential;

return {credential, projectId};
}

/**
Expand Down
35 changes: 35 additions & 0 deletions test/test.googleauth.ts
Expand Up @@ -127,6 +127,7 @@ describe('googleauth', () => {
GCLOUD_PROJECT: undefined,
GOOGLE_APPLICATION_CREDENTIALS: undefined,
google_application_credentials: undefined,
GOOGLE_CLOUD_QUOTA_PROJECT: undefined,
HOME: path.join('/', 'fake', 'user'),
});
sandbox.stub(process, 'env').value(envVars);
Expand Down Expand Up @@ -1043,6 +1044,40 @@ describe('googleauth', () => {
assert.strictEqual(undefined, client.scope);
});

it('explicitly set quota project should not be overriden by environment value', async () => {
mockLinuxWellKnownFile(
'./test/fixtures/config-with-quota/.config/gcloud/application_default_credentials.json'
);
mockEnvVar('GOOGLE_CLOUD_QUOTA_PROJECT', 'quota_from_env');
let result = await auth.getApplicationDefault();
let client = result.credential as JWT;
assert.strictEqual('quota_from_env', client.quotaProjectId);

client.quotaProjectId = 'explicit_quota';
result = await auth.getApplicationDefault();
client = result.credential as JWT;
assert.strictEqual('explicit_quota', client.quotaProjectId);
});

it('getApplicationDefault should use quota project id from file if environment variable is empty', async () => {
mockLinuxWellKnownFile(
'./test/fixtures/config-with-quota/.config/gcloud/application_default_credentials.json'
);
mockEnvVar('GOOGLE_CLOUD_QUOTA_PROJECT', '');
const result = await auth.getApplicationDefault();
const client = result.credential as JWT;
assert.strictEqual('my-quota-project', client.quotaProjectId);
});

it('getApplicationDefault should use quota project id from file if environment variable is not set', async () => {
sai-sunder-s marked this conversation as resolved.
Show resolved Hide resolved
mockLinuxWellKnownFile(
'./test/fixtures/config-with-quota/.config/gcloud/application_default_credentials.json'
);
const result = await auth.getApplicationDefault();
const client = result.credential as JWT;
assert.strictEqual('my-quota-project', client.quotaProjectId);
});

it('getApplicationDefault should use GCE when well-known file and env const are not set', async () => {
// Set up the creds.
// * Environment variable is not set.
Expand Down