Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions delete-test.json
Comment thread
dallinjsevy marked this conversation as resolved.
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
Expand Up @@ -32,6 +32,7 @@ import SecretMappings from './config-manager-pull-secret-mappings';
import Secrets from './config-manager-pull-secrets';
import ServiceObjects from './config-manager-pull-service-objects';
import Services from './config-manager-pull-services';
import Telemetry from './config-manager-pull-telemetry';
import Terms from './config-manager-pull-terms-and-conditions';
import Test from './config-manager-pull-test';
import Themes from './config-manager-pull-themes';
Expand Down Expand Up @@ -76,6 +77,7 @@ export default function setup() {
program.addCommand(ServiceObjects().name('service-objects'));
program.addCommand(Services().name('services'));
program.addCommand(Themes().name('themes'));
program.addCommand(Telemetry().name('telemetry'));
program.addCommand(Terms().name('terms-and-conditions'));
program.addCommand(Test().name('test'));
program.addCommand(UiConfig().name('ui-config'));
Expand Down
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(
Comment thread
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Restart from './config-manager-push-restart';
import Schedules from './config-manager-push-schedules';
import SecretMappings from './config-manager-push-secret-mappings';
import ServiceObjects from './config-manager-push-service-objects';
import Telemetry from './config-manager-push-telemetry';
import TermsAndConditions from './config-manager-push-terms-and-conditions';
import Themes from './config-manager-push-themes';
import UiConfig from './config-manager-push-ui-config';
Expand All @@ -50,6 +51,7 @@ export default function setup() {
program.addCommand(Audit().name('audit'));
program.addCommand(CookieDomains().name('cookie-domains'));
program.addCommand(ServiceObjects().name('service-objects'));
program.addCommand(Telemetry().name('telemetry'));
program.addCommand(UiConfig().name('ui-config'));
program.addCommand(Authentication().name('authentication'));
program.addCommand(ConnectorDefinitions().name('connector-definitions'));
Expand Down
141 changes: 141 additions & 0 deletions src/configManagerOps/FrConfigTelemetryOps.ts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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 FrConfigTelemtry.ts since we want it to be named FrConfigTelemetryOps. Once you are done with all the changes in this PR, rebase with pull telemetry so you have the pull telemetry changes. We'll have this PR be the PR we merge into rockcarver that has both the pull and push commands.

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.
"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Commands:
remote-servers [Experimental] Import RCS config.
schedules [Experimental] Import schedules.
service-objects [Experimental] Import service objects.
telemetry [Experimental] Import telemetry exporters.
terms-and-conditions [Experimental] Import terms and conditions.
themes [Experimental] Import themes.
ui-config [Experimental] Import UI configuration.
Expand Down
10 changes: 10 additions & 0 deletions test/client_cli/en/config-manager-pull-telemetry.test.js

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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();
});
Loading