@elastic/synthetics
Version:
Elastic synthetic monitoring agent
186 lines (171 loc) • 5.13 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 { createHash } from 'crypto';
import merge from 'deepmerge';
import { bold, red } from 'kleur/colors';
import {
ThrottlingOptions,
Location,
ScreenshotOptions,
Params,
PlaywrightOptions,
} from '../common_types';
import { indent, isMatch } from '../helpers';
import { LocationsMap } from '../locations/public-locations';
export type SyntheticsLocationsType = keyof typeof LocationsMap;
export const SyntheticsLocations = Object.keys(
LocationsMap
) as SyntheticsLocationsType[];
export const ALLOWED_SCHEDULES = [
1, 2, 3, 5, 10, 15, 20, 30, 60, 120, 240,
] as const;
export interface AlertConfig {
status?: {
enabled: boolean;
};
tls?: {
enabled: boolean;
};
}
export type MonitorConfig = {
id?: string;
name?: string;
type?: string;
tags?: string[];
fields?: Record<string, string>;
schedule?: typeof ALLOWED_SCHEDULES[number];
enabled?: boolean;
locations?: SyntheticsLocationsType[];
privateLocations?: string[];
/**
* @deprecated This option is ignored.
* Network throttling via chrome devtools is ignored at the moment.
* See https://github.com/elastic/synthetics/blob/main/docs/throttling.md for more details.
*/
throttling?: boolean | ThrottlingOptions;
screenshot?: ScreenshotOptions;
params?: Params;
playwrightOptions?: PlaywrightOptions;
alert?: AlertConfig;
/**
* By default, the monitor will be retested on failure
*/
retestOnFailure?: boolean;
};
type MonitorFilter = {
match: string;
tags?: string[];
};
export class Monitor {
content?: string;
source?: Location;
filter: MonitorFilter;
constructor(public config: MonitorConfig = {}) {}
/**
* Treat the creation time config with `monitor.use` as source of truth by
* merging the values coming from CLI and Synthetics config file
*/
update(globalOpts: MonitorConfig = {}) {
this.config = merge(globalOpts, this.config, {
arrayMerge(target, source) {
if (source && source.length > 0) {
return [...new Set(source)];
}
return target;
},
});
}
get type() {
return this.config.type;
}
setSource(source: Location) {
this.source = source;
}
/**
* The underlying journey code of the monitor
* along with its dependencies
*/
setContent(content = '') {
this.content = content;
}
/**
* If journey files are colocated within the same file during
* push command, when we invoke synthetics from HB we rely on
* this filter for running that specific journey alone instead of
* all journeys on the file
*/
setFilter(filter: MonitorFilter) {
this.filter = filter;
}
/**
* Matches monitors based on the provided args. Proitize tags over match
*/
isMatch(matchPattern: string, tagsPattern: Array<string>) {
return isMatch(
this.config.tags,
this.config.name,
tagsPattern,
matchPattern
);
}
/**
* Hash is used to identify if the monitor has changed since the last time
* it was pushed to Kibana. Change is based on three factors:
* - Monitor configuration
* - Code changes
* - File path changes
*/
hash(): string {
const hash = createHash('sha256');
return hash
.update(JSON.stringify(this.config))
.update(this.content || '')
.update(this.source?.file || '')
.digest('base64');
}
/**
* Returns the size of the monitor in bytes which is sent as payload to Kibana
*/
size() {
return JSON.stringify(this).length;
}
validate() {
const schedule = this.config.schedule;
if (ALLOWED_SCHEDULES.includes(schedule)) {
return;
}
const { config, source } = this;
let outer = bold(
`Invalid schedule: ${schedule}, allowed values are ${ALLOWED_SCHEDULES.join(
','
)}\n`
);
if (source) {
const inner = `* ${config.id} - ${source.file}:${source.line}:${source.column}\n`;
outer += indent(inner);
}
throw red(outer);
}
}