-
Notifications
You must be signed in to change notification settings - Fork 4k
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
feat(toolkit): add 'cdk context' command #1169
Changes from 1 commit
a22a17a
00b7101
041982f
06e782a
c4bbf50
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 |
---|---|---|
|
@@ -42,3 +42,52 @@ The |cdk| currently supports the following context providers. | |
.. code:: js | ||
|
||
const ami: string = new SSMParameterProvider(this).getString('my-awesome-value'); | ||
|
||
|
||
########################### | ||
Viewing and managing context | ||
########################### | ||
|
||
Context is used to retrieve things like Availability Zones available to you, or | ||
AMI IDs used to start your instances. In order to avoid unexpected changes to | ||
your deployments-- let's say you were adding a ``Queue`` to your application but | ||
it happened that a new Amazon Linux AMI was released and all of a sudden your | ||
AutoScalingGroup will change-- we store the context values in ``cdk.json``, so | ||
after they've been retrieved once we can be sure we're using the same value on | ||
the next synthesis. | ||
|
||
To have a look at the context values stored for your application, run ``cdk | ||
context``. You will see something like the following: | ||
|
||
.. code:: | ||
|
||
$ cdk context | ||
|
||
Context found in cdk.json: | ||
|
||
┌───┬────────────────────────────────────────────────────┬────────────────────────────────────────────────────┐ | ||
│ # │ Key │ Value │ | ||
├───┼────────────────────────────────────────────────────┼────────────────────────────────────────────────────┤ | ||
│ 1 │ availability-zones:account=123456789012:region=us- │ [ "us-east-1a", "us-east-1b", "us-east-1c", │ | ||
│ │ east-1 │ "us-east-1d", "us-east-1e", "us-east-1f" ] │ | ||
├───┼────────────────────────────────────────────────────┼────────────────────────────────────────────────────┤ | ||
│ 2 │ ssm:account=123456789012:parameterName=/aws/ │ "ami-013be31976ca2c322" │ | ||
│ │ service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_ │ │ | ||
│ │ 64-gp2:region=us-east-1 │ │ | ||
└───┴────────────────────────────────────────────────────┴────────────────────────────────────────────────────┘ | ||
|
||
Run cdk context --invalidate KEY_OR_NUMBER to invalidate a context key. It will be refreshed on the next CDK synthesis run. | ||
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.
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.
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. I will call it reset. |
||
|
||
At some point, we *do* want to update to the latest version of the Amazon Linux | ||
AMI. To do a controlled update of the context value, invalidate it and | ||
synthesize again: | ||
|
||
.. code:: | ||
|
||
$ cdk context --invalidate 2 | ||
Context value | ||
ssm:account=123456789012:parameterName=/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2:region=us-east-1 | ||
invalidated. It will be refreshed on the next SDK synthesis run. | ||
|
||
$ cdk synth | ||
... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import colors = require('colors/safe'); | ||
import table = require('table'); | ||
import yargs = require('yargs'); | ||
import { print } from '../../lib/logging'; | ||
import { DEFAULTS, loadProjectConfig, saveProjectConfig } from '../settings'; | ||
|
||
export const command = 'context'; | ||
export const describe = 'Manage cached context values'; | ||
export const builder = { | ||
invalidate: { | ||
alias: 'd', | ||
desc: 'The context key (or its index) to invalidate', | ||
type: 'string', | ||
requiresArg: 'KEY' | ||
}, | ||
clear: { | ||
desc: 'Clear all context', | ||
type: 'boolean', | ||
} | ||
}; | ||
|
||
export async function handler(args: yargs.Arguments): Promise<number> { | ||
const settings = await loadProjectConfig(); | ||
const context = settings.get(['context']) || {}; | ||
|
||
if (args.clear) { | ||
settings.set(['context'], {}); | ||
await saveProjectConfig(settings); | ||
print('All context values cleared.'); | ||
} else if (args.invalidate) { | ||
invalidateContext(context, args.invalidate); | ||
settings.set(['context'], context); | ||
await saveProjectConfig(settings); | ||
} else { | ||
listContext(context); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
function listContext(context: any) { | ||
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. Can we add a |
||
const keys = contextKeys(context); | ||
|
||
// Print config by default | ||
const data: any[] = [[colors.green('#'), colors.green('Key'), colors.green('Value')]]; | ||
for (const [i, key] of keys) { | ||
const jsonWithoutNewlines = JSON.stringify(context[key], undefined, 2).replace(/\s+/g, ' '); | ||
data.push([i, key, jsonWithoutNewlines]); | ||
} | ||
|
||
print(`Context found in ${colors.blue(DEFAULTS)}:\n`); | ||
|
||
print(table.table(data, { | ||
border: table.getBorderCharacters('norc'), | ||
columns: { | ||
1: { width: 50, wrapWord: true } as any, | ||
2: { width: 50, wrapWord: true } as any | ||
} | ||
})); | ||
|
||
// tslint:disable-next-line:max-line-length | ||
print(`Run ${colors.blue('cdk context --invalidate KEY_OR_NUMBER')} to invalidate a context key. It will be refreshed on the next CDK synthesis run.`); | ||
} | ||
|
||
function invalidateContext(context: any, key: string) { | ||
const i = parseInt(key, 10); | ||
if (`${i}` === key) { | ||
// Twas a number and we fully parsed it. | ||
key = keyByNumber(context, i); | ||
} | ||
|
||
// Unset! | ||
if (!(key in context)) { | ||
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. Can probably be a no-op (think scripts that want to ensure that a context is invalidated) |
||
throw new Error(`No context value with key: ${key}`); | ||
} | ||
|
||
delete context[key]; | ||
|
||
print(`Context value ${colors.blue(key)} invalidated. It will be refreshed on the next SDK synthesis run.`); | ||
} | ||
|
||
function keyByNumber(context: any, n: number) { | ||
for (const [i, key] of contextKeys(context)) { | ||
if (n === i) { | ||
return key; | ||
} | ||
} | ||
throw new Error(`No context key with number: ${n}`); | ||
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. Maybe add --force and this will be no-op (similar to 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. This is for a semi-interactive usage, where copying the key is a bother and you simply supply the number. Blindly deleting by key # is dangerous, given that the numbering is not unique. But yeah, just deleting a nonexistant key will be a no-op. 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. You are right of course! |
||
} | ||
|
||
/** | ||
* Return enumerated keys in a definitive order | ||
*/ | ||
function contextKeys(context: any) { | ||
const keys = Object.keys(context); | ||
keys.sort(); | ||
return enumerate1(keys); | ||
} | ||
|
||
function enumerate1<T>(xs: T[]): Array<[number, T]> { | ||
const ret = new Array<[number, T]>(); | ||
let i = 1; | ||
for (const x of xs) { | ||
ret.push([i, x]); | ||
i += 1; | ||
} | ||
return ret; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Availability zone available" sounds weird