fleet/website/api/controllers/webhooks/receive-usage-analytics.js
Eric 9ea69df576
Website: add new usage statistics (#19315)
Closes: https://github.com/fleetdm/fleet/issues/19308

Changes:
- Added six new attributes to the `HistoricalUsageSnapshot` model (
`numSoftwareVersions`, `numHostSoftwares`, `numSoftwareTitles`,
`numHostSoftwareInstalledPaths`, `numSoftwareCPEs` and
`numSoftwareCVEs`)
- Added inputs to the receive-usage-analytics webhook for the new usage
statistics.


When this PR is merged, we will need to migrate the Fleet website's
database to add the new columns, to do this:
- [x] Make an announcement in Slack to let people know the website will
be offline for ~8 minutes
- [ ] Merge this PR
- [ ] As the deploy action runs, put the website into maintenance mode
- [ ] Add the new columns for the added attributes
- [ ] Set the default value for the new attributes on existing database
records
- [ ] After the deploy workflow is complete, take the website out of
maintenance mode.
2024-06-04 18:36:25 -05:00

61 lines
3.4 KiB
JavaScript
Vendored

module.exports = {
friendlyName: 'Receive usage analytics',
description: 'Receive anonymous usage analytics from deployments of Fleet running in production. (Not fleetctl preview or dev-mode deployments.)',
inputs: {
anonymousIdentifier: { required: true, type: 'string', example: '9pnzNmrES3mQG66UQtd29cYTiX2+fZ4CYxDvh495720=', description: 'An anonymous identifier telling us which Fleet deployment this is.', },
fleetVersion: { required: true, type: 'string', example: 'x.x.x' },
licenseTier: { type: 'string', isIn: ['free', 'premium', 'unknown'], defaultsTo: 'unknown' },
numHostsEnrolled: { required: true, type: 'number', min: 0, custom: (num) => Math.floor(num) === num },
numUsers: { type: 'number', defaultsTo: 0 },
numTeams: { type: 'number', defaultsTo: 0 },
numPolicies: { type: 'number', defaultsTo: 0 },
numLabels: { type: 'number', defaultsTo: 0 },
softwareInventoryEnabled: { type: 'boolean', defaultsTo: false },
vulnDetectionEnabled: { type: 'boolean', defaultsTo: false },
systemUsersEnabled: { type: 'boolean', defaultsTo: false },
hostsStatusWebHookEnabled: { type: 'boolean', defaultsTo: false },
numWeeklyActiveUsers: { type: 'number', defaultsTo: 0 },
numWeeklyPolicyViolationDaysActual: { type: 'number', defaultsTo: 0 },
numWeeklyPolicyViolationDaysPossible: { type: 'number', defaultsTo: 0 },
hostsEnrolledByOperatingSystem: { type: {}, defaultsTo: {} },
hostsEnrolledByOrbitVersion: { type: [{orbitVersion: 'string', numHosts: 'number'}], defaultsTo: [] }, // TODO: The name of this parameter does not match naming conventions.
hostsEnrolledByOsqueryVersion: { type: [{osqueryVersion: 'string', numHosts: 'number'}], defaultsTo: [] }, // TODO: The name of this parameter does not match naming conventions.
storedErrors: { type: [{}], defaultsTo: [] }, // TODO migrate all rows that have "[]" to {}
numHostsNotResponding: { type: 'number', defaultsTo: 0, description: 'The number of hosts per deployment that have not submitted results for distibuted queries. A host is counted as not responding if Fleet hasn\'t received a distributed write to requested distibuted queries for the host during the 2-hour interval since the host was last seen. Hosts that have not been seen for 7 days or more are not counted.', },
organization: { type: 'string', defaultsTo: 'unknown', description: 'For Fleet Premium deployments, the organization registered with the license.', },
mdmMacOsEnabled: {type: 'boolean', defaultsTo: false},
mdmWindowsEnabled: {type: 'boolean', defaultsTo: false},
liveQueryDisabled: {type: 'boolean', defaultsTo: false},
hostExpiryEnabled: {type: 'boolean', defaultsTo: false},
numSoftwareVersions: {type: 'number', defaultsTo: 0},
numHostSoftwares: {type: 'number', defaultsTo: 0},
numSoftwareTitles: {type: 'number', defaultsTo: 0},
numHostSoftwareInstalledPaths: {type: 'number', defaultsTo: 0},
numSoftwareCPEs: {type: 'number', defaultsTo: 0},
numSoftwareCVEs: {type: 'number', defaultsTo: 0},
},
exits: {
success: { description: 'Analytics data was stored successfully.' },
},
fn: async function (inputs) {
// If organization was reported as an empty string, set it to the default value.
if(inputs.organization === '') {
inputs.organization = 'unknown';
}
// Create a database record for these usage statistics.
await HistoricalUsageSnapshot.create(Object.assign({}, inputs));
}
};