-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/config manager push telemetry #122
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
base: main
Are you sure you want to change the base?
Changes from all commits
2cca624
e5ff552
acbb1d8
593dacf
9d0fa7f
ae7e34b
c88bbbf
441efb1
b36d214
43d75b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {"encoding":"JSON","endpoint":"https://example-siem.com:4317","headers":{"api-key":"","api-secret":""},"id":"test-otlp","sources":["am-activity","idm-activity"],"type":"HTTP"} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import { frodo } from '@rockcarver/frodo-lib'; | ||
|
|
||
| import { configManagerExportTelemetry } from '../../../configManagerOps/FrConfigTelemetryOps'; | ||
| import { getTokens } from '../../../ops/AuthenticateOps'; | ||
| import { verboseMessage } from '../../../utils/Console'; | ||
| import { FrodoCommand } from '../../FrodoCommand'; | ||
|
|
||
| const { CLOUD_DEPLOYMENT_TYPE_KEY } = frodo.utils.constants; | ||
|
|
||
| const deploymentTypes = [CLOUD_DEPLOYMENT_TYPE_KEY]; | ||
|
|
||
| export default function setup() { | ||
| const program = new FrodoCommand( | ||
| 'frodo config-manager pull telemetry', | ||
| [], | ||
| deploymentTypes | ||
| ); | ||
| program | ||
| .description('Export telemetry exporters.') | ||
| .option( | ||
| '-c, --category <category>', | ||
| 'Telemetry category to export (e.g. otlp, splunk).' | ||
| ) | ||
| .option('-n, --name <name>', 'Name of a single exporter to export.') | ||
| .action(async (host, realm, user, password, options, command) => { | ||
| command.handleDefaultArgsAndOpts( | ||
| host, | ||
| realm, | ||
| user, | ||
| password, | ||
| options, | ||
| command | ||
| ); | ||
| const getTokensIsSuccessful = await getTokens( | ||
| false, | ||
| true, | ||
| deploymentTypes | ||
| ); | ||
| if (!getTokensIsSuccessful) process.exit(1); | ||
| verboseMessage('Exporting telemetry configuration.'); | ||
| const outcome = await configManagerExportTelemetry( | ||
| options.category, | ||
| options.name | ||
| ); | ||
| if (!outcome) process.exitCode = 1; | ||
| }); | ||
| return program; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import { frodo } from '@rockcarver/frodo-lib'; | ||
|
|
||
| import { configManagerImportTelemetry } from '../../../configManagerOps/FrConfigTelemetryOps'; | ||
| import { getTokens } from '../../../ops/AuthenticateOps'; | ||
| import { printMessage, verboseMessage } from '../../../utils/Console'; | ||
| import { FrodoCommand } from '../../FrodoCommand'; | ||
|
|
||
| const { CLOUD_DEPLOYMENT_TYPE_KEY } = frodo.utils.constants; | ||
|
|
||
| const deploymentTypes = [CLOUD_DEPLOYMENT_TYPE_KEY]; | ||
|
|
||
| export default function setup() { | ||
| const program = new FrodoCommand( | ||
| 'frodo config-manager push telemetry', | ||
| [], | ||
| deploymentTypes | ||
| ); | ||
| program | ||
| .description('Import telemetry exporters.') | ||
| .option( | ||
| '-c, --category <category>', | ||
| 'Telemetry category to import (otlp or splunk).' | ||
| ) | ||
| .option( | ||
| '-n, --name <name>', | ||
| 'Name of a single exporter to import. Requires --category.' | ||
| ) | ||
| .action(async (host, realm, user, password, options, command) => { | ||
| command.handleDefaultArgsAndOpts( | ||
| host, | ||
| realm, | ||
| user, | ||
| password, | ||
| options, | ||
| command | ||
| ); | ||
| if (options.name && !options.category) { | ||
| printMessage( | ||
| 'Named telemetry config requires category (e.g. --category otlp)', | ||
| 'error' | ||
| ); | ||
| process.exitCode = 1; | ||
| program.help(); | ||
| } | ||
|
|
||
| const getTokensIsSuccessful = await getTokens( | ||
|
dallinjsevy marked this conversation as resolved.
|
||
| false, | ||
| true, | ||
| deploymentTypes | ||
| ); | ||
| if (!getTokensIsSuccessful) process.exit(1); | ||
| verboseMessage('Importing telemetry.'); | ||
| const outcome = await configManagerImportTelemetry( | ||
| options.category, | ||
| options.name | ||
| ); | ||
| if (!outcome) process.exitCode = 1; | ||
| }); | ||
| return program; | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After making changes to FrConfigTelemetry.ts as I commented about, copy those over to this file so you can delete |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import { frodo } from '@rockcarver/frodo-lib'; | ||
| import { TelemetryExporterCategory } from '@rockcarver/frodo-lib/types/api/cloud/TelemetryApi'; | ||
| import { TelemetryExportInterface } from '@rockcarver/frodo-lib/types/ops/cloud/TelemetryOps'; | ||
| import fs from 'fs'; | ||
|
|
||
| import { | ||
| createProgressIndicator, | ||
| printError, | ||
| stopProgressIndicator, | ||
| } from '../utils/Console'; | ||
|
|
||
| const { saveJsonToFile, getFilePath, readJsonFile } = frodo.utils; | ||
| const { exportTelemetry, importTelemetry } = frodo.cloud.telemetry; | ||
|
|
||
| /** | ||
| * Exports telemetry configuration in config manager format | ||
| * @param {TelemetryExporterCategory} category optional parameter to export telemetry by category. | ||
| * @param {string} name optional parameter to export telemetry config by name. | ||
| * @returns { Promise<boolean> } returns true if telemetry was successfully exported | ||
| */ | ||
| export async function configManagerExportTelemetry( | ||
| category?: TelemetryExporterCategory, | ||
| name?: string | ||
| ): Promise<boolean> { | ||
| try { | ||
| const exporters = await exportTelemetry(name, category); | ||
|
|
||
| for (const [cat, providers] of Object.entries(exporters.telemetry)) { | ||
| for (const provider of providers) { | ||
| const exportProvider = provider as any; | ||
| if (exportProvider.headers) { | ||
| const placeholders: Record<string, string> = {}; | ||
| Object.keys(exportProvider.headers).forEach((headerName) => { | ||
| placeholders[headerName] = | ||
| `\${TELEMETRY_HEADER_${cat}_${provider.id}_${headerName}}` | ||
| .replaceAll('-', '_') | ||
| .toUpperCase(); | ||
| }); | ||
| exportProvider.headers = placeholders; | ||
| } | ||
| saveJsonToFile( | ||
| exportProvider, | ||
| getFilePath(`telemetry/${cat}/${provider.id}.json`, true), | ||
| false | ||
| ); | ||
| } | ||
| } | ||
| return true; | ||
| } catch (e) { | ||
| printError(e); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Imports telemetry configuration in config manager format | ||
| * @param {TelemetryExporterCategory} category optional paremeter to export specific telemetry. | ||
| * @param {string} name optional parameter to export telemetry config by name. | ||
| * @returns { Promise<boolean> } returns true if telemetry was successfully imported | ||
| */ | ||
| export async function configManagerImportTelemetry( | ||
| category?: TelemetryExporterCategory, | ||
| name?: string | ||
| ): Promise<boolean> { | ||
| const spinnerId = createProgressIndicator( | ||
| 'indeterminate', | ||
| 0, | ||
| `Reading telemetry exporters...` | ||
| ); | ||
|
|
||
| try { | ||
| const telemetryDir = getFilePath('telemetry'); | ||
|
|
||
| if (!fs.existsSync(telemetryDir)) { | ||
| stopProgressIndicator( | ||
| spinnerId, | ||
| 'No telemetry exporters found to import', | ||
| 'fail' | ||
| ); | ||
| return false; | ||
| } | ||
|
|
||
| const categories = fs | ||
| .readdirSync(telemetryDir, { withFileTypes: true }) | ||
| .filter((entry) => entry.isDirectory()) | ||
| .map((entry) => entry.name as TelemetryExporterCategory) | ||
| .filter((cat) => !category || cat === category); | ||
|
|
||
| const importData = { | ||
| telemetry: Object.fromEntries(categories.map((cat) => [cat, []])), | ||
| } as TelemetryExportInterface; | ||
|
|
||
| let exportCounter = 0; | ||
|
|
||
| for (const cat of categories) { | ||
| const catDir = getFilePath(`telemetry/${cat}`); | ||
| if (!fs.existsSync(catDir)) { | ||
| continue; | ||
| } | ||
| const files = fs | ||
| .readdirSync(catDir) | ||
| .filter((f) => f.toLowerCase().endsWith('.json')) | ||
| .filter((f) => !name || f === `${name}.json`); | ||
| for (const file of files) { | ||
| const filePath = `${catDir}/${file}`; | ||
| const provider = readJsonFile(filePath) as any; | ||
| importData.telemetry[cat].push(provider); | ||
| exportCounter++; | ||
| } | ||
| } | ||
| if (exportCounter === 0) { | ||
| stopProgressIndicator( | ||
| spinnerId, | ||
| name | ||
| ? `No matching telemetry exporter found for ${name}` | ||
| : 'No telemetry exporters found to import', | ||
| 'fail' | ||
| ); | ||
| return false; | ||
| } | ||
|
|
||
| stopProgressIndicator( | ||
| spinnerId, | ||
| `Successfully read ${exportCounter} telemetry exporter(s).`, | ||
| 'success' | ||
| ); | ||
|
|
||
| await importTelemetry(importData); | ||
|
|
||
| stopProgressIndicator( | ||
| spinnerId, | ||
| `Successfully imported ${exportCounter} telemetry exporter(s).`, | ||
| 'success' | ||
| ); | ||
|
|
||
| return true; | ||
| } catch (error) { | ||
| printError(error, 'Error importing telemetry configuration'); | ||
| return false; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
|
||
| exports[`CLI help interface for 'config-manager pull telemetry' should be expected english 1`] = ` | ||
| "Usage: frodo config-manager pull telemetry [options] [host] [realm] [username] [password] | ||
|
|
||
| [Experimental] Export telemetry exporters. | ||
|
|
||
| Arguments: | ||
| host AM base URL, e.g.: https://cdk.iam.example.com/am. | ||
| To use a connection profile, just specify a unique | ||
| substring or alias. | ||
| realm Realm. Specify realm as '/' for the root realm or | ||
| 'realm' or '/parent/child' otherwise. (default: | ||
| "alpha" for Identity Cloud tenants, "/" otherwise.) | ||
| username Username to login with. Must be an admin user with | ||
| appropriate rights to manage authentication | ||
| journeys/trees. If given without a password, and it | ||
| matches the username already stored in the | ||
| connection profile for the target host, frodo uses | ||
| that profile's stored password instead of requiring | ||
| it on the command line. | ||
| password Password. | ||
|
|
||
| Deployment: Cloud-only | ||
|
|
||
| Options: | ||
| -c, --category <category> Telemetry category to export (e.g. otlp, splunk). | ||
| -n, --name <name> Name of a single exporter to export. | ||
| -h, --help Help | ||
| -hh, --help-more Help with all options. | ||
| -hhh, --help-all Help with all options, environment variables, and | ||
| usage examples. | ||
| " | ||
| `; |
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are missing a test like this but for the push command |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import cp from 'child_process'; | ||
| import { promisify } from 'util'; | ||
|
|
||
| const exec = promisify(cp.exec); | ||
| const CMD = 'frodo config-manager pull telemetry --help'; | ||
| const { stdout } = await exec(CMD); | ||
|
|
||
| test("CLI help interface for 'config-manager pull telemetry' should be expected english", async () => { | ||
| expect(stdout).toMatchSnapshot(); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.