@puls-atlas/cli
Version:
The Puls Atlas CLI tool for managing Atlas projects
167 lines • 7.09 kB
JavaScript
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);