Skip to content

Commit

Permalink
Merge pull request #286 from vscheuber/main
Browse files Browse the repository at this point in the history
cli e2e tests
  • Loading branch information
vscheuber committed Sep 29, 2023
2 parents f4c7d72 + dd39ecc commit 8f6a61f
Show file tree
Hide file tree
Showing 179 changed files with 45,240 additions and 137,883 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ jobs:
- name: Install frodo-cli globally
run: npm i -g

- name: CLI Help Tests
- name: CLI Tests
run: npm test

- name: Version Test
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ esm/
frodo
frodo.exe

connections.json
journey-*.json
# connections.json
masterkey.key

# Logs
logs
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
]
},
"dependencies": {
"@rockcarver/frodo-lib": "2.0.0-27",
"@rockcarver/frodo-lib": "2.0.0-32",
"chokidar": "^3.5.3",
"cli-progress": "^3.11.2",
"cli-table3": "^0.6.3",
Expand Down
3 changes: 2 additions & 1 deletion src/cli/FrodoCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,11 @@ export class FrodoCommand extends FrodoStubCommand {
? ` FRODO_LOG_KEY: Log API key. Overrides '--log-api-key' option.\n` +
` FRODO_LOG_SECRET: Log API secret. Overrides '--log-api-secret' option.\n`
: ``) +
(this.name().startsWith('frodo logs')
(this.name().startsWith('frodo log')
? ` FRODO_LOG_KEY: Log API key. Overrides 'username' argument.\n` +
` FRODO_LOG_SECRET: Log API secret. Overrides 'password' argument.\n`
: ``) +
` FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'.\n` +
` FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use.\n` +
` FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'.\n` +
` FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file.\n` +
Expand Down
88 changes: 57 additions & 31 deletions src/cli/log/log-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,81 @@ program
.description('List available ID Cloud log sources.')
.action(async (host, user, password, options, command) => {
command.handleDefaultArgsAndOpts(host, user, password, options, command);
let credsFromParameters = true;
let saveCredentials = false;
let foundCredentials = false;
verboseMessage('Listing available ID Cloud log sources...');

const conn = await getConnectionProfile();
if (conn) {
state.setHost(conn.tenant);
if (conn.logApiKey != null && conn.logApiSecret != null) {
credsFromParameters = false;
state.setLogApiKey(conn.logApiKey);
state.setLogApiSecret(conn.logApiSecret);
} else {
if (conn.username == null && conn.password == null) {
if (!state.getUsername() && !state.getPassword()) {
credsFromParameters = false;
printMessage(
'User credentials not specified as parameters and no saved API key and secret found!',
'warn'
);
return;
}
} else {
state.setUsername(conn.username);
state.setPassword(conn.password);
}
if (await getTokens(true)) {
const creds = await provisionCreds();
state.setLogApiKey(creds.api_key_id as string);
state.setLogApiSecret(creds.api_key_secret as string);
}
if (conn) state.setHost(conn.tenant);

// log api creds have been supplied as username and password arguments
if (state.getUsername() && state.getPassword()) {
verboseMessage(`Using log api credentials from command line.`);
state.setLogApiKey(state.getUsername());
state.setLogApiSecret(state.getPassword());
foundCredentials = true;
}
// log api creds from connection profile
else if (conn && conn.logApiKey != null && conn.logApiSecret != null) {
verboseMessage(`Using log api credentials from connection profile.`);
state.setLogApiKey(conn.logApiKey);
state.setLogApiSecret(conn.logApiSecret);
foundCredentials = true;
}
// log api creds have been supplied via env variables
else if (state.getLogApiKey() && state.getLogApiSecret()) {
verboseMessage(`Using log api credentials from environment variables.`);
foundCredentials = true;
}
// no log api creds but got username and password, so can try to create them
else if (conn && conn.username && conn.password) {
printMessage(
`Found admin credentials in connection profile, attempting to create log api credentials...`
);
state.setUsername(conn.username);
state.setPassword(conn.password);
if (await getTokens(true)) {
const creds = await provisionCreds();
state.setLogApiKey(creds.api_key_id as string);
state.setLogApiSecret(creds.api_key_secret as string);
foundCredentials = true;
saveCredentials = true;
}
// unable to create credentials
else {
printMessage(`Unable to create log api credentials.`);
}
}

if (foundCredentials) {
const sources = await getLogSources();
if (sources.length === 0) {
printMessage(
"Can't get sources, possible cause - wrong API key or secret",
'error'
);
} else {
if (credsFromParameters) await saveConnectionProfile(host); // save new values if they were specified on CLI
printMessage(`Log sources from ${conn.tenant}`);
sources.forEach((source) => {
if (saveCredentials) await saveConnectionProfile(state.getHost()); // save new values if they were specified on CLI
printMessage(`Log sources from ${state.getHost()}`);
for (const source of sources) {
printMessage(`${source}`, 'data');
});
}
printMessage(
'Use any combination of comma separated sources, example:',
'info'
);
printMessage(`$ frodo logs tail -c am-core,idm-core ${host}`, 'text');
printMessage(
`$ frodo logs tail -c am-core,idm-core ${state.getHost()}`,
'text'
);
}
}
// no log api credentials
else {
printMessage('No log api credentials found!');
program.help();
process.exitCode = 1;
}
});

program.parse();
10 changes: 6 additions & 4 deletions src/ops/SecretsOps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { frodo } from '@rockcarver/frodo-lib';
import { frodo, state } from '@rockcarver/frodo-lib';

import {
createKeyValueTable,
Expand Down Expand Up @@ -48,7 +48,7 @@ export async function listSecrets(long) {
'Status'['brightCyan'],
'Description'['brightCyan'],
'Modifier'['brightCyan'],
'Modified'['brightCyan'],
'Modified (UTC)'['brightCyan'],
]);
for (const secret of secrets) {
table.push([
Expand All @@ -57,8 +57,10 @@ export async function listSecrets(long) {
{ hAlign: 'right', content: secret.loadedVersion },
secret.loaded ? 'loaded'['brightGreen'] : 'unloaded'['brightRed'],
wordwrap(secret.description, 40),
await resolveUserName('teammember', secret.lastChangedBy),
new Date(secret.lastChangeDate).toLocaleString(),
state.getUseBearerTokenForAmApis()
? secret.lastChangedBy
: await resolveUserName('teammember', secret.lastChangedBy),
new Date(secret.lastChangeDate).toUTCString(),
]);
}
printMessage(table.toString(), 'data');
Expand Down
4 changes: 3 additions & 1 deletion src/ops/ServiceOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ export async function listServices(long = false, globalConfig = false) {
export async function exportServicesToFile(file, globalConfig = false) {
const exportData = await exportServices(globalConfig);
let fileName = getTypedFilename(
`all${titleCase(getRealmName(state.getRealm()))}Services`,
`all${
globalConfig ? 'Global' : titleCase(getRealmName(state.getRealm()))
}Services`,
`service`
);
if (file) {
Expand Down
10 changes: 6 additions & 4 deletions src/ops/VariablesOps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { frodo } from '@rockcarver/frodo-lib';
import { frodo, state } from '@rockcarver/frodo-lib';
import { VariableExpressionType } from '@rockcarver/frodo-lib/types/api/cloud/VariablesApi';

import {
Expand Down Expand Up @@ -42,16 +42,18 @@ export async function listVariables(long) {
'Status'['brightCyan'],
'Description'['brightCyan'],
'Modifier'['brightCyan'],
'Modified'['brightCyan'],
'Modified (UTC)'['brightCyan'],
]);
for (const variable of variables) {
table.push([
variable._id,
wordwrap(decodeBase64(variable.valueBase64), 40),
variable.loaded ? 'loaded'['brightGreen'] : 'unloaded'['brightRed'],
wordwrap(variable.description, 40),
await resolveUserName('teammember', variable.lastChangedBy),
new Date(variable.lastChangeDate).toLocaleString(),
state.getUseBearerTokenForAmApis()
? variable.lastChangedBy
: await resolveUserName('teammember', variable.lastChangedBy),
new Date(variable.lastChangeDate).toUTCString(),
]);
}
printMessage(table.toString(), 'data');
Expand Down
101 changes: 17 additions & 84 deletions src/utils/ExportImportUtils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { frodo, state } from '@rockcarver/frodo-lib';
import { frodo } from '@rockcarver/frodo-lib';
import fs from 'fs';
import { lstat, readdir, readFile } from 'fs/promises';
import { join } from 'path';
import slugify from 'slugify';

import { printMessage } from './Console';

const { stringify, deleteDeepByKey } = frodo.utils.json;

/**
* find all (nested) files in a directory
*
Expand Down Expand Up @@ -47,66 +44,23 @@ export async function readFiles(
return filePathsNested.flat();
}

const { getMetadata } = frodo.utils;
const {
getMetadata,
getTypedFilename,
saveJsonToFile,
saveToFile,
titleCase,
getRealmString,
} = frodo.utils;

/**
* Get a typed filename. E.g. "my-script.script.json"
*
* @param name The name of the file
* @param type The type of the file, e.g. script, idp, etc.
* @param suffix The suffix of the file, e.g. json, xml, etc. Defaults to json.
* @returns The typed filename
*/
export function getTypedFilename(
name: string,
type: string,
suffix = 'json'
): string {
const slug = slugify(name.replace(/^http(s?):\/\//, ''));
return `${slug}.${type}.${suffix}`;
}

/**
* Save JSON object to file
*
* @param data data object
* @param filename file name
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function saveJsonToFile(data: any, filename: string) {
const exportData = data;
if (!exportData.meta) {
exportData.meta = getMetadata();
}
deleteDeepByKey(exportData, '_rev');
fs.writeFile(filename, stringify(exportData), (err) => {
if (err) {
return printMessage(`ERROR - can't save ${filename}`, 'error');
}
return '';
});
}

export function saveToFile(type, data, identifier, filename) {
const exportData = {};
exportData['meta'] = getMetadata();
exportData[type] = {};

if (Array.isArray(data)) {
data.forEach((element) => {
exportData[type][element[identifier]] = element;
});
} else {
exportData[type][data[identifier]] = data;
}
deleteDeepByKey(exportData, '_rev');
fs.writeFile(filename, stringify(exportData), (err) => {
if (err) {
return printMessage(`ERROR - can't save ${type} to file`, 'error');
}
return '';
});
}
export {
getMetadata,
getRealmString,
getTypedFilename,
saveJsonToFile,
saveToFile,
titleCase,
};

/**
* Save text data to file
Expand All @@ -123,24 +77,3 @@ export function saveTextToFile(data: string, filename: string): boolean {
return false;
}
}

/*
* Output str in title case
*
* e.g.: 'ALL UPPERCASE AND all lowercase' = 'All Uppercase And All Lowercase'
*/
export function titleCase(input) {
const str = input.toString();
const splitStr = str.toLowerCase().split(' ');
for (let i = 0; i < splitStr.length; i += 1) {
splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].slice(1);
}
return splitStr.join(' ');
}

export function getRealmString() {
const realm = state.getRealm();
return realm
.split('/')
.reduce((result, item) => `${result}${titleCase(item)}`, '');
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Evironment Variables:
FRODO_PASSWORD: Password. Overrides 'password' argument.
FRODO_SA_ID: Service account uuid. Overrides '--sa-id' option.
FRODO_SA_JWK: Service account JWK. Overrides '--sa-jwk-file' option but takes the actual JWK as a value, not a file name.
FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'.
FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use.
FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'.
FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Evironment Variables:
FRODO_PASSWORD: Password. Overrides 'password' argument.
FRODO_SA_ID: Service account uuid. Overrides '--sa-id' option.
FRODO_SA_JWK: Service account JWK. Overrides '--sa-jwk-file' option but takes the actual JWK as a value, not a file name.
FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'.
FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use.
FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'.
FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Evironment Variables:
FRODO_PASSWORD: Password. Overrides 'password' argument.
FRODO_SA_ID: Service account uuid. Overrides '--sa-id' option.
FRODO_SA_JWK: Service account JWK. Overrides '--sa-jwk-file' option but takes the actual JWK as a value, not a file name.
FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'.
FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use.
FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'.
FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Evironment Variables:
FRODO_PASSWORD: Password. Overrides 'password' argument.
FRODO_SA_ID: Service account uuid. Overrides '--sa-id' option.
FRODO_SA_JWK: Service account JWK. Overrides '--sa-jwk-file' option but takes the actual JWK as a value, not a file name.
FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'.
FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use.
FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'.
FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file.
Expand Down
Loading

0 comments on commit 8f6a61f

Please sign in to comment.