UNPKG

zcatalyst-cli

Version:

Command Line Tool for CATALYST

256 lines (255 loc) 14.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.filterTargets = filterTargets; exports.validateAppSail = validateAppSail; const ansi_colors_1 = require("ansi-colors"); const error_1 = __importDefault(require("./error")); const appsail_1 = require("./util_modules/config/lib/appsail"); const constants_1 = require("./util_modules/constants"); const index_1 = require("./util_modules/logger/index"); const option_1 = require("./util_modules/option"); const container_1 = require("./util_modules/container"); const image_1 = require("@zcatalyst/container-plugin/out/endpoints/image"); const fs_1 = require("./util_modules/fs"); const util_1 = require("util"); const OCI_IMAGE_INDEX_MEDIA_TYPES = [ 'application/vnd.oci.image.index.v1+json', 'application/vnd.docker.distribution.manifest.list.v2+json' ]; const OCI_IMAGE_MANIFEST_MEDIA_TYPES = [ 'application/vnd.oci.image.manifest.v1+json', 'application/vnd.docker.distribution.manifest.v2+json' ]; function getBlobPathFromDigest(digest, sourceFile) { const [algorithm, hash] = digest.split(':'); if (!algorithm || !hash) { throw new error_1.default(`Corrupted image tar :: invalid digest ${digest} found in ${sourceFile}`, { exit: 1 }); } return `blobs/${algorithm}/${hash}`; } function getAppSailFilters(targetStr) { return targetStr.split(',').reduce((filterArr, target) => { const opts = target.split(':'); if (opts[0] === 'appsail' && opts[1]) { filterArr.push(opts[1]); return filterArr; } return filterArr; }, []); } function filterTargets(allTargets) { const only = (0, option_1.getOptionValue)('only'); if (only !== undefined) { const onlyTargs = getAppSailFilters(only); if (onlyTargs.length === 0) { return allTargets; } const refined = onlyTargs.reduce((filtered, filter) => { const targDetailsIdx = allTargets.findIndex((targ) => targ.name === filter); if (targDetailsIdx === -1) { filtered.unmatched.push(filter); } else { const targDetails = allTargets.splice(targDetailsIdx, 1); filtered.matched.push(targDetails[0]); } return filtered; }, { matched: [], unmatched: [] }); if (refined.unmatched.length > 0) { throw new error_1.default(`The filters ${(0, ansi_colors_1.bold)(refined.unmatched.join(', '))} provided with --only option does not match with any AppSail in ${constants_1.FILENAME.config}.`, { exit: 1 }); } return refined.matched; } const except = (0, option_1.getOptionValue)('except'); if (except !== undefined) { const exceptTargs = getAppSailFilters(except); const refined = exceptTargs.reduce((filtered, filter) => { const targDetailsIdx = allTargets.findIndex((targ) => targ.name === filter); if (targDetailsIdx === -1) { filtered.unmatched.push(filter); } else { filtered.matched.splice(targDetailsIdx, 1); } return filtered; }, { matched: allTargets, unmatched: [] }); if (refined.unmatched.length > 0) { (0, index_1.message)(`The filters ${(0, ansi_colors_1.bold)(refined.unmatched.join(', '))} provided with --except option does not match with any AppSail in ${constants_1.FILENAME.config}, hence ignored.`); } return refined.matched; } return allTargets; } function validateAppSail(targDetails) { return __awaiter(this, void 0, void 0, function* () { let isSocket; const getImageInfo = (target) => __awaiter(this, void 0, void 0, function* () { switch (true) { case target.source.startsWith(appsail_1.CONTAINER_IMAGE_PROTOCOLS.docker): { const imgTag = target.source.replace(appsail_1.CONTAINER_IMAGE_PROTOCOLS.docker, ''); return (0, image_1.inspectImage)(imgTag); } case target.source.startsWith(appsail_1.CONTAINER_IMAGE_PROTOCOLS.dockerArchive): { const imgTar = target.source.replace(appsail_1.CONTAINER_IMAGE_PROTOCOLS.dockerArchive, ''); const isFile = yield fs_1.ASYNC.fileExists(imgTar); if (!isFile) { throw new error_1.default('Invalid image tar path: ' + imgTar, { exit: 1 }); } return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var _a; try { const manifestJsonStream = yield (0, image_1.getTarEntry)(imgTar, 'manifest.json'); const repositoriesStream = yield (0, image_1.getTarEntry)(imgTar, 'repositories'); const indexJsonStream = yield (0, image_1.getTarEntry)(imgTar, 'index.json'); if (!indexJsonStream) { if (manifestJsonStream || repositoriesStream) { throw new error_1.default('Legacy docker-archive format is not supported. Only OCI-compliant docker-archive formats (pure OCI or hybrid OCI) are supported.', { exit: 1 }); } throw new error_1.default('Corrupted image tar :: required OCI metadata index.json not found in the tar', { exit: 1 }); } const indexJson = yield fs_1.ASYNC.readStreamAsJSON(indexJsonStream); if (indexJson.schemaVersion !== 2) { throw new error_1.default('Corrupted image tar :: invalid index.json file. Unsupported schemaVersion', { exit: 1 }); } const traversedDigests = new Set(); const resolveImageManifestPath = (manifestIndex, sourceFile) => __awaiter(this, void 0, void 0, function* () { var _a; const imageDescriptor = (_a = manifestIndex.manifests) === null || _a === void 0 ? void 0 : _a.find((manifest) => { if (!(manifest === null || manifest === void 0 ? void 0 : manifest.digest)) { return false; } if (!manifest.mediaType) { return true; } return (OCI_IMAGE_MANIFEST_MEDIA_TYPES.includes(manifest.mediaType) || OCI_IMAGE_INDEX_MEDIA_TYPES.includes(manifest.mediaType)); }); if (!(imageDescriptor === null || imageDescriptor === void 0 ? void 0 : imageDescriptor.digest)) { throw new error_1.default(`Corrupted image tar :: invalid ${sourceFile} file. Unable to identify image manifest`, { exit: 1 }); } if (traversedDigests.has(imageDescriptor.digest)) { throw new error_1.default(`Corrupted image tar :: circular descriptor reference found for digest ${imageDescriptor.digest}`, { exit: 1 }); } if (!imageDescriptor.mediaType || OCI_IMAGE_MANIFEST_MEDIA_TYPES.includes(imageDescriptor.mediaType)) { return getBlobPathFromDigest(imageDescriptor.digest, sourceFile); } traversedDigests.add(imageDescriptor.digest); const nestedIndexPath = getBlobPathFromDigest(imageDescriptor.digest, sourceFile); const nestedIndexStream = yield (0, image_1.getTarEntry)(imgTar, nestedIndexPath); if (!nestedIndexStream) { throw new error_1.default(`Corrupted image tar :: nested image index ${nestedIndexPath} not found in the tar`, { exit: 1 }); } const nestedIndex = yield fs_1.ASYNC.readStreamAsJSON(nestedIndexStream); if (nestedIndex.schemaVersion !== 2) { throw new error_1.default(`Corrupted image tar :: invalid nested image index ${nestedIndexPath}. Unsupported schemaVersion`, { exit: 1 }); } return resolveImageManifestPath(nestedIndex, nestedIndexPath); }); const imageManifestPath = yield resolveImageManifestPath(indexJson, 'index.json'); const imageManifestStream = yield (0, image_1.getTarEntry)(imgTar, imageManifestPath); if (!imageManifestStream) { throw new error_1.default(`Corrupted image tar :: image manifest ${imageManifestPath} not found in the tar`, { exit: 1 }); } const imageManifest = yield fs_1.ASYNC.readStreamAsJSON(imageManifestStream); if (imageManifest.schemaVersion !== 2) { throw new error_1.default('Corrupted image tar :: invalid image manifest file. Unsupported schemaVersion', { exit: 1 }); } const configDigest = (_a = imageManifest.config) === null || _a === void 0 ? void 0 : _a.digest; if (!configDigest) { throw new error_1.default('Corrupted image tar :: invalid image manifest file. Unable to identify the config blob', { exit: 1 }); } const configBlobPath = getBlobPathFromDigest(configDigest, imageManifestPath); const configFileStream = yield (0, image_1.getTarEntry)(imgTar, configBlobPath); if (!configFileStream) { throw new error_1.default(`Corrupted image tar :: config blob ${configBlobPath} not found in the tar`, { exit: 1 }); } const configFile = yield fs_1.ASYNC.readStreamAsJSON(configFileStream); resolve(configFile); } catch (er) { er instanceof error_1.default ? reject(er) : reject(error_1.default.getErrorInstance(er)); } })); } default: { throw new error_1.default('Unsupported image source: ' + target.source, { exit: 1 }); } } }); const _details = targDetails.map((targ) => __awaiter(this, void 0, void 0, function* () { var _a, _b; if (targ.validity.valid) { if (targ.runtime === 'custom') { if (isSocket === undefined) { isSocket = yield (0, container_1.isSocketAccessible)(false); } if (isSocket === false) { targ.validity = { valid: false, reason: 'ZCatalyst-CLI is unable to communicate with the Container runtime' }; return targ; } try { const imgInfo = (yield getImageInfo(targ)); const os = 'os' in imgInfo ? imgInfo.os : imgInfo.Os; if ((os === null || os === void 0 ? void 0 : os.toLowerCase()) !== 'linux') { targ.validity = { valid: false, reason: 'container image should be Linux based' }; return targ; } const arch = 'architecture' in imgInfo ? imgInfo.architecture : imgInfo.Architecture; if ((arch === null || arch === void 0 ? void 0 : arch.toLowerCase()) !== 'amd64') { targ.validity = { valid: false, reason: 'container image should be amd64 based' }; return targ; } } catch (er) { targ.validity = { valid: false, reason: er instanceof error_1.default ? er.message : (0, util_1.inspect)(er) }; } return targ; } if (((_a = targ.config) === null || _a === void 0 ? void 0 : _a.platform) !== 'war' && !((_b = targ.config) === null || _b === void 0 ? void 0 : _b.command)) { targ.validity = { valid: false, reason: 'Start-up command missing' }; return targ; } } return targ; })); return Promise.all(_details); }); }