UNPKG

@puls-atlas/cli

Version:

The Puls Atlas CLI tool for managing Atlas projects

148 lines 6.56 kB
import { execFileSync as nodeExecFileSync } from 'child_process'; import { hasScheduledSearchSources } from './scheduled.js'; import { normalizeOptionalString } from '../../../utils/value.js'; import { SEARCH_SOURCE_RUNNER_JOB_NAME } from '../resourceNames.js'; import { resolveSearchRuntimeContract } from '../planning.js'; import { resolveSearchCloudRunDeployConfig, resolveSearchTaskQueueConfig } from '../deploymentConfig.js'; import { PULS_ATLAS_DEFAULT_REGION_ENV, PULS_ATLAS_PROJECT_ID_ENV, PULS_ATLAS_SEARCH_CONFIG_URI_ENV, PULS_ATLAS_SEARCH_INDEXES_ENV, PULS_ATLAS_SEARCH_PROVIDER_ENV, PULS_ATLAS_SEARCH_REINDEX_SESSION_ID_ENV, PULS_ATLAS_SEARCH_SOURCE_NAME_ENV, SEARCH_SYNC_TASK_LOCATION_ENV, SEARCH_SYNC_TASK_QUEUE_ENV } from '../runtimeEnv.js'; import { createGcloudRunJobExecuteCommand, getCommandErrorMessage, isGcloudResourceNotFoundError, runGcloudFileCommand } from '../../../utils/gcloud.js'; export const SEARCH_SOURCE_RUNNER_CONTAINER_NAME = 'runtime'; const toEnvironmentVariableMap = environmentVariables => Object.fromEntries(environmentVariables.map(environmentVariable => [environmentVariable.name, environmentVariable.value])); const toSecretEnvironmentVariableMap = secretEnvironmentVariables => Object.fromEntries(secretEnvironmentVariables.map(secretEnvironmentVariable => [secretEnvironmentVariable.env, { secret_name: secretEnvironmentVariable.secretName, version: secretEnvironmentVariable.version }])); const resolveSearchSourceRunnerEnvironmentVariables = context => { const cloudRunConfig = resolveSearchCloudRunDeployConfig(context); const taskQueueConfig = resolveSearchTaskQueueConfig(context); return [{ name: PULS_ATLAS_PROJECT_ID_ENV, required: true, source: 'atlas search apply', value: context.projectId }, { name: PULS_ATLAS_DEFAULT_REGION_ENV, required: true, source: 'atlas search apply', value: cloudRunConfig.region }, { name: PULS_ATLAS_SEARCH_PROVIDER_ENV, required: true, source: 'atlas search apply', value: context.config.provider }, { name: SEARCH_SYNC_TASK_LOCATION_ENV, required: true, source: 'atlas search apply', value: taskQueueConfig.location }, { name: SEARCH_SYNC_TASK_QUEUE_ENV, required: true, source: 'atlas search apply', value: taskQueueConfig.name }, { name: PULS_ATLAS_SEARCH_CONFIG_URI_ENV, required: true, source: 'atlas search apply', value: cloudRunConfig.runtimeConfigUri ?? '' }]; }; export const resolveSearchSourceRunnerJobDeployment = context => { if (!hasScheduledSearchSources(context.config)) { return null; } const cloudRunConfig = resolveSearchCloudRunDeployConfig(context); const runtimeContract = resolveSearchRuntimeContract(context); const runtimeSecretEnvironmentVariables = runtimeContract.requiredSecretEnvironmentVariables.filter(secretReference => secretReference.services?.includes(SEARCH_SOURCE_RUNNER_JOB_NAME)); return { image: cloudRunConfig.sourceRunnerJobImage, jobName: SEARCH_SOURCE_RUNNER_JOB_NAME, projectId: context.projectId, region: cloudRunConfig.region, runtimeEnvironmentVariables: toEnvironmentVariableMap(resolveSearchSourceRunnerEnvironmentVariables(context)), runtimeSecretEnvironmentVariables: toSecretEnvironmentVariableMap(runtimeSecretEnvironmentVariables), serviceAccountEmail: cloudRunConfig.serviceAccountEmail }; }; export const inspectSearchSourceRunnerJob = (context, options = {}) => { const { runCommand = nodeExecFileSync } = options; const deployment = resolveSearchSourceRunnerJobDeployment(context); if (!deployment) { return { exists: false, jobName: SEARCH_SOURCE_RUNNER_JOB_NAME, projectId: context.projectId, region: null }; } try { const resolvedJobName = runGcloudFileCommand(['run', 'jobs', 'describe', deployment.jobName, `--project=${deployment.projectId}`, `--region=${deployment.region}`, '--format="value(metadata.name)"'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }, runCommand).trim(); return { exists: resolvedJobName.length > 0, jobName: deployment.jobName, projectId: deployment.projectId, region: deployment.region }; } catch (error) { if (isGcloudResourceNotFoundError(error)) { return { exists: false, jobName: deployment.jobName, projectId: deployment.projectId, region: deployment.region }; } throw new Error(`Failed to inspect Atlas search source runner job ${deployment.jobName} in project ${context.projectId}. ` + `${normalizeOptionalString(getCommandErrorMessage(error)) ?? error.message}`); } }; export const ensureSearchSourceRunnerJobExists = (context, options = {}) => { const inspection = inspectSearchSourceRunnerJob(context, options); if (!inspection.exists) { throw new Error(`Atlas search source runner job ${inspection.jobName} was not found in project ${inspection.projectId}. ` + 'Run "atlas search apply" to deploy the source runner job before running "atlas search run source --source <name>".'); } return inspection; }; export const createSearchSourceRunnerJobExecutionCommand = ({ context, indexNames = [], reindexSessionId = null, sourceName }) => { const deployment = resolveSearchSourceRunnerJobDeployment(context); if (!deployment) { throw new Error('Atlas search source runner is not configured. Ensure at least one scheduled source is enabled in the search config.'); } const environmentVariables = { [PULS_ATLAS_SEARCH_SOURCE_NAME_ENV]: sourceName }; const normalizedIndexNames = [...new Set((Array.isArray(indexNames) ? indexNames : []).map(indexName => normalizeOptionalString(indexName)).filter(Boolean))]; const normalizedReindexSessionId = normalizeOptionalString(reindexSessionId); if (normalizedIndexNames.length > 0) { environmentVariables[PULS_ATLAS_SEARCH_INDEXES_ENV] = normalizedIndexNames.join(','); } if (normalizedReindexSessionId) { environmentVariables[PULS_ATLAS_SEARCH_REINDEX_SESSION_ID_ENV] = normalizedReindexSessionId; } return { environmentVariables, jobName: deployment.jobName, projectId: deployment.projectId, region: deployment.region, sourceName, ...createGcloudRunJobExecuteCommand({ environmentVariables, jobName: deployment.jobName, projectId: deployment.projectId, region: deployment.region }) }; }; export default { SEARCH_SOURCE_RUNNER_CONTAINER_NAME, resolveSearchSourceRunnerJobDeployment };