185 lines
6.7 KiB
JavaScript
185 lines
6.7 KiB
JavaScript
const slugify = require("slugify");
|
|
const _ = require("lodash");
|
|
const yaml = require('yaml');
|
|
|
|
module.exports = {
|
|
yaml: (doc) => {
|
|
let object = _.cloneDeep(doc);
|
|
let componentsForAppSpecComp = [];
|
|
let componentsForAppWideScope = [];
|
|
|
|
const protectedVariables = ["_id", "type", "metaType", "organization", "_edit", "_publish", "variables", "utilityFunctions", "resources"];
|
|
protectedVariables.forEach(p => {
|
|
delete object[p];
|
|
});
|
|
|
|
//Templates
|
|
if (object.templates) {
|
|
object.templates = object.templates.map(v => ({
|
|
id: v.id,
|
|
type: v.type,
|
|
range: [v.minValue, v.maxValue],
|
|
unit: v.unit
|
|
}));
|
|
}
|
|
|
|
object.metrics_comp = [];
|
|
object.metrics_global = [];
|
|
//Metrics
|
|
if (object.metrics) {
|
|
object.metrics.forEach(v => {
|
|
let metricsDetail = {};
|
|
|
|
if (v.type === 'composite') {
|
|
let windowDetail = {};
|
|
|
|
if (v.isWindowInput && v.input.type && v.input.interval && v.input.unit) {
|
|
windowDetail = {
|
|
type: v.input.type,
|
|
size: `${v.input.interval} ${v.input.unit}`,
|
|
};
|
|
}
|
|
|
|
// Only add windowDetail to metricsDetail if it's not empty
|
|
if (Object.keys(windowDetail).length > 0) {
|
|
metricsDetail.window = windowDetail;
|
|
}
|
|
|
|
let componentNames = v.components ? v.components.map(component => component.componentName) : [];
|
|
|
|
if (componentNames.length === 1) {
|
|
componentsForAppSpecComp.push(componentNames[0]);
|
|
} else if (componentNames.length > 1) {
|
|
componentsForAppWideScope = componentsForAppWideScope.concat(componentNames);
|
|
}
|
|
|
|
metricsDetail = {
|
|
name: v.name,
|
|
type: v.type,
|
|
template: v.template,
|
|
formula: v.formula,
|
|
...metricsDetail,
|
|
};
|
|
|
|
if (v.isWindowOutput && v.output.type && v.output.interval && v.output.unit) {
|
|
metricsDetail.output = `${v.output.type} ${v.output.interval} ${v.output.unit}`;
|
|
}
|
|
} else if (v.type === 'raw') {
|
|
let configs = v.config.map(configItem => ({
|
|
[configItem.name]: configItem.value
|
|
}));
|
|
|
|
let configObject = configs.reduce((acc, current) => ({ ...acc, ...current }), {});
|
|
|
|
metricsDetail = {
|
|
name: v.name,
|
|
type: v.type,
|
|
sensor: {
|
|
type: v.sensor,
|
|
config: configObject,
|
|
}
|
|
};
|
|
|
|
if (v.outputRaw && v.outputRaw.type && v.outputRaw.interval && v.outputRaw.unit) {
|
|
metricsDetail.output = `${v.outputRaw.type} ${v.outputRaw.interval} ${v.outputRaw.unit}`;
|
|
}
|
|
if (v.template) {
|
|
metricsDetail.template = v.template;
|
|
}
|
|
|
|
let componentNames = v.components ? v.components.map(component => component.componentName) : [];
|
|
|
|
if (componentNames.length === 1) {
|
|
componentsForAppSpecComp.push(componentNames[0]);
|
|
} else if (componentNames.length > 1) {
|
|
componentsForAppWideScope = componentsForAppWideScope.concat(componentNames);
|
|
}
|
|
}
|
|
|
|
if (v.components && v.components.length === 1) {
|
|
object.metrics_comp.push({...metricsDetail});
|
|
} else {
|
|
object.metrics_global.push({...metricsDetail});
|
|
}
|
|
});
|
|
}
|
|
|
|
//SLO Violations
|
|
if (object.sloViolations) {
|
|
const processSloViolations = (violations) => {
|
|
const buildConstraint = (v, parentCondition = '') => {
|
|
let constraint = '';
|
|
if (!v.isComposite) {
|
|
constraint = `${v.metricName} ${v.operator} ${v.value}`;
|
|
} else {
|
|
const childConstraints = v.children.map(child => buildConstraint(child, v.condition)).join(` ${v.condition} `);
|
|
|
|
constraint = v.not ? `NOT (${childConstraints})` : `(${childConstraints})`;
|
|
}
|
|
return constraint;
|
|
};
|
|
|
|
const combinedConstraint = buildConstraint(JSON.parse(doc['sloViolations']));
|
|
|
|
return [{
|
|
name: 'Combined SLO',
|
|
type: 'slo',
|
|
constraint: combinedConstraint
|
|
}];
|
|
};
|
|
|
|
object.sloViolations = processSloViolations(object.sloViolations);
|
|
}
|
|
|
|
let specContent = {
|
|
components: [
|
|
...componentsForAppSpecComp,
|
|
{
|
|
name: "spec-comp",
|
|
metrics: object.metrics_comp
|
|
}
|
|
], scopes: [{
|
|
name: "app-wide-scope",
|
|
components: componentsForAppWideScope,
|
|
metrics: object.metrics_global,
|
|
requirements: object.sloViolations ? object.sloViolations : [],
|
|
}]
|
|
};
|
|
|
|
//Parameters + push to app wide scope
|
|
if (object.parameters) {
|
|
const parametersMetrics = object.parameters.map(v => ({
|
|
name: v.name,
|
|
type: "constant",
|
|
initialValue: v.initialValue,
|
|
template: v.template
|
|
}));
|
|
|
|
if (parametersMetrics.length > 0) {
|
|
specContent.scopes.push({
|
|
name: "parameters-scope",
|
|
components: [],
|
|
metrics: parametersMetrics,
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
//Construct and return the final version
|
|
const yamlDoc = {
|
|
apiVersion: "nebulous/v1",
|
|
kind: "MetricModel",
|
|
metadata: {
|
|
name: object.uuid,
|
|
labels: {
|
|
app: object.title,
|
|
}
|
|
},
|
|
templates: object.templates,
|
|
spec: specContent
|
|
};
|
|
return yamlDoc;
|
|
}
|
|
};
|
|
|