mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-24 08:01:07 +02:00
* only generate 2 mounts per client type * add acme_clients to attribution chart * add acme clients to running total * cleanup selectors * add stat box to single historical month running total, update tests * update filter subtext * add mountpath to usage stat description, change tags from h to p * cleanup extra divs * remove extra clients text from cluttered stats * update attribution test * fix some a11y tests and update attribution counts * fix a11y violations * move to beforeEach?
272 lines
9.0 KiB
JavaScript
272 lines
9.0 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import {
|
|
addMonths,
|
|
differenceInCalendarMonths,
|
|
endOfMonth,
|
|
formatRFC3339,
|
|
fromUnixTime,
|
|
isAfter,
|
|
isBefore,
|
|
isSameMonth,
|
|
isWithinInterval,
|
|
startOfMonth,
|
|
subMonths,
|
|
} from 'date-fns';
|
|
import { parseAPITimestamp } from 'core/utils/date-formatters';
|
|
import { CLIENT_TYPES } from 'core/utils/client-count-utils';
|
|
|
|
/*
|
|
HOW TO ADD NEW TYPES:
|
|
1. add key to CLIENT_TYPES
|
|
2. Find "ADD NEW CLIENT TYPES HERE" comment below and add type to destructuring array
|
|
3. Add generateMounts() for that client type to the mounts array
|
|
*/
|
|
export const LICENSE_START = new Date('2023-07-02T00:00:00Z');
|
|
export const STATIC_NOW = new Date('2024-01-25T23:59:59Z');
|
|
const COUNTS_START = subMonths(STATIC_NOW, 12); // user started Vault cluster on 2023-01-25
|
|
// upgrade happened 2 month after license start
|
|
export const UPGRADE_DATE = addMonths(LICENSE_START, 2); // monthly attribution added
|
|
|
|
function getSum(array, key) {
|
|
return array.reduce((sum, { counts }) => sum + counts[key], 0);
|
|
}
|
|
|
|
function getTotalCounts(array) {
|
|
const counts = CLIENT_TYPES.reduce((obj, key) => {
|
|
obj[key] = getSum(array, key);
|
|
return obj;
|
|
}, {});
|
|
|
|
// add deprecated keys
|
|
return {
|
|
...counts,
|
|
distinct_entities: counts.entity_clients,
|
|
non_entity_tokens: counts.non_entity_clients,
|
|
};
|
|
}
|
|
|
|
function randomBetween(min, max) {
|
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
}
|
|
|
|
function generateMountBlock(path, counts) {
|
|
const baseObject = CLIENT_TYPES.reduce((obj, key) => {
|
|
obj[key] = 0;
|
|
return obj;
|
|
}, {});
|
|
return {
|
|
mount_path: path,
|
|
counts: {
|
|
...baseObject,
|
|
distinct_entities: 0,
|
|
non_entity_tokens: 0,
|
|
// object contains keys for which 0-values of base object to overwrite
|
|
...counts,
|
|
},
|
|
};
|
|
}
|
|
|
|
function generateNamespaceBlock(idx = 0, isLowerCounts = false, ns, skipCounts = false) {
|
|
const min = isLowerCounts ? 10 : 50;
|
|
const max = isLowerCounts ? 100 : 1000;
|
|
const nsBlock = {
|
|
namespace_id: ns?.namespace_id || (idx === 0 ? 'root' : Math.random().toString(36).slice(2, 7) + idx),
|
|
namespace_path: ns?.namespace_path || (idx === 0 ? '' : `ns${idx}`),
|
|
counts: {},
|
|
mounts: {},
|
|
};
|
|
|
|
if (skipCounts) return nsBlock; // skip counts to generate empty ns block with namespace ids and paths
|
|
|
|
// generates one mount per client type
|
|
const mountsArray = (idx) => {
|
|
// * ADD NEW CLIENT TYPES HERE and pass to a new generateMountBlock() function below
|
|
const [acme_clients, entity_clients, non_entity_clients, secret_syncs] = CLIENT_TYPES.map(() =>
|
|
randomBetween(min, max)
|
|
);
|
|
|
|
// each mount type generates a different type of client
|
|
return [
|
|
generateMountBlock(`auth/authid/${idx}`, {
|
|
clients: non_entity_clients + entity_clients,
|
|
non_entity_clients,
|
|
entity_clients,
|
|
}),
|
|
generateMountBlock(`kvv2-engine-${idx}`, { clients: secret_syncs, secret_syncs }),
|
|
generateMountBlock(`pki-engine-${idx}`, { clients: acme_clients, acme_clients }),
|
|
];
|
|
};
|
|
|
|
// two mounts per client type for more varied mock data
|
|
const mounts = [...mountsArray(0), ...mountsArray(1)];
|
|
|
|
mounts.sort((a, b) => b.counts.clients - a.counts.clients);
|
|
nsBlock.mounts = mounts;
|
|
nsBlock.counts = getTotalCounts(mounts);
|
|
return nsBlock;
|
|
}
|
|
|
|
function generateMonths(startDate, endDate, namespaces) {
|
|
const startDateObject = parseAPITimestamp(startDate);
|
|
const endDateObject = parseAPITimestamp(endDate);
|
|
const numberOfMonths = differenceInCalendarMonths(endDateObject, startDateObject) + 1;
|
|
const months = [];
|
|
|
|
// only generate monthly block if queried dates span or follow upgrade to 1.10
|
|
const upgradeWithin = isWithinInterval(UPGRADE_DATE, { start: startDateObject, end: endDateObject });
|
|
const upgradeAfter = isAfter(startDateObject, UPGRADE_DATE);
|
|
if (upgradeWithin || upgradeAfter) {
|
|
for (let i = 0; i < numberOfMonths; i++) {
|
|
const month = addMonths(startOfMonth(startDateObject), i);
|
|
const hasNoData = isBefore(month, UPGRADE_DATE) && !isSameMonth(month, UPGRADE_DATE);
|
|
if (hasNoData) {
|
|
months.push({
|
|
timestamp: formatRFC3339(month),
|
|
counts: null,
|
|
namespaces: null,
|
|
new_clients: null,
|
|
});
|
|
continue;
|
|
}
|
|
|
|
const monthNs = namespaces.map((ns, idx) => generateNamespaceBlock(idx, false, ns));
|
|
const newClients = namespaces.map((ns, idx) => generateNamespaceBlock(idx, true, ns));
|
|
months.push({
|
|
timestamp: formatRFC3339(month),
|
|
counts: getTotalCounts(monthNs),
|
|
namespaces: monthNs.sort((a, b) => b.counts.clients - a.counts.clients),
|
|
new_clients: {
|
|
counts: getTotalCounts(newClients),
|
|
namespaces: newClients.sort((a, b) => b.counts.clients - a.counts.clients),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
return months;
|
|
}
|
|
|
|
function generateActivityResponse(startDate, endDate) {
|
|
let namespaces = Array.from(Array(12)).map((v, idx) => generateNamespaceBlock(idx, null, null, true));
|
|
const months = generateMonths(startDate, endDate, namespaces);
|
|
if (months.length) {
|
|
const monthlyCounts = months.filter((m) => m.counts);
|
|
// sum namespace counts from monthly data
|
|
namespaces.forEach((ns) => {
|
|
const nsData = monthlyCounts.map((d) =>
|
|
d.namespaces.find((n) => n.namespace_path === ns.namespace_path)
|
|
);
|
|
const mountCounts = nsData.flatMap((d) => d.mounts);
|
|
const paths = nsData[0].mounts.map(({ mount_path }) => mount_path);
|
|
ns.mounts = paths.map((path) => {
|
|
const counts = getTotalCounts(mountCounts.filter((m) => m.mount_path === path));
|
|
return { mount_path: path, counts };
|
|
});
|
|
ns.counts = getTotalCounts(nsData);
|
|
});
|
|
} else {
|
|
// if no monthly data due to upgrade stuff, generate counts
|
|
namespaces = Array.from(Array(12)).map((v, idx) => generateNamespaceBlock(idx));
|
|
}
|
|
namespaces.sort((a, b) => b.counts.clients - a.counts.clients);
|
|
return {
|
|
start_time: isAfter(new Date(startDate), COUNTS_START) ? startDate : formatRFC3339(COUNTS_START),
|
|
end_time: endDate,
|
|
by_namespace: namespaces,
|
|
months,
|
|
total: getTotalCounts(namespaces),
|
|
};
|
|
}
|
|
|
|
export default function (server) {
|
|
server.get('sys/license/status', function () {
|
|
return {
|
|
request_id: 'my-license-request-id',
|
|
data: {
|
|
autoloaded: {
|
|
license_id: 'my-license-id',
|
|
start_time: formatRFC3339(LICENSE_START),
|
|
expiration_time: formatRFC3339(endOfMonth(addMonths(STATIC_NOW, 6))),
|
|
},
|
|
},
|
|
};
|
|
});
|
|
|
|
server.get('sys/internal/counters/config', function () {
|
|
return {
|
|
request_id: 'some-config-id',
|
|
data: {
|
|
default_report_months: 12,
|
|
enabled: 'default-enable',
|
|
queries_available: true,
|
|
retention_months: 24,
|
|
billing_start_timestamp: formatRFC3339(LICENSE_START),
|
|
},
|
|
};
|
|
});
|
|
|
|
server.get('/sys/internal/counters/activity', (schema, req) => {
|
|
let { start_time, end_time } = req.queryParams;
|
|
// backend returns a timestamp if given unix time, so first convert to timestamp string here
|
|
if (!start_time.includes('T')) start_time = fromUnixTime(start_time).toISOString();
|
|
if (!end_time.includes('T')) end_time = fromUnixTime(end_time).toISOString();
|
|
return {
|
|
request_id: 'some-activity-id',
|
|
lease_id: '',
|
|
renewable: false,
|
|
lease_duration: 0,
|
|
data: generateActivityResponse(start_time, end_time),
|
|
wrap_info: null,
|
|
warnings: null,
|
|
auth: null,
|
|
};
|
|
});
|
|
|
|
// client counting has changed in different ways since 1.9 see link below for details
|
|
// https://developer.hashicorp.com/vault/docs/concepts/client-count/faq#client-count-faq
|
|
server.get('sys/version-history', function () {
|
|
return {
|
|
request_id: 'version-history-request-id',
|
|
data: {
|
|
keys: ['1.9.0', '1.9.1', '1.10.1', '1.14.4', '1.16.0'],
|
|
key_info: {
|
|
// entity/non-entity breakdown added
|
|
'1.9.0': {
|
|
// we don't currently use build_date, including for accuracy. it's only tracked in versions >= 1.110
|
|
build_date: null,
|
|
previous_version: null,
|
|
timestamp_installed: LICENSE_START.toISOString(),
|
|
},
|
|
'1.9.1': {
|
|
build_date: null,
|
|
previous_version: '1.9.0',
|
|
timestamp_installed: addMonths(LICENSE_START, 1).toISOString(),
|
|
},
|
|
// auth mount attribution added in 1.10.0
|
|
'1.10.1': {
|
|
build_date: null,
|
|
previous_version: '1.9.1',
|
|
timestamp_installed: UPGRADE_DATE.toISOString(),
|
|
},
|
|
// no notable UI changes
|
|
'1.14.4': {
|
|
build_date: addMonths(LICENSE_START, 3).toISOString(),
|
|
previous_version: '1.10.1',
|
|
timestamp_installed: addMonths(LICENSE_START, 3).toISOString(),
|
|
},
|
|
// sync clients added
|
|
'1.16.0': {
|
|
build_date: addMonths(LICENSE_START, 4).toISOString(),
|
|
previous_version: '1.14.4',
|
|
timestamp_installed: addMonths(LICENSE_START, 4).toISOString(),
|
|
},
|
|
},
|
|
},
|
|
};
|
|
});
|
|
}
|