Skip to content

Commit

Permalink
feat: secrets env command
Browse files Browse the repository at this point in the history
  • Loading branch information
tegefaulkes committed Feb 23, 2024
1 parent ea3de7a commit 144af95
Show file tree
Hide file tree
Showing 4 changed files with 399 additions and 65 deletions.
2 changes: 1 addition & 1 deletion npmDepsHash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sha256-zPxb3VUEeVjeWsIpBlszq3RWeZh3/0ICKRw/SyMCKsw=
sha256-LZ3QekhwOZj3IkbnZVdC/lbUvwhbJXROzd9oTxrMs7s=
123 changes: 77 additions & 46 deletions src/secrets/CommandEnv.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type PolykeyClient from 'polykey/dist/PolykeyClient';
import path from 'path';
import * as utils from 'polykey/dist/utils';
import { exec } from '@matrixai/exec';
import * as binProcessors from '../utils/processors';
import * as binUtils from '../utils';
import CommandPolykey from '../CommandPolykey';
Expand All @@ -10,13 +11,24 @@ class CommandEnv extends CommandPolykey {
constructor(...args: ConstructorParameters<typeof CommandPolykey>) {
super(...args);
this.name('env');
this.description('Secrets Env');
this.description(
'Run a command with the given secrets and env variables. If no command is specified then the variables are printed to stdout.',
);
this.addOption(binOptions.nodeId);
this.addOption(binOptions.clientHost);
this.addOption(binOptions.clientPort);
this.addOption(binOptions.envVariables);
this.action(async (options) => {
const { env: envVariables } = options;
this.addOption(binOptions.envFormat);
this.argument('[cmd] [argv...]', 'command and arguments');
this.action(async (args: Array<string>, options) => {
const [cmd, ...argv] = args;
const {
env: envVariables,
outputFormat,
}: {
env: Array<[string, string, string?]>;
outputFormat: 'dotenv' | 'json' | 'prepend';
} = options;

// There are a few stages here
// 1. parse the desired secrets
Expand Down Expand Up @@ -57,78 +69,97 @@ class CommandEnv extends CommandPolykey {
});

// Getting envs
// TODO: use auth
const response = await binUtils.retryAuthentication(async (auth) => {
const envp = await binUtils.retryAuthentication(async (auth) => {
const responseStream =
await pkClient.rpcClient.methods.vaultsSecretsEnv();
// Writing desired secrets
const secretRenameMap = new Map<string, string>();
const secretRenameMap = new Map<string, string | undefined>();
const writeP = (async () => {
const writer = responseStream.writable.getWriter();
let first = true;
for (const envVariable of envVariables) {
const [nameOrId, secretName, secretNameNew] = envVariable;
secretRenameMap.set(secretName, secretNameNew);
await writer.write({
nameOrId,
secretName,
metadata: first ? auth : undefined,
});
first = false;
}
await writer.close();
})();

const envs = new Map<string, string>();
const envp: Record<string, string> = {};
for await (const value of responseStream.readable) {
const { secretName, secretContent } = value;
let newName = secretRenameMap.get(secretName);
newName = newName ?? path.basename(secretName);
envs.set(newName, secretContent);
envp[newName] = secretContent;
}
await writeP;
return envs;
return envp;
}, meta);
// End connection early to avoid errors on server
await pkClient.stop();

// Here we want to switch between the different usages

const output: 'dotfile' | 'precom' | 'json' = 'json';
let outString = '';
switch (output) {
case 'dotfile':
{
// Formatting as a .env file
for (const [key, value] of response) {
outString += `${key}="${value}"\n`;
if (cmd != null) {
// If a cmd is provided then we default to exec it
exec.execvp(cmd, argv, envp);
} else {
// Otherwise we switch between output formats
switch (outputFormat) {
case 'dotenv':
{
// Formatting as a .env file
let data = '';
for (const [key, value] of Object.entries(envp)) {
data += `${key}="${value}"\n`;
}
process.stdout.write(
binUtils.outputFormatter({
type: 'raw',
data,
}),
);
}
}
break;
case 'precom':
{
// Formatting as a command input
let first = true;
for (const [key, value] of response) {
outString += `${first ? '' : ' '}${key}="${value}"`;
first = false;
break;
case 'prepend':
{
// Formatting as a command input
let first = true;
let data = '';
for (const [key, value] of Object.entries(envp)) {
data += `${first ? '' : ' '}${key}="${value}"`;
first = false;
}
process.stdout.write(
binUtils.outputFormatter({
type: 'raw',
data,
}),
);
}
}
break;
case 'json':
{
const object = {};
for (const [key, value] of response) {
object[key] = value;
break;
case 'json':
{
const data = {};
for (const [key, value] of Object.entries(envp)) {
data[key] = value;
}
process.stdout.write(
binUtils.outputFormatter({
type: 'json',
data: data,
}),
);
}
outString = JSON.stringify(object, undefined, 1);
}
break;
case 'exec':
{
// TODO: k/exec here
}
break;
default:
utils.never();
break;
default:
utils.never();
}
}

console.log(outString);
} finally {
if (pkClient! != null) await pkClient.stop();
}
Expand Down
9 changes: 9 additions & 0 deletions src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ const envVariables = new commander.Option('-e --env <envs...>', 'specify envs')
},
);

// '-f, --format <format>', 'Output Format'
const envFormat = new commander.Option(
'-of --output-format <outputFormat>',
'How the env variables are formatted when outputted. Only used if no commands are executed',
)
.choices(['dotenv', 'json', 'prepend'])
.default('dotenv');

export {
nodePath,
format,
Expand Down Expand Up @@ -236,4 +244,5 @@ export {
depth,
commitId,
envVariables,
envFormat,
};
Loading

0 comments on commit 144af95

Please sign in to comment.