UNPKG

@puls-atlas/cli

Version:

The Puls Atlas CLI tool for managing Atlas projects

167 lines 7.09 kB
import * as features from '../../utils/feature.js'; import { compileSyncPlan } from './config/syncConfig.js'; import { resolveSyncCloudRunDeployConfig } from './deploymentConfig.js'; import { SYNC_BACKFILL_JOB_NAME, SYNC_SERVICE_NAME } from './resourceNames.js'; import { isGcloudResourceNotFoundError, loadAtlasFeatureCache, logger, runGcloudFileCommand } from '../../utils/index.js'; import { executeSyncAdminRequest } from './syncApiAdmin.js'; import { createSyncReadAliasEntryRows, createSyncReadAliasSummaryRows, createSyncSchemaPlanEntryRows, createSyncSchemaPlanSummaryRows } from './schemaPlan.js'; const queryCloudRunServiceStatus = (projectId, region, serviceName) => { try { const output = runGcloudFileCommand(['run', 'services', 'describe', serviceName, `--project=${projectId}`, `--region=${region}`, '--format=value(status.conditions[0].status)'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); const statusValue = String(output ?? '').trim(); return statusValue === 'True' ? 'serving' : statusValue ? 'degraded' : 'unknown'; } catch (error) { if (isGcloudResourceNotFoundError(error)) { return 'not-deployed'; } return 'unknown'; } }; const queryCloudRunJobStatus = (projectId, region, jobName) => { try { runGcloudFileCommand(['run', 'jobs', 'describe', jobName, `--project=${projectId}`, `--region=${region}`, '--format=value(name)'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); return 'deployed'; } catch (error) { if (isGcloudResourceNotFoundError(error)) { return 'not-deployed'; } return 'unknown'; } }; const countScheduledSources = config => Object.values(config?.sources ?? {}).filter(sourceConfig => typeof sourceConfig?.schedule === 'string' && sourceConfig.schedule.trim().length > 0).length; const createRemoteStatusRows = (context, remoteStatus) => { const deployConfig = resolveSyncCloudRunDeployConfig(context); const rows = [{ label: 'Cloud Run service', value: remoteStatus.service }, { label: 'Cloud Run backfill job', value: remoteStatus.backfillJob }, { label: 'Region', value: deployConfig.region }]; return rows; }; const createSyncStatusSummaryRows = (context, syncPlan, cacheArtifact) => { const totalWorkloads = Object.keys(context.config?.workloads ?? {}).length; const enabledWorkloads = syncPlan.pipelines.length; const cachedReadAliasPlan = cacheArtifact?.cache?.readAliasPlan ?? null; const cachedSchemaPlan = cacheArtifact?.cache?.schemaPlan ?? null; return [{ label: 'Project', value: context.projectId }, context.environment ? { label: 'Environment', value: context.environment } : null, { label: 'Version', value: context.config.version }, { label: 'Workloads', value: totalWorkloads }, { label: 'Enabled workloads', value: enabledWorkloads }, { label: 'Disabled workloads', value: totalWorkloads - enabledWorkloads }, { label: 'Sources', value: Object.keys(context.config?.sources ?? {}).length }, { label: 'Destinations', value: Object.keys(context.config?.destinations ?? {}).length }, { label: 'Scheduled sources', value: countScheduledSources(context.config) }, { label: 'State namespace', value: context.config?.deploy?.syncState?.collectionPath ?? 'not configured' }, { label: 'Local cache', value: cacheArtifact ? `present (last cached: ${cacheArtifact.cachedAt ?? 'unknown'})` : 'not found' }, ...createSyncReadAliasSummaryRows(cachedReadAliasPlan), ...createSyncSchemaPlanSummaryRows(cachedSchemaPlan)]; }; export const reportSyncStatus = async (options, { compileSyncPlanImpl = compileSyncPlan, executeSyncAdminRequestImpl = executeSyncAdminRequest, loadAtlasFeatureCacheImpl = loadAtlasFeatureCache, loadFeatureContextImpl = features.loadFeatureContext, loggerImpl = logger, queryCloudRunServiceStatusImpl = queryCloudRunServiceStatus, queryCloudRunJobStatusImpl = queryCloudRunJobStatus, workingDirectory = process.cwd() } = {}) => { let spinner; try { const context = await loadFeatureContextImpl('sync', options, { cwd: workingDirectory }); spinner = loggerImpl.spinner('Loading Atlas sync status...'); const syncPlan = compileSyncPlanImpl(context.config); const cacheArtifact = loadAtlasFeatureCacheImpl('sync', context.projectId, workingDirectory); spinner.succeed('Atlas sync status loaded.'); loggerImpl.summary('Status summary', createSyncStatusSummaryRows(context, syncPlan, cacheArtifact)); if ((cacheArtifact?.cache?.schemaPlan?.entries?.length ?? 0) > 0) { loggerImpl.summary('Cached managed schema targets', createSyncSchemaPlanEntryRows(cacheArtifact.cache.schemaPlan)); } if ((cacheArtifact?.cache?.readAliasPlan?.entries?.length ?? 0) > 0) { loggerImpl.summary('Cached BigQuery read aliases', createSyncReadAliasEntryRows(cacheArtifact.cache.readAliasPlan)); } if (!options.localOnly) { const deployConfig = resolveSyncCloudRunDeployConfig(context); const remoteSpinner = loggerImpl.spinner('Checking remote Atlas sync resources...'); const remoteStatus = { service: queryCloudRunServiceStatusImpl(context.projectId, deployConfig.region, SYNC_SERVICE_NAME), backfillJob: queryCloudRunJobStatusImpl(context.projectId, deployConfig.region, SYNC_BACKFILL_JOB_NAME) }; remoteSpinner.succeed('Remote Atlas sync resource check complete.'); loggerImpl.summary('Remote resources', createRemoteStatusRows(context, remoteStatus)); if (remoteStatus.service === 'serving') { try { const failureSummary = await executeSyncAdminRequestImpl(context, { method: 'GET', path: '/admin/status/summary' }); const hasFailures = (failureSummary?.totalFailureCount ?? 0) > 0; loggerImpl.summary('Sync failure state', [{ label: 'Dead-letter items', value: failureSummary?.deadLetterCount ?? 0 }, { label: 'Terminal failures', value: failureSummary?.terminalCount ?? 0 }]); if (hasFailures) { loggerImpl.warning(`Atlas sync has ${failureSummary.totalFailureCount} failed item(s). Use "atlas sync repair" to requeue dead-letter items.`); } } catch {} } } loggerImpl.summary('Local files', [{ label: 'Config', value: context.configPath }, { label: 'Workload files', value: context.configPaths.length }]); if (context.configPaths.length > 0) { loggerImpl.section('Workload files', context.configPaths, { detailOnly: true }); } if (syncPlan.pipelines.length === 0) { loggerImpl.warning('Atlas sync config is valid, but no workloads are enabled for the selected project.'); } } catch (error) { spinner?.fail('Failed to load Atlas sync status.'); loggerImpl.error(error.message); } }; export default async options => reportSyncStatus(options);