Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI parameters #472

Merged
merged 18 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions examples/impls/functional/new-params-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: nesting-demo
description: null
tags:
kind: web
complexity: moderate
category: on-premise
aggregation:
type: both
metrics:
- 'cpu-util-ayo'
- 'energy'
initialize:
plugins:
sci-e:
path: '@grnsft/if-models'
method: SciE
tree:
children:
child-0:
pipeline:
- sci-e
inputs:
- timestamp: 2023-07-06T00:00
duration: 10
cpu-util: 50
cpu-util-ayo: 50
network/energy: 1
- timestamp: 2023-07-06T00:05
duration: 10
cpu-util-ayo: 50
network/energy: 1
19 changes: 0 additions & 19 deletions src/__tests__/unit/util/param-selectors.test.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/config/strings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {ManifestParameter} from '../types/parameters';

export const STRINGS = {
FILE_IS_NOT_YAML: 'Provided manifest is not in yaml format.',
MANIFEST_IS_MISSING: 'Manifest is missing.',
Expand Down Expand Up @@ -41,4 +43,6 @@ https://github.com/Green-Software-Foundation/if/issues/new?assignees=&labels=fee
METRIC_MISSING: (metric: string, index: number) =>
`Aggregation metric ${metric} is not found in inputs[${index}].`,
INVALID_GROUP_BY: (type: string) => `Invalid group ${type}.`,
REJECTING_OVERRIDE: (param: ManifestParameter) =>
`Rejecting overriding of canonical parameter: ${param.name}.`,
};
18 changes: 10 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
#!/usr/bin/env node
import {aggregate} from './lib/aggregate';
import {compute} from './lib/compute';
import {initalize} from './lib/initialize';
import {load} from './lib/load';
import {parameterize} from './lib/parameterize';

import {parseArgs} from './util/args';
import {ERRORS} from './util/errors';
import {andHandle} from './util/helpers';
Expand All @@ -7,11 +13,6 @@ import {saveYamlFileAs} from './util/yaml';

import {STRINGS} from './config';

import {initalize} from './lib/initialize';
import {compute} from './lib/compute';
import {load} from './lib/load';
import {aggregate} from './lib/aggregate';

const {CliInputError} = ERRORS;

