-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/eng 949 secrets envvars management commands for the cli (#108)
- Loading branch information
Showing
8 changed files
with
803 additions
and
416 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { Argv } from 'yargs' | ||
import { run as runList } from './list' | ||
import { run as runSet } from './set' | ||
import { run as runUnset } from './unset' | ||
import { logger } from '../../../lib/logger' | ||
|
||
export function functionEnvCli(yargs: Argv) { | ||
yargs.usage('bls function env [subcommand]') | ||
|
||
yargs.command( | ||
['list'], | ||
'Lists all environment variables for a function', | ||
(yargs) => { | ||
return yargs | ||
.option('target', { | ||
alias: 't', | ||
description: 'The name of the function (Defaults to the working directory)', | ||
type: 'string', | ||
default: undefined | ||
}) | ||
.group(['target'], 'Options:') | ||
}, | ||
(argv) => { | ||
runList({ target: argv.target }) | ||
} | ||
) | ||
|
||
yargs.command( | ||
'set', | ||
'Set environment variables for a function', | ||
(yargs) => { | ||
return yargs | ||
.usage('set NAME=VALUE ...') | ||
.strict(false) | ||
.parserConfiguration({ 'unknown-options-as-args': true }) | ||
.option('target', { | ||
alias: 't', | ||
description: 'The name of the function (Defaults to the working directory)', | ||
type: 'string', | ||
default: undefined | ||
}) | ||
.group(['target'], 'Options:') | ||
}, | ||
(argv) => { | ||
const vars = argv._.filter( | ||
(f: string | number) => | ||
typeof f === 'string' && | ||
['function', 'functions', 'env', 'set'].indexOf(f) === -1 && | ||
/^[A-Za-z]{2,}=.+$/.test(f) | ||
) as string[] | ||
|
||
if (vars.length > 0) { | ||
const envVars = vars.reduce( | ||
(dest, item) => ({ ...dest, [item.split('=')[0]]: item.split('=')[1] }), | ||
{} | ||
) | ||
|
||
runSet({ target: argv.target, envVars }) | ||
} else { | ||
logger.log('Skipping: Nothing to update.') | ||
} | ||
} | ||
) | ||
|
||
yargs.command( | ||
'unset', | ||
'Unset environment variables for a function', | ||
(yargs) => { | ||
return yargs | ||
.usage('unset NAME NAME2 ...') | ||
.strict(false) | ||
.parserConfiguration({ 'unknown-options-as-args': true }) | ||
.positional('target', { | ||
alias: 't', | ||
description: 'The name of the function (Defaults to the working directory)', | ||
type: 'string', | ||
default: undefined | ||
}) | ||
.group(['target'], 'Options:') | ||
}, | ||
(argv) => { | ||
const envVarKeys = argv._.filter( | ||
(f: string | number) => | ||
typeof f === 'string' && | ||
['function', 'functions', 'env', 'unset'].indexOf(f) === -1 && | ||
/^[A-Za-z]+$/.test(f) | ||
) as string[] | ||
|
||
if (envVarKeys.length > 0) { | ||
const envVars = envVarKeys.reduce((dest, key) => ({ ...dest, [key]: null }), {}) | ||
runUnset({ target: argv.target, envVars }) | ||
} else { | ||
logger.log('Skipping: Nothing to update.') | ||
} | ||
} | ||
) | ||
|
||
return yargs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import Chalk from 'chalk' | ||
import Table from 'cli-table3' | ||
import dayjs from 'dayjs' | ||
import relativeTime from 'dayjs/plugin/relativeTime' | ||
|
||
import { parseBlsConfig } from '../../../lib/blsConfig' | ||
import { gatewayRequest } from '../../../lib/gateway' | ||
import { logger } from '../../../lib/logger' | ||
import { normalizeFunctionName } from '../../../lib/strings' | ||
|
||
dayjs.extend(relativeTime) | ||
|
||
interface EnvSetCommandOptions { | ||
target: string | undefined | ||
} | ||
|
||
/** | ||
* Entry function for bls function env list | ||
* | ||
* @param options | ||
* @returns | ||
*/ | ||
export const run = async (options: EnvSetCommandOptions) => { | ||
try { | ||
if (options.target) { | ||
await listEnvVars({ name: options.target }) | ||
} else { | ||
const { name: configName } = parseBlsConfig() | ||
await listEnvVars({ name: configName }) | ||
} | ||
} catch (error: any) { | ||
logger.error('Failed to list environment variables.', error.message) | ||
return | ||
} | ||
} | ||
|
||
const listEnvVars = async ({ name: functionName }: { name: string }) => { | ||
logger.log(Chalk.yellow(`Listing environment variables for ${functionName} ...`)) | ||
logger.log('') | ||
|
||
try { | ||
let matchingFunction = null | ||
let internalFunctionId = null | ||
|
||
const { data } = await gatewayRequest('[GET] /functions') | ||
const functions = data.docs ? data.docs : [] | ||
|
||
// Sort all matching functions by name and select the last matching function | ||
// TODO: Ensure all functions have unique names under a user's scope | ||
const matchingFunctions = functions.filter( | ||
(f: any) => normalizeFunctionName(f.functionName) === normalizeFunctionName(functionName) | ||
) | ||
|
||
if (matchingFunctions && matchingFunctions.length > 0) { | ||
matchingFunction = matchingFunctions[matchingFunctions.length - 1] | ||
internalFunctionId = matchingFunction._id | ||
} | ||
|
||
// If a function does not exist, request the user to deploy that function first | ||
if (!matchingFunction) { | ||
throw new Error('Function not found.') | ||
} | ||
|
||
const { data: fn } = await gatewayRequest('[GET] /functions/{id}', { id: internalFunctionId }) | ||
|
||
var table = new Table({ | ||
wordWrap: true, | ||
wrapOnWordBoundary: false, | ||
head: ['Name', 'Last Updated'], | ||
colWidths: [25] | ||
}) | ||
|
||
fn.envVars.map((envVar: any) => | ||
table.push([envVar.name, envVar.updatedAt ? dayjs().to(envVar.updatedAt) : 'Unknown']) | ||
) | ||
|
||
logger.log(table.toString()) | ||
} catch (error) { | ||
logger.error('Failed to list environment variables.') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import Chalk from 'chalk' | ||
|
||
import { parseBlsConfig } from '../../../lib/blsConfig' | ||
import { gatewayRequest } from '../../../lib/gateway' | ||
import { logger } from '../../../lib/logger' | ||
import { normalizeFunctionName } from '../../../lib/strings' | ||
|
||
interface EnvSetCommandOptions { | ||
target: string | undefined | ||
envVars: { | ||
[key: string]: string | ||
} | ||
} | ||
|
||
/** | ||
* Entry function for bls function env set | ||
* | ||
* @param options | ||
* @returns | ||
*/ | ||
export const run = async (options: EnvSetCommandOptions) => { | ||
try { | ||
if (options.target) { | ||
await setEnvVars({ name: options.target, envVars: options.envVars }) | ||
} else { | ||
const { name: configName } = parseBlsConfig() | ||
await setEnvVars({ name: configName, envVars: options.envVars }) | ||
} | ||
} catch (error: any) { | ||
logger.error('Failed to set environment variables.', error.message) | ||
return | ||
} | ||
} | ||
|
||
const setEnvVars = async ({ | ||
name: functionName, | ||
envVars | ||
}: { | ||
name: string | ||
envVars: { | ||
[key: string]: string | ||
} | ||
}) => { | ||
logger.log(Chalk.yellow(`Setting environment variables for ${functionName} ...`)) | ||
|
||
try { | ||
let matchingFunction = null | ||
let internalFunctionId = null | ||
|
||
const { data } = await gatewayRequest('[GET] /functions') | ||
const functions = data.docs ? data.docs : [] | ||
|
||
// Sort all matching functions by name and select the last matching function | ||
// TODO: Ensure all functions have unique names under a user's scope | ||
const matchingFunctions = functions.filter( | ||
(f: any) => normalizeFunctionName(f.functionName) === normalizeFunctionName(functionName) | ||
) | ||
|
||
if (matchingFunctions && matchingFunctions.length > 0) { | ||
matchingFunction = matchingFunctions[matchingFunctions.length - 1] | ||
internalFunctionId = matchingFunction._id | ||
} | ||
|
||
// If a function does not exist, request the user to deploy that function first | ||
if (!matchingFunction) { | ||
throw new Error('Function not found.') | ||
} | ||
|
||
await gatewayRequest('[PATCH] /functions/{id}/env-vars', { | ||
id: internalFunctionId, | ||
envVars | ||
}) | ||
|
||
logger.log(Chalk.green(`Successfully updated function ${functionName}!`)) | ||
} catch (error: any) { | ||
logger.error('Failed to set environment variables.', error.message) | ||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import Chalk from 'chalk' | ||
|
||
import { parseBlsConfig } from '../../../lib/blsConfig' | ||
import { logger } from '../../../lib/logger' | ||
import { gatewayRequest } from '../../../lib/gateway' | ||
import { normalizeFunctionName } from '../../../lib/strings' | ||
|
||
interface EnvUnsetCommandOptions { | ||
target: string | undefined | ||
envVars: { | ||
[key: string]: null | ||
} | ||
} | ||
|
||
/** | ||
* Entry function for bls function env unset | ||
* | ||
* @param options | ||
* @returns | ||
*/ | ||
export const run = async (options: EnvUnsetCommandOptions) => { | ||
try { | ||
if (options.target) { | ||
await unsetEnvVars({ name: options.target, envVars: options.envVars }) | ||
} else { | ||
const { name: configName } = parseBlsConfig() | ||
await unsetEnvVars({ name: configName, envVars: options.envVars }) | ||
} | ||
} catch (error: any) { | ||
logger.error('Failed to unset environment variables.', error.message) | ||
return | ||
} | ||
} | ||
|
||
const unsetEnvVars = async ({ | ||
name: functionName, | ||
envVars | ||
}: { | ||
name: string | ||
envVars: { | ||
[key: string]: null | ||
} | ||
}) => { | ||
logger.log(Chalk.yellow(`Unsetting environment variables for ${functionName} ...`)) | ||
|
||
try { | ||
let matchingFunction = null | ||
let internalFunctionId = null | ||
|
||
const { data } = await gatewayRequest('[GET] /functions') | ||
const functions = data.docs ? data.docs : [] | ||
|
||
// Sort all matching functions by name and select the last matching function | ||
// TODO: Ensure all functions have unique names under a user's scope | ||
const matchingFunctions = functions.filter( | ||
(f: any) => normalizeFunctionName(f.functionName) === normalizeFunctionName(functionName) | ||
) | ||
|
||
if (matchingFunctions && matchingFunctions.length > 0) { | ||
matchingFunction = matchingFunctions[matchingFunctions.length - 1] | ||
internalFunctionId = matchingFunction._id | ||
} | ||
|
||
// If a function does not exist, request the user to deploy that function first | ||
if (!matchingFunction) { | ||
throw new Error('Function not found.') | ||
} | ||
|
||
await gatewayRequest('[PATCH] /functions/{id}/env-vars', { | ||
id: internalFunctionId, | ||
envVars | ||
}) | ||
|
||
logger.log(Chalk.green(`Successfully updated function ${functionName}!`)) | ||
} catch (error) { | ||
logger.error('Failed to unset environment variables.') | ||
} | ||
} |
Oops, something went wrong.