Files
demo/release-environment.js

196 lines
5.5 KiB
JavaScript

'use strict';
const path = require('path');
const { statSync } = require('fs');
function requireRuntimeFile(environment, field, minimumSize, maximumSize) {
const file = String(environment[field] || '').trim();
if (!path.isAbsolute(file)) {
throw new Error(`${field} must be absolute`);
}
const stat = statSync(file, { throwIfNoEntry: false });
if (!stat?.isFile() || stat.size < minimumSize || stat.size > maximumSize) {
throw new Error(`${field} is unavailable or invalid`);
}
}
function requireExactReleaseEnvironment(environment = process.env) {
const runtimeEnvironment = String(environment.NODE_ENV || '')
.trim()
.toLowerCase();
const localOrTest = ['local', 'test'].includes(runtimeEnvironment);
const required =
runtimeEnvironment === 'prod' ||
runtimeEnvironment === 'production' ||
environment.RPC_REQUIRE_EXACT_RELEASE === 'true';
if (!required) return;
const fields = [
'RPC_SERVICE_VERSION',
'RPC_MIN_SCHEMA_VERSION',
'RPC_MAX_SCHEMA_VERSION',
'RPC_RELEASE_ID',
'RPC_RELEASE_VERSION',
'RPC_SCHEMA_CHECKSUM',
'RPC_ACTION_MANIFEST_CHECKSUM',
'RPC_RUNTIME_CONTRACT_CHECKSUM',
'RPC_IMPLEMENTATION_MANIFEST_CHECKSUM',
'RPC_ARTIFACT_DIGEST',
'BUILD_SHA',
'SAAS_CONTROL_PLANE_URL',
'SAAS_TENANT_CREDENTIALS_FILE',
];
const missing = fields.filter(
field => !String(environment[field] || '').trim()
);
if (missing.length) {
throw new Error(
`Exact SaaS Release environment is incomplete: ${missing.join(', ')}`
);
}
const bootstrapTicketFile = String(
environment.SAAS_RUNTIME_BOOTSTRAP_TOKEN_FILE || ''
).trim();
const bootstrapStateFile = String(
environment.SAAS_RUNTIME_BOOTSTRAP_STATE_FILE || ''
).trim();
const configuredIdentityReady = [
'RPC_INSTANCE_ID',
'SAAS_RUNTIME_NODE_ID',
'SAAS_SERVICE_TOKEN_FILE',
].every(field => String(environment[field] || '').trim());
if (
String(environment.SAAS_RUNTIME_BOOTSTRAP_TOKEN || '').trim() &&
['prod', 'production'].includes(runtimeEnvironment)
) {
throw new Error(
'Production SaaS runtime bootstrap tokens must be mounted as a file'
);
}
if (
!bootstrapTicketFile &&
!bootstrapStateFile &&
!(localOrTest && configuredIdentityReady)
) {
throw new Error(
'Strict SaaS runtime identity requires an authenticated orchestrator bootstrap ticket/state; configured RPC_INSTANCE_ID and SAAS_RUNTIME_NODE_ID are local/test migration compatibility only'
);
}
if (
environment.RPC_MODULE_CODE &&
!/^[a-z][a-z0-9_]{0,62}$/.test(environment.RPC_MODULE_CODE)
) {
throw new Error('RPC_MODULE_CODE is invalid');
}
if (
environment.RPC_INSTANCE_ID &&
!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
environment.RPC_INSTANCE_ID
)
) {
throw new Error('RPC_INSTANCE_ID must be a UUID');
}
const releaseId = Number(environment.RPC_RELEASE_ID);
const minSchema = Number(environment.RPC_MIN_SCHEMA_VERSION);
const maxSchema = Number(environment.RPC_MAX_SCHEMA_VERSION);
const runtimeNodeId = environment.SAAS_RUNTIME_NODE_ID
? Number(environment.SAAS_RUNTIME_NODE_ID)
: undefined;
if (
!Number.isSafeInteger(releaseId) ||
releaseId < 1 ||
!Number.isSafeInteger(minSchema) ||
minSchema < 1 ||
minSchema !== maxSchema ||
(runtimeNodeId !== undefined &&
(!Number.isSafeInteger(runtimeNodeId) ||
runtimeNodeId < 0 ||
runtimeNodeId > 65_535))
) {
throw new Error('Exact SaaS Release numeric identity is invalid');
}
for (const field of [
'RPC_SCHEMA_CHECKSUM',
'RPC_ACTION_MANIFEST_CHECKSUM',
'RPC_RUNTIME_CONTRACT_CHECKSUM',
'RPC_IMPLEMENTATION_MANIFEST_CHECKSUM',
]) {
if (!/^[a-f0-9]{64}$/.test(environment[field])) {
throw new Error(`${field} must be a SHA-256 checksum`);
}
}
if (!/^sha256:[a-f0-9]{64}$/.test(environment.RPC_ARTIFACT_DIGEST)) {
throw new Error('RPC_ARTIFACT_DIGEST must be a sha256 digest');
}
if (!/^[0-9A-Za-z._-]{6,128}$/.test(environment.BUILD_SHA)) {
throw new Error('BUILD_SHA is invalid');
}
if (
!/^\d+\.\d+\.\d+(?:-rc\.\d+)?(?:\+[0-9A-Za-z.-]+)?$/.test(
environment.RPC_SERVICE_VERSION
)
) {
throw new Error('RPC_SERVICE_VERSION is invalid');
}
if (
!/^[0-9A-Za-z][0-9A-Za-z.+_-]{0,95}$/.test(
environment.RPC_RELEASE_VERSION
)
) {
throw new Error('RPC_RELEASE_VERSION is invalid');
}
let controlPlane;
try {
controlPlane = new URL(environment.SAAS_CONTROL_PLANE_URL);
} catch {
throw new Error('SAAS_CONTROL_PLANE_URL is invalid');
}
if (!['http:', 'https:'].includes(controlPlane.protocol)) {
throw new Error('SAAS_CONTROL_PLANE_URL is invalid');
}
if (
['prod', 'production'].includes(runtimeEnvironment) &&
controlPlane.protocol !== 'https:'
) {
throw new Error('Production SAAS_CONTROL_PLANE_URL must use HTTPS');
}
if (bootstrapTicketFile) {
requireRuntimeFile(
environment,
'SAAS_RUNTIME_BOOTSTRAP_TOKEN_FILE',
32,
16_384
);
}
if (bootstrapStateFile) {
requireRuntimeFile(
environment,
'SAAS_RUNTIME_BOOTSTRAP_STATE_FILE',
2,
1024 * 1024
);
}
if (environment.SAAS_SERVICE_TOKEN_FILE) {
requireRuntimeFile(
environment,
'SAAS_SERVICE_TOKEN_FILE',
32,
16_384
);
}
requireRuntimeFile(
environment,
'SAAS_TENANT_CREDENTIALS_FILE',
2,
1024 * 1024
);
}
module.exports = { requireExactReleaseEnvironment };