@deploystack/docker-to-iac
Version:
Translate docker run and docker compose file to Infrastructure as Code
135 lines (134 loc) • 5.19 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateRenderSchema = validateRenderSchema;
const _2020_1 = __importDefault(require("ajv/dist/2020"));
const ajv_formats_1 = __importDefault(require("ajv-formats"));
// Cache for the schema to avoid repeated fetching
let schemaCache = null;
/**
* Validates data against the Render.com schema
* @param data The YAML data to validate
* @param schemaUrl URL to the Render.com schema
* @returns Promise<boolean> indicating if validation passed
*/
async function validateRenderSchema(data, schemaUrl = 'https://render.com/schema/render.yaml.json') {
try {
// Fetch the schema if not already cached
if (!schemaCache) {
console.log('Fetching Render.com schema...');
const response = await fetch(schemaUrl, {
headers: {
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch schema: ${response.statusText} (${response.status})`);
}
schemaCache = await response.json();
console.log('Schema fetched successfully');
}
// Create an Ajv instance with support for 2020-12 draft
const ajv = new _2020_1.default({
allErrors: true,
verbose: true,
strict: false,
allowUnionTypes: true
});
// Add format validation
(0, ajv_formats_1.default)(ajv);
// First, try direct validation against the schema
try {
// Compile the schema
const validate = ajv.compile(schemaCache);
// Validate the data
const isValid = validate(data);
if (!isValid && validate.errors) {
console.error('❌ Schema validation failed:');
validate.errors.forEach((error, index) => {
console.error(`Error ${index + 1}:`, JSON.stringify(error, null, 2));
});
return false;
}
console.log('✓ Schema validation passed');
return true;
}
catch (validationError) {
// If we have a schema compilation issue, try manual validation of key structure
console.warn('Schema compilation error, falling back to structural validation:', validationError);
return validateStructure(data);
}
}
catch (error) {
console.error('❌ Schema validation error:', error);
return false;
}
}
/**
* Fallback validation that checks the structure of the Render YAML
* @param data Render YAML data
* @returns boolean indicating if basic structure is valid
*/
function validateStructure(data) {
try {
let isValid = true;
const issues = [];
// Check that services is an array
if (!data.services || !Array.isArray(data.services)) {
issues.push('services must be an array');
isValid = false;
}
else {
// Validate each service has the required fields
data.services.forEach((service, index) => {
if (!service.name) {
issues.push(`Service at index ${index} is missing a name`);
isValid = false;
}
if (!service.type) {
issues.push(`Service ${service.name || index} is missing a type`);
isValid = false;
}
if (service.type === 'web' && !service.envVars && !Array.isArray(service.envVars)) {
issues.push(`Service ${service.name || index} should have envVars as an array`);
// Not a critical error, so we don't set isValid to false
}
if (service.image && !service.image.url) {
issues.push(`Service ${service.name || index} has an image object but no url`);
isValid = false;
}
});
}
// Check databases if present
if (data.databases) {
if (!Array.isArray(data.databases)) {
issues.push('databases must be an array');
isValid = false;
}
else {
data.databases.forEach((db, index) => {
if (!db.name) {
issues.push(`Database at index ${index} is missing a name`);
isValid = false;
}
});
}
}
if (!isValid) {
console.error('❌ Structure validation failed:');
issues.forEach((issue, index) => {
console.error(` ${index + 1}. ${issue}`);
});
}
else {
console.log('✓ Structure validation passed');
}
return isValid;
}
catch (error) {
console.error('❌ Structure validation error:', error);
return false;
}
}