const {DISCLAIMER_MESSAGE, SOMETHING_WRONG} = STRINGS;
Expand All @@ -21,11 +22,12 @@ const impactEngine = async () => {
const options = parseArgs();

if (options) {
const {inputPath, outputPath} = options;
const {inputPath, outputPath, paramPath} = options;

const {tree, context} = await load(inputPath);
const {tree, context, parameters} = await load(inputPath, paramPath);
parameterize.combine(context.params, parameters);
const plugins = await initalize(context.initialize.plugins);
const computedTree = await compute(tree, context, plugins);
const computedTree = await compute(tree, {context, plugins});
const aggregatedTree = aggregate(computedTree, context.aggregation);

const outputFile = {
Expand Down
51 changes: 1 addition & 50 deletions src/lib/aggregate.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,8 @@
import {ERRORS} from '../util/errors';
import {getAggregationMethod} from '../util/param-selectors';
import {aggregateInputsIntoOne} from '../util/aggregation-helper';

import {STRINGS} from '../config';

import {AggregationResult} from '../types/aggregation';
import {PluginParams} from '../types/interface';
import {AggregationParams} from '../types/manifest';

const {InvalidAggregationParams} = ERRORS;
const {INVALID_AGGREGATION_METHOD, METRIC_MISSING} = STRINGS;

/**
* Validates metrics array before applying aggregator.
* If aggregation method is `none`, then throws error.
*/
const checkIfMetricsAreValid = (metrics: string[]) => {
metrics.forEach(metric => {
const method = getAggregationMethod(metric);

if (method === 'none') {
throw new InvalidAggregationParams(INVALID_AGGREGATION_METHOD(method));
}
});
};

/**
* Aggregates child node level metrics. Validates if metric aggregation type is `none`, then rejects with error.
* Otherwise iterates over inputs by aggregating per given `metrics`.
*/
const aggregateInputsIntoOne = (inputs: PluginParams[], metrics: string[]) => {
checkIfMetricsAreValid(metrics);

return inputs.reduce((acc, input, index) => {
for (const metric of metrics) {
if (!(metric in input)) {
throw new InvalidAggregationParams(METRIC_MISSING(metric, index));
}

acc[metric] = acc[metric] ?? 0;
acc[metric] += parseFloat(input[metric]);

/** Checks for the last iteration. */
if (index === inputs.length - 1) {
if (getAggregationMethod(metric) === 'avg') {
acc[metric] /= inputs.length;
}
}
}

return acc;
}, {} as AggregationResult);
};

/**
* Gets `i`th element from all children outputs and collects them in single array.
*/
Expand Down
20 changes: 6 additions & 14 deletions src/lib/compute.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {Node, Params} from '../types/compute';
import {PluginsStorage} from '../types/initialize';
import {ComputeParams, Node, Params} from '../types/compute';
import {PluginParams} from '../types/interface';
import {ManifestCommon} from '../types/manifest';

/**
* Traverses all child nodes based on children grouping.
Expand All @@ -15,7 +13,7 @@ const traverse = async (children: any, params: Params) => {
/**
* Appends `default` values to `inputs`.
*/
const mergePluginParams = (
const mergeDefaults = (
inputs: PluginParams[],
defaults: PluginParams[] | undefined
) =>
Expand Down Expand Up @@ -56,7 +54,7 @@ const computeNode = async (node: Node, params: Params): Promise<any> => {

while (pipelineCopy.length !== 0) {
const pluginName = pipelineCopy.shift() as string;
storage = mergePluginParams(storage, defaults);
storage = mergeDefaults(storage, defaults);

const plugin = params.plugins[pluginName];
const {execute, metadata} = plugin;
Expand Down Expand Up @@ -86,16 +84,10 @@ const computeNode = async (node: Node, params: Params): Promise<any> => {
/**
* Creates copy of existing tree, then applies computing strategy.
*/
export const compute = async (
tree: any,
context: ManifestCommon,
plugins: PluginsStorage
) => {
export const compute = async (tree: any, params: ComputeParams) => {
const copyOfTree = structuredClone(tree);
await computeNode(copyOfTree, {
plugins,
context,
});

await computeNode(copyOfTree, params);

return copyOfTree;
};
18 changes: 15 additions & 3 deletions src/lib/load.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import {validateManifest} from '../util/validations';
import {openYamlFileAsObject} from '../util/yaml';
import {readAndParseJson} from '../util/json';

import {PARAMETERS} from '../config';

import {Manifest} from '../types/manifest';
import {ContextTree} from '../types/load';
import {ContextTreeParams} from '../types/load';
import {Parameters} from '../types/parameters';

/**
* Parses YAML file as a object, then contructs tree, context interface.
* Parses manifest file as an object. Checks if parameter file is passed via CLI, then loads it too.
* Returns context, tree and parameters (either the default one, or from CLI).
*/
export const load = async (inputPath: string): Promise<ContextTree> => {
export const load = async (
inputPath: string,
paramPath?: string
): Promise<ContextTreeParams> => {
const safeManifest = await openYamlFileAsObject<Manifest>(inputPath);
const {name, description, tags, params, aggregation, initialize, tree} =
validateManifest(safeManifest);
const parametersFromCli =
paramPath && (await readAndParseJson<Parameters>(paramPath)); // todo: validate json
const parameters = parametersFromCli || PARAMETERS;

return {
tree,
context: {name, description, tags, params, aggregation, initialize},
parameters,
};
};
62 changes: 62 additions & 0 deletions src/lib/parameterize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {logger} from '../util/logger';

import {STRINGS, PARAMETERS} from '../config';

import {ManifestParameter} from '../types/parameters';

const {REJECTING_OVERRIDE} = STRINGS;

/**
* Parameters manager. Provides get aggregation method and combine functionality.
*/
const Parametrize = () => {
let parametersStorage = {};

/**
* Returns aggregation method for given `unitName`. If doesn't exist then returns value `sum`.
*/
const getAggregationMethod = (unitName: string) => {
if (`${unitName}` in parametersStorage) {
return PARAMETERS[unitName as keyof typeof PARAMETERS].aggregation;
}

return 'sum';
};

/**
* Checks if additional parameters are provided in context.
* If so, then checks if they are coincident with default ones and exits with warning message.
* Otherwise appends context based parameters to defaults.
*/
const combine = (
contextParameters: ManifestParameter[] | null | undefined,
parameters: any
) => {
if (contextParameters) {
contextParameters.forEach((param: any) => {
if (`${param.name}` in parameters) {
logger.warn(REJECTING_OVERRIDE);

return;
}

const {description, unit, aggregation, name} = param;

parameters[name] = {
description,
unit,
aggregation,
};
});
}

parametersStorage = parameters;
};

return {
combine,
getAggregationMethod,
};
};

export const parameterize = Parametrize();
11 changes: 7 additions & 4 deletions src/models/time-sync.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {isDate} from 'node:util/types';
import {DateTime, DateTimeMaybeValid, Interval} from 'luxon';

import {parameterize} from '../lib/parameterize';

import {ERRORS} from '../util/errors';
import {getAggregationMethod} from '../util/param-selectors';

import {STRINGS} from '../config';

Expand Down Expand Up @@ -36,6 +37,7 @@ export const TimeSync = (
const endTime = parseDate(globalConfig['end-time']);
const interval = globalConfig.interval;
const allowPadding = globalConfig['allow-padding'];

return {startTime, endTime, interval, allowPadding};
};

Expand All @@ -49,6 +51,7 @@ export const TimeSync = (
if (globalConfig === undefined) {
throw new InputValidationError(INVALID_TIME_NORMALIZATION);
}

if (config) {
throw new InputValidationError(UNEXPECTED_TIME_CONFIG);
}
Expand Down Expand Up @@ -181,7 +184,7 @@ export const TimeSync = (
const inputKeys = Object.keys(input);

return inputKeys.reduce((acc, key) => {
const method = getAggregationMethod(key);
const method = parameterize.getAggregationMethod(key);

if (key === 'timestamp') {
const perSecond = normalizeTimePerSecond(input.timestamp, i);
Expand Down Expand Up @@ -235,7 +238,7 @@ export const TimeSync = (
return acc;
}

const method = getAggregationMethod(metric);
const method = parameterize.getAggregationMethod(metric);

if (method === 'avg' || method === 'sum') {
acc[metric] = 0;
Expand Down Expand Up @@ -293,7 +296,7 @@ export const TimeSync = (
const metrics = Object.keys(input);

metrics.forEach(metric => {
const method = getAggregationMethod(metric);
const method = parameterize.getAggregationMethod(metric);
acc[metric] = acc[metric] ?? 0;

if (metric === 'timestamp') {
Expand Down
9 changes: 7 additions & 2 deletions src/types/compute.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {PluginsStorage} from './initialize';
import {PluginParams} from './interface';
import {ManifestCommon} from './manifest';
import {Context} from './manifest';

export type NodeConfig = {
[key: string]: Record<string, any>;
};

export type Params = {
plugins: PluginsStorage;
context: ManifestCommon;
context: Context;
pipeline?: string[];
config?: NodeConfig;
defaults?: PluginParams[];
Expand All @@ -22,3 +22,8 @@ export type Node = {
inputs?: PluginParams[];
outputs?: PluginParams[];
};

export type ComputeParams = {
context: Context;
plugins: PluginsStorage;
};
4 changes: 0 additions & 4 deletions src/types/helpers.ts

This file was deleted.

Loading
Loading