gui-controller/lib/metric_model.js
Fotis Paraskevopoulos efc6c9f6da Initial release
Change-Id: I04faafd3e3a508d9359138f07e3a10fa2d84689f
2024-04-26 07:53:39 +00:00

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;
}
};