UNPKG

@elastic/synthetics

Version:

Elastic synthetic monitoring agent

274 lines (271 loc) 12.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.warnIfThrottled = exports.pushLegacy = exports.catchIncorrectSettings = exports.validateSettings = exports.validatePush = exports.loadSettings = exports.formatDuplicateError = exports.push = void 0; /** * MIT License * * Copyright (c) 2020-present, Elastic NV * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ const promises_1 = require("fs/promises"); const enquirer_1 = require("enquirer"); const colors_1 = require("kleur/colors"); const monitor_1 = require("./monitor"); const monitor_2 = require("../dsl/monitor"); const helpers_1 = require("../helpers"); const config_1 = require("../config"); const kibana_api_1 = require("./kibana_api"); const utils_1 = require("./utils"); const run_local_1 = require("./run-local"); const globals_1 = require("../core/globals"); async function push(monitors, options) { if (parseInt(process.env.CHUNK_SIZE) > 250) { throw (0, helpers_1.error)('Invalid CHUNK_SIZE. CHUNK_SIZE must be less than or equal to 250'); } const duplicates = trackDuplicates(monitors); if (duplicates.size > 0) { throw (0, helpers_1.error)(formatDuplicateError(duplicates)); } (0, helpers_1.progress)(`Pushing monitors for '${options.id}' project in kibana '${options.space}' space`); /** * Legacy API for kibana which does not support bulk operations */ if (!(0, utils_1.isBulkAPISupported)(options.kibanaVersion)) { return await pushLegacy(monitors, options); } const { monitors: remote } = await (0, kibana_api_1.bulkGetMonitors)(options); (0, helpers_1.progress)(`preparing ${monitors.length} monitors`); const { schemas, sizes } = await (0, monitor_1.buildMonitorSchema)(monitors, true); const local = (0, monitor_1.getLocalMonitors)(schemas); const { newIDs, changedIDs, removedIDs, unchangedIDs } = (0, monitor_1.diffMonitors)(local, remote); (0, utils_1.logDiff)(newIDs, changedIDs, removedIDs, unchangedIDs); if ((0, globals_1.inDebugMode)()) { (0, utils_1.logGroups)(sizes, newIDs, changedIDs, removedIDs, unchangedIDs); // show bundle size for the whole project let totalSize = 0; for (const value of sizes.values()) { totalSize += value; } (0, helpers_1.progress)(`total size of the ${sizes.size} monitors payload is ` + (0, utils_1.printBytes)(totalSize)); } if (options.dryRun) { (0, helpers_1.progress)('Running browser monitors in dry run mode'); await (0, run_local_1.runLocal)(schemas); (0, helpers_1.progress)('Dry run completed'); return; } const updatedMonitors = new Set([...changedIDs, ...newIDs]); if (updatedMonitors.size > 0) { const updatedMonSchemas = schemas.filter(s => updatedMonitors.has(s.id)); const batches = (0, utils_1.getSizedBatches)(updatedMonSchemas, sizes, kibana_api_1.MAX_PAYLOAD_SIZE_KB, kibana_api_1.BATCH_SIZE); if (batches.length > 1) { (0, helpers_1.progress)(`Monitors will be pushed as ${batches.length} batches.`); } for (const batch of batches) { await (0, helpers_1.liveProgress)((0, kibana_api_1.bulkPutMonitors)(options, batch), `creating or updating ${batch.length} monitors`); } } if (removedIDs.size > 0) { if (updatedMonitors.size === 0 && unchangedIDs.size === 0) { await confirmDelete(`Pushing without any monitors will delete all monitors associated with the project.\n Do you want to continue?`, options.yes); } else { await confirmDelete(`Deleting ${removedIDs.size} monitors. Do you want to continue?`, options.yes); } const batches = (0, utils_1.getBatches)(Array.from(removedIDs), kibana_api_1.BATCH_SIZE); for (const batch of batches) { await (0, helpers_1.liveProgress)((0, kibana_api_1.bulkDeleteMonitors)(options, batch), `deleting ${batch.length} monitors`); } } (0, helpers_1.done)(`Pushed: ${(0, colors_1.underline)((0, helpers_1.getMonitorManagementURL)(options.url))} `); } exports.push = push; async function confirmDelete(message, skip) { const { deleteMonitors } = await (0, enquirer_1.prompt)({ type: 'confirm', skip() { (0, helpers_1.write)(''); if (skip || !process.stdout.isTTY) { this.initial = process.env.TEST_OVERRIDE ?? true; return true; } return false; }, name: 'deleteMonitors', message, initial: false, }); if (!deleteMonitors) { throw (0, helpers_1.warn)('Push command Aborted'); } } function trackDuplicates(monitors) { const monitorMap = new Map(); const duplicates = new Set(); for (const monitor of monitors) { const id = monitor.config.id; if (monitorMap.has(id)) { duplicates.add(monitorMap.get(id)); duplicates.add(monitor); } monitorMap.set(id, monitor); } return duplicates; } function formatDuplicateError(monitors) { let outer = (0, colors_1.bold)(`Aborted: Duplicate monitors found\n`); let inner = ''; for (const monitor of monitors) { const { config, source } = monitor; inner += `* ${config.id} - ${source.file}:${source.line}:${source.column}\n`; } outer += (0, helpers_1.indent)(inner); return outer; } exports.formatDuplicateError = formatDuplicateError; const INSTALLATION_HELP = `Run 'npx @elastic/synthetics init' to create project with default settings.`; async function loadSettings(configPath, ignoreMissing = false) { try { const config = await (0, config_1.readConfig)(process.env['NODE_ENV'] || 'development', configPath); // Missing config file, fake throw to capture as missing file if (Object.keys(config).length === 0) { throw ''; } return config.project || {}; } catch (e) { if (!ignoreMissing) { throw (0, helpers_1.error)(`Aborted (missing synthetics config file), Project not set up correctly. ${INSTALLATION_HELP}`); } } } exports.loadSettings = loadSettings; async function validatePush(opts, settings) { validateSettings(opts); await catchIncorrectSettings(settings, opts); } exports.validatePush = validatePush; function validateSettings(opts) { const INVALID = 'Aborted. Invalid synthetics project settings.'; let reason = ''; if (!opts.id) { reason = `Set project id via - CLI '--id <id>' - Config file 'project.id' field`; } else if (!opts.locations && !opts.privateLocations) { reason = `Set default location for all monitors via - CLI '--locations <values...> or --privateLocations <values...>' - Config file 'monitors.locations' | 'monitors.privateLocations' field`; } else if (!opts.schedule) { reason = `Set default schedule in minutes for all monitors via - CLI '--schedule <mins>' - Config file 'monitors.schedule' field`; } else if (opts.schedule && !monitor_2.ALLOWED_SCHEDULES.includes(opts.schedule)) { reason = `Set default schedule(${opts.schedule}) to one of the allowed values - ${monitor_2.ALLOWED_SCHEDULES.join(',')}`; } else if ((opts.locations ?? []).length > 0 && (opts?.playwrightOptions?.clientCertificates ?? []).filter(cert => { return cert.certPath || cert.keyPath || cert.pfxPath; }).length > 0) { reason = `Certificate path options (certPath, keyPath, pfxPath) are not supported on globally managed locations, use in-memory alternatives (cert, key, pfx) when running on cloud.`; } if (!reason) return; throw (0, helpers_1.error)(`${INVALID} ${reason} ${INSTALLATION_HELP}`); } exports.validateSettings = validateSettings; async function overrideSettings(configPath, oldValue, newValue) { const cwd = process.cwd(); configPath = configPath ?? (await (0, config_1.findSyntheticsConfig)(cwd, cwd)); if (!configPath) { throw (0, helpers_1.warn)(`Unable to find synthetics config file: ${configPath}`); } const config = await (0, promises_1.readFile)(configPath, 'utf-8'); const updatedConfig = config.replace(`id: '${oldValue}'`, `id: '${newValue}'`); await (0, promises_1.writeFile)(configPath, updatedConfig, 'utf-8'); } async function catchIncorrectSettings(settings, options) { let override = !settings.id; if (settings.id && settings.id !== options.id) { // Add an extra line to make it easier to read the prompt (0, helpers_1.write)(''); ({ override } = await (0, enquirer_1.prompt)({ type: 'confirm', name: 'override', skip() { if (options.yes) { this.initial = process.env.TEST_OVERRIDE ?? true; return true; } return false; }, message: `Monitors were pushed under the '${settings.id}' project. Are you sure you want to push them under the new '${options.id}' (note that this will duplicate the monitors, the old ones being orphaned)`, initial: false, })); if (!override) { throw (0, helpers_1.warn)('Push command Aborted'); } } if (override) { await overrideSettings(options.config, settings.id, options.id); } } exports.catchIncorrectSettings = catchIncorrectSettings; async function pushLegacy(monitors, options) { if ((0, utils_1.isLightweightMonitorSupported)(monitors, options)) { throw (0, helpers_1.error)(`Aborted: Lightweight monitors are not supported in ${options.kibanaVersion}. Please upgrade to 8.5.0 or above.`); } let schemas = []; let sizes = new Map(); if (monitors.length > 0) { (0, helpers_1.progress)(`preparing ${monitors.length} monitors`); ({ schemas, sizes } = await (0, monitor_1.buildMonitorSchema)(monitors, false)); const batches = (0, utils_1.getSizedBatches)(schemas, sizes, kibana_api_1.MAX_PAYLOAD_SIZE_KB, 10); if (batches.length > 1) { (0, helpers_1.progress)(`Monitors will be pushed as ${batches.length} batches.`); } for (const batch of batches) { await (0, helpers_1.liveProgress)((0, kibana_api_1.createMonitorsLegacy)({ schemas: batch, keepStale: true, options }), `creating or updating ${batch.length} monitors`); } } else { await confirmDelete(`Pushing without any monitors will delete all monitors associated with the project.\n Do you want to continue?`, options.yes); } await (0, helpers_1.liveProgress)((0, kibana_api_1.createMonitorsLegacy)({ schemas, keepStale: false, options }), `deleting all stale monitors`); (0, helpers_1.done)(`Pushed: ${(0, colors_1.underline)((0, helpers_1.getMonitorManagementURL)(options.url))}`); } exports.pushLegacy = pushLegacy; // prints warning if any of the monitors has throttling settings enabled during push function warnIfThrottled(monitors) { const throttled = monitors.some(monitor => monitor.config.throttling != null); if (throttled) { (0, helpers_1.warn)(helpers_1.THROTTLING_WARNING_MSG); } } exports.warnIfThrottled = warnIfThrottled; //# sourceMappingURL=index.js.map