Skip to content
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(cli): bundle dependencies #18667

Merged
merged 82 commits into from Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
6b738cd
mid work
iliapolo Jan 24, 2022
476d1f0
link to issues
iliapolo Jan 24, 2022
8e7abd6
mid work
iliapolo Jan 25, 2022
6ce1d2c
mid work
iliapolo Jan 26, 2022
c080106
Merge branch 'master' into epolon/cli-bundle
iliapolo Jan 26, 2022
a1991fe
compile
iliapolo Jan 26, 2022
39eb196
Merge branch 'master' into epolon/cli-bundle
iliapolo Jan 26, 2022
613dbf8
yargs types
iliapolo Jan 26, 2022
d526cf9
lint
iliapolo Jan 26, 2022
fffa051
mid work
iliapolo Feb 1, 2022
93b0028
mid work
iliapolo Feb 6, 2022
8ecdd89
mid work
iliapolo Feb 6, 2022
aaf5fc7
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 6, 2022
7e96b49
mid work
iliapolo Feb 6, 2022
71e5e0b
mid work
iliapolo Feb 7, 2022
aa6e99c
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 7, 2022
e504608
mid work
iliapolo Feb 7, 2022
caa9bc6
mid work
iliapolo Feb 7, 2022
b0c3c31
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 7, 2022
1067fb6
mid work
iliapolo Feb 9, 2022
f3262bd
mid work
iliapolo Feb 9, 2022
123fb5d
mid work
iliapolo Feb 9, 2022
658351d
mid work
iliapolo Feb 10, 2022
8640e74
mid work
iliapolo Feb 10, 2022
773f0be
mid work
iliapolo Feb 10, 2022
da16164
mid work
iliapolo Feb 10, 2022
30e9b58
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 10, 2022
c529c2c
mid work
iliapolo Feb 12, 2022
b529ad9
fix package
iliapolo Feb 12, 2022
b24c681
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 12, 2022
dd8cb79
linter
iliapolo Feb 12, 2022
f0325c6
fix test
iliapolo Feb 12, 2022
8ec6010
standard line endings
iliapolo Feb 12, 2022
6f6c54e
debug
iliapolo Feb 12, 2022
40c5dd7
allow custom notice file path
iliapolo Feb 12, 2022
1bcb3b9
fix message
iliapolo Feb 12, 2022
3e582ed
debug
iliapolo Feb 12, 2022
b494fb9
dont attribute cdk-assets
iliapolo Feb 12, 2022
d8a9568
revert unncessary changes
iliapolo Feb 12, 2022
a6aeb2a
revert bin
iliapolo Feb 12, 2022
d5a53d1
cleanup
iliapolo Feb 12, 2022
785ee3c
better algo for detecting external dependency version
iliapolo Feb 13, 2022
aedfa2b
more cleanup
iliapolo Feb 14, 2022
79da8ba
added resource
iliapolo Feb 14, 2022
df33d0d
review
iliapolo Feb 14, 2022
ba42055
one more resource
iliapolo Feb 14, 2022
f2e413d
added build+test for node-bundle package
iliapolo Feb 14, 2022
ca79b7d
fix warnings check
iliapolo Feb 15, 2022
8e4a232
split pack command to write + pack
iliapolo Feb 15, 2022
f3af9b1
use validate --fix instead of fix
iliapolo Feb 15, 2022
9b8d528
bundle index.js
iliapolo Feb 16, 2022
81c585a
fix resources
iliapolo Feb 16, 2022
9f5a431
default entrypoint
iliapolo Feb 16, 2022
97779dd
mid work
iliapolo Feb 17, 2022
ff68201
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 17, 2022
23a71e3
merge
iliapolo Feb 17, 2022
65577cd
handle multiple major versions of the same dep
iliapolo Feb 17, 2022
9d6b3da
Merge branch 'epolon/third-party-licenses' into epolon/cli-bundle
iliapolo Feb 17, 2022
6564a15
switch to THIRD_PARTY_LICENSES
iliapolo Feb 17, 2022
a040e10
readme
iliapolo Feb 17, 2022
d07b881
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 17, 2022
4ba0273
optional entrypoint
iliapolo Feb 18, 2022
d989b09
fix cli handling empty arrays
iliapolo Feb 20, 2022
ffba06a
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 20, 2022
515aa48
more docs
iliapolo Feb 22, 2022
e770d96
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 22, 2022
1319efb
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 22, 2022
52233c6
add referal to the THIRD_PARTY_LICENSES file
iliapolo Feb 23, 2022
4b6c90c
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 23, 2022
4a9d43b
make package private
iliapolo Feb 23, 2022
d9ac029
review
iliapolo Feb 23, 2022
ac6f266
regen cli usage
iliapolo Feb 23, 2022
fadbee2
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 23, 2022
0f58de4
Update tools/@aws-cdk/node-bundle/src/api/_attributions.ts
iliapolo Feb 23, 2022
dfc8790
chore: npm-check-updates && yarn upgrade (#19109)
iliapolo Feb 23, 2022
bba506e
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 23, 2022
0e9a151
fix attributions
iliapolo Feb 23, 2022
26933d8
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 23, 2022
bdee73b
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 23, 2022
6df4268
fix externals
iliapolo Feb 23, 2022
2e50f7f
external usage disclaimer
iliapolo Feb 23, 2022
a1c8a5f
Merge branch 'master' into epolon/cli-bundle
iliapolo Feb 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
400 changes: 398 additions & 2 deletions packages/@aws-cdk/cloudformation-diff/lib/diff/types.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/@aws-cdk/cloudformation-diff/lib/format-table.ts
@@ -1,5 +1,5 @@
import * as chalk from 'chalk';
import * as stringWidth from 'string-width';
import stringWidth from 'string-width';
import * as table from 'table';

/**
Expand Down
285 changes: 2 additions & 283 deletions packages/@aws-cdk/cloudformation-diff/lib/iam/iam-changes.ts
@@ -1,283 +1,2 @@
import * as cfnspec from '@aws-cdk/cfnspec';
import * as chalk from 'chalk';
import { PropertyChange, PropertyMap, ResourceChange } from '../diff/types';
import { DiffableCollection } from '../diffable';
import { renderIntrinsics } from '../render-intrinsics';
import { deepRemoveUndefined, dropIfEmpty, flatMap, makeComparator } from '../util';
import { ManagedPolicyAttachment, ManagedPolicyJson } from './managed-policy';
import { parseLambdaPermission, parseStatements, Statement, StatementJson } from './statement';

export interface IamChangesProps {
propertyChanges: PropertyChange[];
resourceChanges: ResourceChange[];
}

/**
* Changes to IAM statements
*/
export class IamChanges {
public static IamPropertyScrutinies = [
cfnspec.schema.PropertyScrutinyType.InlineIdentityPolicies,
cfnspec.schema.PropertyScrutinyType.InlineResourcePolicy,
cfnspec.schema.PropertyScrutinyType.ManagedPolicies,
];

public static IamResourceScrutinies = [
cfnspec.schema.ResourceScrutinyType.ResourcePolicyResource,
cfnspec.schema.ResourceScrutinyType.IdentityPolicyResource,
cfnspec.schema.ResourceScrutinyType.LambdaPermission,
];

public readonly statements = new DiffableCollection<Statement>();
public readonly managedPolicies = new DiffableCollection<ManagedPolicyAttachment>();

constructor(props: IamChangesProps) {
for (const propertyChange of props.propertyChanges) {
this.readPropertyChange(propertyChange);
}
for (const resourceChange of props.resourceChanges) {
this.readResourceChange(resourceChange);
}

this.statements.calculateDiff();
this.managedPolicies.calculateDiff();
}

public get hasChanges() {
return this.statements.hasChanges || this.managedPolicies.hasChanges;
}

/**
* Return whether the changes include broadened permissions
*
* Permissions are broadened if positive statements are added or
* negative statements are removed, or if managed policies are added.
*/
public get permissionsBroadened(): boolean {
return this.statements.additions.some(s => !s.isNegativeStatement)
|| this.statements.removals.some(s => s.isNegativeStatement)
|| this.managedPolicies.hasAdditions;
}

/**
* Return a summary table of changes
*/
public summarizeStatements(): string[][] {
const ret: string[][] = [];

const header = ['', 'Resource', 'Effect', 'Action', 'Principal', 'Condition'];

// First generate all lines, then sort on Resource so that similar resources are together
for (const statement of this.statements.additions) {
const renderedStatement = statement.render();
ret.push([
'+',
renderedStatement.resource,
renderedStatement.effect,
renderedStatement.action,
renderedStatement.principal,
renderedStatement.condition,
].map(s => chalk.green(s)));
}
for (const statement of this.statements.removals) {
const renderedStatement = statement.render();
ret.push([
chalk.red('-'),
renderedStatement.resource,
renderedStatement.effect,
renderedStatement.action,
renderedStatement.principal,
renderedStatement.condition,
].map(s => chalk.red(s)));
}

// Sort by 2nd column
ret.sort(makeComparator((row: string[]) => [row[1]]));

ret.splice(0, 0, header);

return ret;
}

public summarizeManagedPolicies(): string[][] {
const ret: string[][] = [];
const header = ['', 'Resource', 'Managed Policy ARN'];

for (const att of this.managedPolicies.additions) {
ret.push([
'+',
att.identityArn,
att.managedPolicyArn,
].map(s => chalk.green(s)));
}
for (const att of this.managedPolicies.removals) {
ret.push([
'-',
att.identityArn,
att.managedPolicyArn,
].map(s => chalk.red(s)));
}

// Sort by 2nd column
ret.sort(makeComparator((row: string[]) => [row[1]]));

ret.splice(0, 0, header);

return ret;
}

/**
* Return a machine-readable version of the changes.
* This is only used in tests.
*
* @internal
*/
public _toJson(): IamChangesJson {
return deepRemoveUndefined({
statementAdditions: dropIfEmpty(this.statements.additions.map(s => s._toJson())),
statementRemovals: dropIfEmpty(this.statements.removals.map(s => s._toJson())),
managedPolicyAdditions: dropIfEmpty(this.managedPolicies.additions.map(s => s._toJson())),
managedPolicyRemovals: dropIfEmpty(this.managedPolicies.removals.map(s => s._toJson())),
});
}

private readPropertyChange(propertyChange: PropertyChange) {
switch (propertyChange.scrutinyType) {
case cfnspec.schema.PropertyScrutinyType.InlineIdentityPolicies:
// AWS::IAM::{ Role | User | Group }.Policies
this.statements.addOld(...this.readIdentityPolicies(propertyChange.oldValue, propertyChange.resourceLogicalId));
this.statements.addNew(...this.readIdentityPolicies(propertyChange.newValue, propertyChange.resourceLogicalId));
break;
case cfnspec.schema.PropertyScrutinyType.InlineResourcePolicy:
// Any PolicyDocument on a resource (including AssumeRolePolicyDocument)
this.statements.addOld(...this.readResourceStatements(propertyChange.oldValue, propertyChange.resourceLogicalId));
this.statements.addNew(...this.readResourceStatements(propertyChange.newValue, propertyChange.resourceLogicalId));
break;
case cfnspec.schema.PropertyScrutinyType.ManagedPolicies:
// Just a list of managed policies
this.managedPolicies.addOld(...this.readManagedPolicies(propertyChange.oldValue, propertyChange.resourceLogicalId));
this.managedPolicies.addNew(...this.readManagedPolicies(propertyChange.newValue, propertyChange.resourceLogicalId));
break;
}
}

private readResourceChange(resourceChange: ResourceChange) {
switch (resourceChange.scrutinyType) {
case cfnspec.schema.ResourceScrutinyType.IdentityPolicyResource:
// AWS::IAM::Policy
this.statements.addOld(...this.readIdentityPolicyResource(resourceChange.oldProperties));
this.statements.addNew(...this.readIdentityPolicyResource(resourceChange.newProperties));
break;
case cfnspec.schema.ResourceScrutinyType.ResourcePolicyResource:
// AWS::*::{Bucket,Queue,Topic}Policy
this.statements.addOld(...this.readResourcePolicyResource(resourceChange.oldProperties));
this.statements.addNew(...this.readResourcePolicyResource(resourceChange.newProperties));
break;
case cfnspec.schema.ResourceScrutinyType.LambdaPermission:
this.statements.addOld(...this.readLambdaStatements(resourceChange.oldProperties));
this.statements.addNew(...this.readLambdaStatements(resourceChange.newProperties));
break;
}
}

/**
* Parse a list of policies on an identity
*/
private readIdentityPolicies(policies: any, logicalId: string): Statement[] {
if (policies === undefined) { return []; }

const appliesToPrincipal = 'AWS:${' + logicalId + '}';

return flatMap(policies, (policy: any) => {
// check if the Policy itself is not an intrinsic, like an Fn::If
const unparsedStatement = policy.PolicyDocument?.Statement
? policy.PolicyDocument.Statement
: policy;
return defaultPrincipal(appliesToPrincipal, parseStatements(renderIntrinsics(unparsedStatement)));
});
}

/**
* Parse an IAM::Policy resource
*/
private readIdentityPolicyResource(properties: any): Statement[] {
if (properties === undefined) { return []; }

properties = renderIntrinsics(properties);

const principals = (properties.Groups || []).concat(properties.Users || []).concat(properties.Roles || []);
return flatMap(principals, (principal: string) => {
const ref = 'AWS:' + principal;
return defaultPrincipal(ref, parseStatements(properties.PolicyDocument.Statement));
});
}

private readResourceStatements(policy: any, logicalId: string): Statement[] {
if (policy === undefined) { return []; }

const appliesToResource = '${' + logicalId + '.Arn}';
return defaultResource(appliesToResource, parseStatements(renderIntrinsics(policy.Statement)));
}

/**
* Parse an AWS::*::{Bucket,Topic,Queue}policy
*/
private readResourcePolicyResource(properties: any): Statement[] {
if (properties === undefined) { return []; }

properties = renderIntrinsics(properties);

const policyKeys = Object.keys(properties).filter(key => key.indexOf('Policy') > -1);

// Find the key that identifies the resource(s) this policy applies to
const resourceKeys = Object.keys(properties).filter(key => !policyKeys.includes(key) && !key.endsWith('Name'));
let resources = resourceKeys.length === 1 ? properties[resourceKeys[0]] : ['???'];

// For some resources, this is a singleton string, for some it's an array
if (!Array.isArray(resources)) {
resources = [resources];
}

return flatMap(resources, (resource: string) => {
return defaultResource(resource, parseStatements(properties[policyKeys[0]].Statement));
});
}

private readManagedPolicies(policyArns: any, logicalId: string): ManagedPolicyAttachment[] {
if (!policyArns) { return []; }

const rep = '${' + logicalId + '}';
return ManagedPolicyAttachment.parseManagedPolicies(rep, renderIntrinsics(policyArns));
}

private readLambdaStatements(properties?: PropertyMap): Statement[] {
if (!properties) { return []; }

return [parseLambdaPermission(renderIntrinsics(properties))];
}
}

/**
* Set an undefined or wildcarded principal on these statements
*/
function defaultPrincipal(principal: string, statements: Statement[]) {
statements.forEach(s => s.principals.replaceEmpty(principal));
statements.forEach(s => s.principals.replaceStar(principal));
return statements;
}

/**
* Set an undefined or wildcarded resource on these statements
*/
function defaultResource(resource: string, statements: Statement[]) {
statements.forEach(s => s.resources.replaceEmpty(resource));
statements.forEach(s => s.resources.replaceStar(resource));
return statements;
}

export interface IamChangesJson {
statementAdditions?: StatementJson[];
statementRemovals?: StatementJson[];
managedPolicyAdditions?: ManagedPolicyJson[];
managedPolicyRemovals?: ManagedPolicyJson[];
}
/* eslint-disable */
export { IamChangesProps, IamChanges, IamChangesJson } from '../diff/types';
5 changes: 4 additions & 1 deletion packages/@aws-cdk/cloudformation-diff/lib/iam/statement.ts
@@ -1,6 +1,9 @@
import * as deepEqual from 'fast-deep-equal';
import { deepRemoveUndefined } from '../util';

// namespace object imports won't work in the bundle for function exports
// eslint-disable-next-line @typescript-eslint/no-require-imports
const deepEqual = require('fast-deep-equal');
iliapolo marked this conversation as resolved.
Show resolved Hide resolved

export class Statement {
/**
* Statement ID
Expand Down