Skip to content

Commit

Permalink
fix(js/plugins/google-cloud): pass projectId through to GoogleAuth cl…
Browse files Browse the repository at this point in the history
…ient
  • Loading branch information
MichaelDoyle committed Feb 19, 2025
1 parent 3c3c1b6 commit b993d13
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 29 deletions.
34 changes: 22 additions & 12 deletions js/plugins/google-cloud/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,23 @@ import { GcpPrincipal, GcpTelemetryConfig } from './types.js';
* searches for credential files in standard locations, before using this
* method.
*
* See also: https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#loading-credentials-from-environment-variables
* @see https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#loading-credentials-from-environment-variables
*
* @param projectId if provided, will take precendence over projectId from credential
*/
export async function credentialsFromEnvironment(): Promise<
Partial<GcpTelemetryConfig>
> {
export async function credentialsFromEnvironment(
projectId?: string
): Promise<Partial<GcpTelemetryConfig>> {
let authClient: GoogleAuth;
let options: Partial<GcpTelemetryConfig> = {};

if (projectId !== undefined) {
logger.debug(`Using Google Cloud projectId=${projectId}`);
options.projectId = projectId;
}

if (process.env.GCLOUD_SERVICE_ACCOUNT_CREDS) {
logger.debug('Retrieving credentials from GCLOUD_SERVICE_ACCOUNT_CREDS');
logger.debug('Using credentials from GCLOUD_SERVICE_ACCOUNT_CREDS');
const serviceAccountCreds = JSON.parse(
process.env.GCLOUD_SERVICE_ACCOUNT_CREDS
);
Expand All @@ -48,14 +55,15 @@ export async function credentialsFromEnvironment(): Promise<
} else {
authClient = new GoogleAuth();
}

try {
const projectId = await authClient.getProjectId();
if (projectId && projectId.length > 0) {
options.projectId = projectId;
}
options.projectId ||= await authClient.getProjectId();
} catch (error) {
logger.warn(error);
}

logger.debug(`Final projectId=${options.projectId}`);

return options;
}

Expand All @@ -68,8 +76,10 @@ export async function credentialsFromEnvironment(): Promise<
* can be handy to get access to the current credential for logging debugging
* information or other purposes.
**/
export async function resolveCurrentPrincipal(): Promise<GcpPrincipal> {
const envCredentials = await credentialsFromEnvironment();
export async function resolveCurrentPrincipal(
projectId?: string
): Promise<GcpPrincipal> {
const envCredentials = await credentialsFromEnvironment(projectId);
let adcCredentials = {} as CredentialBody;
try {
adcCredentials = await auth.getCredentials();
Expand All @@ -83,7 +93,7 @@ export async function resolveCurrentPrincipal(): Promise<GcpPrincipal> {
envCredentials.credentials?.client_email ?? adcCredentials.client_email;

return {
projectId: envCredentials.projectId,
projectId: projectId ?? envCredentials.projectId,
serviceAccountEmail,
};
}
4 changes: 2 additions & 2 deletions js/plugins/google-cloud/src/gcpLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ export class GcpLogger {
transports.push(
this.shouldExport(env)
? new LoggingWinston({
projectId: this.config.projectId,
labels: { module: 'genkit' },
prefix: 'genkit',
logName: 'genkit_log',
projectId: this.config.projectId,
credentials: this.config.credentials,
autoRetry: true,
defaultCallback: await this.getErrorHandler(),
Expand All @@ -80,7 +80,7 @@ export class GcpLogger {
private async getErrorHandler(): Promise<(err: Error | null) => void> {
// only log the first time
let instructionsLogged = false;
let helpInstructions = await loggingDeniedHelpText();
let helpInstructions = await loggingDeniedHelpText(this.config.projectId);

return async (err: Error | null) => {
// Use the defaultLogger so that logs don't get swallowed by
Expand Down
14 changes: 8 additions & 6 deletions js/plugins/google-cloud/src/gcpOpenTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,9 @@ export class GcpOpenTelemetry {
spanExporter = new AdjustingTraceExporter(
this.shouldExportTraces()
? new TraceExporter({
// Creds for non-GCP environments; otherwise credentials will be
// automatically detected via ADC
// provided projectId should take precedence over env vars, etc
projectId: this.config.projectId,
// creds for non-GCP environments, in lieu of using ADC.
credentials: this.config.credentials,
})
: new InMemorySpanExporter(),
Expand All @@ -134,7 +135,7 @@ export class GcpOpenTelemetry {
(err) => {
return tracingDenied(err);
},
await tracingDeniedHelpText()
await tracingDeniedHelpText(this.config.projectId)
)
);
return spanExporter;
Expand Down Expand Up @@ -186,15 +187,16 @@ export class GcpOpenTelemetry {
product: 'genkit',
version: GENKIT_VERSION,
},
// Creds for non-GCP environments; otherwise credentials will be
// automatically detected via ADC
// provided projectId should take precedence over env vars, etc
projectId: this.config.projectId,
// creds for non-GCP environments, in lieu of using ADC.
credentials: this.config.credentials,
},
getErrorHandler(
(err) => {
return metricsDenied(err);
},
await metricsDeniedHelpText()
await metricsDeniedHelpText(this.config.projectId)
)
)
: new InMemoryMetricExporter(AggregationTemporality.DELTA);
Expand Down
2 changes: 1 addition & 1 deletion js/plugins/google-cloud/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function enableGoogleCloudTelemetry(
async function configureGcpPlugin(
options?: GcpTelemetryConfigOptions
): Promise<GcpTelemetryConfig> {
const envOptions = await credentialsFromEnvironment();
const envOptions = await credentialsFromEnvironment(options?.projectId);
return {
projectId: options?.projectId || envOptions.projectId,
credentials: options?.credentials || envOptions.credentials,
Expand Down
19 changes: 11 additions & 8 deletions js/plugins/google-cloud/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,22 @@ export function metricsDenied(
return requestDenied(err);
}

export async function permissionDeniedHelpText(role: string) {
const principal = await resolveCurrentPrincipal();
export async function permissionDeniedHelpText(
role: string,
projectId?: string
) {
const principal = await resolveCurrentPrincipal(projectId);
return `Add the role '${role}' to your Service Account in the IAM & Admin page on the Google Cloud console, or use the following command:\n\ngcloud projects add-iam-policy-binding ${principal.projectId ?? '${PROJECT_ID}'} \\\n --member=serviceAccount:${principal.serviceAccountEmail || '${SERVICE_ACCT}'} \\\n --role=${role}`;
}

export async function loggingDeniedHelpText() {
return permissionDeniedHelpText('roles/logging.logWriter');
export async function loggingDeniedHelpText(projectId?: string) {
return permissionDeniedHelpText('roles/logging.logWriter', projectId);
}

export async function tracingDeniedHelpText() {
return permissionDeniedHelpText('roles/cloudtrace.agent');
export async function tracingDeniedHelpText(projectId?: string) {
return permissionDeniedHelpText('roles/cloudtrace.agent', projectId);
}

export async function metricsDeniedHelpText() {
return permissionDeniedHelpText('roles/monitoring.metricWriter');
export async function metricsDeniedHelpText(projectId?: string) {
return permissionDeniedHelpText('roles/monitoring.metricWriter', projectId);
}

0 comments on commit b993d13

Please sign in to comment.