From 78f89eafdadacf8e106c80dc84c3169437923f77 Mon Sep 17 00:00:00 2001 From: Daniel Bloom Date: Tue, 19 Aug 2025 22:43:49 -0700 Subject: [PATCH] feat: resource autodetection for Cloud Run Jobs --- src/utils/metadata.ts | 37 +++++++++++++++++++++++++++++++++-- test/utils/metadata.ts | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/utils/metadata.ts b/src/utils/metadata.ts index bdfac421..a75d3949 100644 --- a/src/utils/metadata.ts +++ b/src/utils/metadata.ts @@ -87,6 +87,23 @@ export async function getCloudRunDescriptor() { }; } +/** + * Create a descriptor for Cloud Run Jpbs. + * + * @returns {object} + */ +export async function getCloudRunJobsDescriptor() { + const qualifiedZone = await gcpMetadata.instance('zone'); + const location = regionFromQualifiedZone(qualifiedZone); + return { + type: 'cloud_run_job', + labels: { + location, + job_name: process.env.CLOUD_RUN_JOB, + }, + }; +} + /** * Create a descriptor for Google App Engine. * @@ -196,7 +213,14 @@ export async function getDefaultResource(auth: GoogleAuth) { case GCPEnv.CLOUD_RUN: return getCloudRunDescriptor().catch(() => getGlobalDescriptor()); case GCPEnv.COMPUTE_ENGINE: - return getGCEDescriptor().catch(() => getGlobalDescriptor()); + // Note: GCPEnv.COMPUTE_ENGINE returns `true` for Google Cloud Run Jobs + // should use case when available + // case GCPEnv.CLOUD_RUN_JOBS: + if (process.env.CLOUD_RUN_JOB) { + return getCloudRunJobsDescriptor().catch(() => getGlobalDescriptor()); + } else { + return getGCEDescriptor().catch(() => getGlobalDescriptor()); + } default: return getGlobalDescriptor(); } @@ -235,7 +259,16 @@ export async function detectServiceContext( service: process.env.K_SERVICE, }; case GCPEnv.COMPUTE_ENGINE: - return null; + // Note: GCPEnv.COMPUTE_ENGINE returns `true` for Google Cloud Run Jobs + // should use case when available + // case GCPEnv.CLOUD_RUN_JOBS: + if (process.env.CLOUD_RUN_JOB) { + return { + service: process.env.CLOUD_RUN_JOB, + }; + } else { + return null; + } default: return null; } diff --git a/test/utils/metadata.ts b/test/utils/metadata.ts index 7f967ed2..10eac37e 100644 --- a/test/utils/metadata.ts +++ b/test/utils/metadata.ts @@ -218,6 +218,50 @@ describe('metadata', () => { }); }); + describe('getCloudRunJobsDescriptor', () => { + const CLOUD_RUN_JOB = 'hello-world'; + + const TARGET_KEYS = ['CLOUD_RUN_JOB']; + + before(() => { + for (const key of TARGET_KEYS) { + INITIAL_ENV[key] = process.env[key]; + } + }); + + after(() => { + for (const key of TARGET_KEYS) { + const val = INITIAL_ENV[key]; + if (val === undefined) { + delete process.env[key]; + } else { + process.env[key] = val; + } + } + }); + + beforeEach(() => { + for (const key of TARGET_KEYS) { + delete process.env[key]; + } + process.env.CLOUD_RUN_JOB = CLOUD_RUN_JOB; + }); + + it('should return the correct descriptor', async () => { + const ZONE_ID = 'cyrodiil-anvil-2'; + const ZONE_FULL = `projects/fake-project/zones/${ZONE_ID}`; + instanceOverride = {path: 'zone', successArg: ZONE_FULL}; + const descriptor = await metadata.getCloudRunJobsDescriptor(); + assert.deepStrictEqual(descriptor, { + type: 'cloud_run_job', + labels: { + job_name: CLOUD_RUN_JOB, + location: 'cyrodiil-anvil', + }, + }); + }); + }); + describe('getGAEDescriptor', () => { const GAE_MODULE_NAME = 'gae-module-name'; const GAE_SERVICE = 'gae-service';