@elastic/synthetics
Version:
Elastic synthetic monitoring agent
202 lines (185 loc) • 5.85 kB
text/typescript
/**
* 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.
*
*/
import semver from 'semver';
import { progress, removeTrailingSlash } from '../helpers';
import { green, red, grey, yellow, Colorize, bold } from 'kleur/colors';
import { PushOptions } from '../common_types';
import { Monitor } from '../dsl/monitor';
export function logDiff<T extends Set<string>>(
newIDs: T,
changedIDs: T,
removedIDs: T,
unchangedIDs: T
) {
progress(
'Monitor Diff: ' +
green(`Added(${newIDs.size}) `) +
yellow(`Updated(${changedIDs.size}) `) +
red(`Removed(${removedIDs.size}) `) +
grey(`Unchanged(${unchangedIDs.size})`)
);
}
export function logGroups<T extends Set<string>>(
sizes: Map<string, number>,
newIDs: T,
changedIDs: T,
removedIDs: T,
unchangedIDs: T
) {
console.groupCollapsed();
logGroup(sizes, 'Added', newIDs, green);
logGroup(sizes, 'Updated', changedIDs, yellow);
logGroup(sizes, 'Removed', removedIDs, red);
logGroup(sizes, 'Unchanged', unchangedIDs, grey);
console.groupEnd();
}
function logGroup(
sizes: Map<string, number>,
name: string,
ids: Set<string>,
color: Colorize
) {
if (ids.size === 0) return;
// under collapsed group, so giving 2 space for padding
printLine(process.stdout.columns - 2);
console.groupCollapsed(color(bold(name)));
[...ids].forEach(id => {
console.log(grey(`- ${id} (${printBytes(sizes.get(id))})`));
});
console.groupEnd();
}
function printLine(length: number = process.stdout.columns) {
console.log(grey('-').repeat(length));
}
const BYTE_UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
export function printBytes(bytes: number) {
if (bytes <= 0) return '0 B';
const exponent = Math.min(
Math.floor(Math.log10(bytes) / 3),
BYTE_UNITS.length - 1
);
bytes /= 1000 ** exponent;
return `${bytes.toFixed(1)} ${BYTE_UNITS[exponent]}`;
}
export function getBatches(arr: string[], size: number): string[][] {
const batches = [];
for (let i = 0; i < arr.length; i += size) {
batches.push(arr.slice(i, i + size));
}
return batches;
}
export function getSizedBatches<T extends { id?: string }>(
arr: Array<T>,
sizes: Map<string, number>,
maxBatchSizeKB: number,
maxBatchItems: number
): Array<T[]> {
const batches: Array<T[]> = [];
let currentBatch: T[] = [];
let currentSize = 0;
for (const item of arr) {
const sizeKB = item.id ? Math.round(sizes.get(item.id) / 1000) : 1;
// If adding the current item would exceed limits, create a new chunk
if (
currentBatch.length >= maxBatchItems ||
currentSize + sizeKB > maxBatchSizeKB
) {
batches.push(currentBatch);
currentBatch = [];
currentSize = 0;
}
currentBatch.push(item);
currentSize += sizeKB;
}
// Push the last chunk if it contains any items
if (currentBatch.length > 0) {
batches.push(currentBatch);
}
return batches;
}
type Operation =
| 'status'
| 'bulk_get'
| 'bulk_update'
| 'bulk_delete'
| 'legacy'
| 'location';
export function generateURL(options: PushOptions, operation: Operation) {
const url = removeTrailingSlash(options.url);
switch (operation) {
case 'status':
return `${url}/s/${options.space}/api/stats`;
case 'bulk_get':
case 'bulk_update':
case 'bulk_delete':
return `${url}/s/${options.space}/api/synthetics/project/${options.id}/monitors`;
case 'legacy':
return `${url}/s/${options.space}/api/synthetics/service/project/monitors`;
case 'location':
return `${url}/internal/uptime/service/locations`;
default:
throw new Error('Invalid operation');
}
}
/**
* Bulk API is supported for push monitors only from 8.6.0 and above
*/
export function isBulkAPISupported(version: string) {
return semver.satisfies(version, '>=8.6.0');
}
/**
* Lightweight monitors are supported only from 8.5.0 and above
*/
export function isLightweightMonitorSupported(
monitors: Monitor[],
options: PushOptions
) {
const version = options.kibanaVersion;
return (
semver.satisfies(version, '<8.5.0') &&
monitors.some(monitor => monitor.type !== 'browser')
);
}
/**
* Lighweight monitor param options are supported only from
* 8.7.2 and above
*/
export function isParamOptionSupported(version: string) {
return semver.satisfies(version, '>=8.7.2');
}
/**
* Helper that replaces url paths traversal issues when bundling
*/
export function normalizeMonitorName(p: string, replacement = '_') {
// replace encoded and non encoded dots
p = p.replace(/%2e|\./gi, replacement);
// encoded slashes
p = p.replace(/%2f|%5c/gi, replacement);
// backslashes
p = p.replace(/[/\\]+/g, replacement);
// remove colons
p = p.replace(/[:]+/g, replacement);
return p;
}