Skip to content

Commit

Permalink
Bug/prefix name clash
Browse files Browse the repository at this point in the history
  • Loading branch information
estohlmann authored Sep 30, 2024
2 parents be977cf + b4624fc commit 36264e9
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 29 deletions.
2 changes: 1 addition & 1 deletion ecs_model_deployer/src/lib/ecsCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export class ECSCluster extends Construct {
environment.SSL_CERT_FILE = config.certificateAuthorityBundle;
}

const taskPolicy = ManagedPolicy.fromManagedPolicyName(this, createCdkId([config.deploymentName, 'ECSPolicy']), createCdkId([config.deploymentName, 'ECSPolicy']));
const taskPolicy = ManagedPolicy.fromManagedPolicyName(this, createCdkId([(config.permissionsBoundaryAspect?.policyPrefix ?? '') + config.deploymentName, 'ECSPolicy']), createCdkId([(config.permissionsBoundaryAspect?.policyPrefix ?? '') + config.deploymentName, 'ECSPolicy']));
const role_id = ecsConfig.identifier;
const roleName = createCdkId([config.deploymentName, role_id, 'Role']);
const taskRole = new Role(this, createCdkId([role_id, 'Role']), {
Expand Down
8 changes: 8 additions & 0 deletions ecs_model_deployer/src/lib/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,14 @@ const RawConfigSchema = z
)
.optional(),
deploymentPrefix: z.string().optional(),
permissionsBoundaryAspect: z
.object({
permissionsBoundaryPolicyName: z.string(),
rolePrefix: z.string().max(20).optional(),
policyPrefix: z.string().max(20).optional(),
instanceProfilePrefix: z.string().optional(),
})
.optional(),
})
.refine((config) => (config.pypiConfig.indexUrl && config.region.includes('iso')) || !config.region.includes('iso'), {
message: 'Must set PypiConfig if in an iso region',
Expand Down
4 changes: 2 additions & 2 deletions lib/api-base/ecsCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ export class ECSCluster extends Construct {
const { config, vpc, securityGroup, ecsConfig } = props;

// Create ECS cluster
const cluster = new Cluster(this, createCdkId([ecsConfig.identifier, 'Cl']), {
const cluster = new Cluster(this, createCdkId(['Cl']), {
clusterName: createCdkId([config.deploymentName, ecsConfig.identifier], 32, 2),
vpc: vpc,
containerInsights: !config.region.includes('iso'),
});

// Create auto scaling group
const autoScalingGroup = cluster.addCapacity(createCdkId([ecsConfig.identifier, 'ASG']), {
const autoScalingGroup = cluster.addCapacity(createCdkId(['ASG']), {
instanceType: new InstanceType(ecsConfig.instanceType),
machineImage: EcsOptimizedImage.amazonLinux2(ecsConfig.amiHardwareType),
minCapacity: ecsConfig.autoScalingConfig.minCapacity,
Expand Down
11 changes: 7 additions & 4 deletions lib/chat/api/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
import { IAuthorizer, RestApi } from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { ISecurityGroup, IVpc } from 'aws-cdk-lib/aws-ec2';
import { IRole } from 'aws-cdk-lib/aws-iam';
import { Role } from 'aws-cdk-lib/aws-iam';
import { LayerVersion } from 'aws-cdk-lib/aws-lambda';
import { StringParameter } from 'aws-cdk-lib/aws-ssm';
import { Construct } from 'constructs';

import { PythonLambdaFunction, registerAPIEndpoint } from '../../api-base/utils';
import { BaseProps } from '../../schema';
import { createLambdaRole } from '../../core/utils';

/**
* Properties for SessionApi Construct.
Expand All @@ -37,7 +38,6 @@ import { BaseProps } from '../../schema';
*/
type SessionApiProps = {
authorizer: IAuthorizer;
lambdaExecutionRole?: IRole;
restApiId: string;
rootResourceId: string;
securityGroups?: ISecurityGroup[];
Expand All @@ -51,7 +51,7 @@ export class SessionApi extends Construct {
constructor (scope: Construct, id: string, props: SessionApiProps) {
super(scope, id);

const { authorizer, config, lambdaExecutionRole, restApiId, rootResourceId, securityGroups, vpc } = props;
const { authorizer, config, restApiId, rootResourceId, securityGroups, vpc } = props;

// Get common layer based on arn from SSM due to issues with cross stack references
const commonLambdaLayer = LayerVersion.fromLayerVersionArn(
Expand Down Expand Up @@ -143,6 +143,9 @@ export class SessionApi extends Construct {
},
},
];

const lambdaRole: Role = createLambdaRole(this, config.deploymentName, 'SessionApi', sessionTable.tableArn);

apis.forEach((f) => {
const lambdaFunction = registerAPIEndpoint(
this,
Expand All @@ -152,7 +155,7 @@ export class SessionApi extends Construct {
[commonLambdaLayer],
f,
config.lambdaConfig.pythonRuntime,
lambdaExecutionRole,
lambdaRole,
vpc,
securityGroups,
);
Expand Down
50 changes: 43 additions & 7 deletions lib/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import * as fs from 'fs';
import * as path from 'path';

import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';

import { Config } from '../schema';
import { Effect, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

const IAM_DIR = path.join(__dirname, 'iam');

type JSONPolicyStatement = {
Effect: iam.Effect;
Effect: Effect;
Action: string[];
Resource: string | string[];
Condition: Record<string, Record<string, string | string[]>>;
Expand All @@ -37,9 +38,9 @@ type JSONPolicyStatement = {
*
* @param {Config} config - The application configuration.
* @param {string} serviceName - AWS service name.
* @returns {iam.PolicyStatement[]} - Extracted IAM policy statements.
* @returns {PolicyStatement[]} - Extracted IAM policy statements.
*/
const extractPolicyStatementsFromJson = (config: Config, serviceName: string): iam.PolicyStatement[] => {
const extractPolicyStatementsFromJson = (config: Config, serviceName: string): PolicyStatement[] => {
const statementData = fs.readFileSync(path.join(IAM_DIR, `${serviceName.toLowerCase()}.json`), 'utf8');
const statements = JSON.parse(statementData).Statement;

Expand All @@ -55,19 +56,54 @@ const extractPolicyStatementsFromJson = (config: Config, serviceName: string): i
}
});

return statements.map((statement: JSONPolicyStatement) => iam.PolicyStatement.fromJson(statement));
return statements.map((statement: JSONPolicyStatement) => PolicyStatement.fromJson(statement));
};

/**
* Wrapper to get IAM policy statements.
* @param {Config} config - The application configuration.
* @param {string} serviceName - AWS service name.
* @returns {iam.PolicyStatement[]} - Extracted IAM policy statements.
* @returns {PolicyStatement[]} - Extracted IAM policy statements.
*/
export const getIamPolicyStatements = (config: Config, serviceName: string): iam.PolicyStatement[] => {
export const getIamPolicyStatements = (config: Config, serviceName: string): PolicyStatement[] => {
return extractPolicyStatementsFromJson(config, serviceName);
};

export const createLambdaRole = (construct: Construct, deploymentName: string, lambdaName: string, tableArn: string = '') => {
return new Role(construct, `Lisa${lambdaName}LambdaExecutionRole`, {
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
roleName: createCdkId([deploymentName, `Lisa${lambdaName}LambdaExecutionRole`]),
description: `Role used by LISA ${lambdaName} lambdas to access AWS resources`,
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'),
],
inlinePolicies: {
lambdaPermissions: new PolicyDocument({
statements: [...(tableArn ? [
new PolicyStatement({
effect: Effect.ALLOW,
actions: [
'dynamodb:BatchGetItem',
'dynamodb:ConditionCheckItem',
'dynamodb:DescribeTable',
'dynamodb:GetItem',
'dynamodb:GetRecords',
'dynamodb:GetShardIterator',
'dynamodb:Query',
'dynamodb:Scan'
],
resources: [
tableArn,
`${tableArn}/*`,
]
})
] : []),
]
}),
}
});
};

/**
* Creates a unique CDK ID using configuration data. The CDK ID is used to uniquely identify resources in the AWS
* Cloud Development Kit (CDK). The maximum length of the CDK ID is 64 characters.
Expand Down
9 changes: 6 additions & 3 deletions lib/models/docker-image-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
*/

import { Construct } from 'constructs';
import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda';
import { Code, Function } from 'aws-cdk-lib/aws-lambda';
import { Role, InstanceProfile, ServicePrincipal, ManagedPolicy, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { Stack, Duration } from 'aws-cdk-lib';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';

import { createCdkId } from '../core/utils';
import { BaseProps } from '../schema';

export type DockerImageBuilderProps = {
export type DockerImageBuilderProps = BaseProps & {
ecrUri: string;
mountS3DebUrl: string;
};
Expand All @@ -37,6 +38,7 @@ export class DockerImageBuilder extends Construct {
const stackName = Stack.of(scope).stackName;

const ec2InstanceProfileRole = new Role(this, createCdkId([stackName, 'docker-image-builder-ec2-role']), {
roleName: createCdkId([stackName, 'docker-image-builder-ec2-role']),
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
});

Expand Down Expand Up @@ -77,6 +79,7 @@ export class DockerImageBuilder extends Construct {
ec2InstanceProfileRole.attachInlinePolicy(ec2InstanceProfilePolicy);

const role = new Role(this, createCdkId([stackName, 'docker_image_builder_role']), {
roleName: createCdkId([stackName, 'docker_image_builder_role']),
assumedBy: new ServicePrincipal('lambda.amazonaws.com')
});

Expand Down Expand Up @@ -112,7 +115,7 @@ export class DockerImageBuilder extends Construct {
const functionId = createCdkId([stackName, 'docker-image-builder']);
this.dockerImageBuilderFn = new Function(this, functionId, {
functionName: functionId,
runtime: Runtime.PYTHON_3_12,
runtime: props.config.lambdaConfig.pythonRuntime,
handler: 'dockerimagebuilder.handler',
code: Code.fromAsset('./lambda/'),
timeout: Duration.minutes(1),
Expand Down
3 changes: 2 additions & 1 deletion lib/models/ecs-model-deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export class ECSModelDeployer extends Construct {
'deploymentStage': props.config.deploymentStage,
'removalPolicy': props.config.removalPolicy,
's3BucketModels': props.config.s3BucketModels,
'mountS3DebUrl': props.config.mountS3DebUrl
'mountS3DebUrl': props.config.mountS3DebUrl,
'permissionsBoundaryAspect': props.config.permissionsBoundaryAspect
};

const functionId = createCdkId([stackName, 'ecs_model_deployer']);
Expand Down
13 changes: 7 additions & 6 deletions lib/models/model-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2';
import { Repository } from 'aws-cdk-lib/aws-ecr';
import {
Effect,
IRole,
ManagedPolicy,
Policy,
PolicyDocument,
Expand All @@ -44,6 +43,7 @@ import { AttributeType, BillingMode, Table, TableEncryption } from 'aws-cdk-lib/
import { CreateModelStateMachine } from './state-machine/create-model';
import { UpdateModelStateMachine } from './state-machine/update-model';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
import { createLambdaRole } from '../core/utils';

/**
* Properties for ModelsApi Construct.
Expand All @@ -57,7 +57,6 @@ import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
*/
type ModelsApiProps = BaseProps & {
authorizer: IAuthorizer;
lambdaExecutionRole?: IRole;
lisaServeEndpointUrlPs: StringParameter;
restApiId: string;
rootResourceId: string;
Expand All @@ -72,7 +71,7 @@ export class ModelsApi extends Construct {
constructor (scope: Construct, id: string, props: ModelsApiProps) {
super(scope, id);

const { authorizer, config, lambdaExecutionRole, lisaServeEndpointUrlPs, restApiId, rootResourceId, securityGroups, vpc } = props;
const { authorizer, config, lisaServeEndpointUrlPs, restApiId, rootResourceId, securityGroups, vpc } = props;

// Get common layer based on arn from SSM due to issues with cross stack references
const commonLambdaLayer = LayerVersion.fromLayerVersionArn(
Expand Down Expand Up @@ -121,7 +120,8 @@ export class ModelsApi extends Construct {

const dockerImageBuilder = new DockerImageBuilder(this, 'docker-image-builder', {
ecrUri: ecsModelBuildRepo.repositoryUri,
mountS3DebUrl: config.mountS3DebUrl!
mountS3DebUrl: config.mountS3DebUrl!,
config: config
});

const managementKeyName = StringParameter.valueForStringParameter(this, `${config.deploymentPrefix}/managementKeySecretName`);
Expand Down Expand Up @@ -252,6 +252,7 @@ export class ModelsApi extends Construct {
MODEL_TABLE_NAME: modelTable.tableName,
};

const lambdaRole: Role = createLambdaRole(this, config.deploymentName, 'ModelApi', modelTable.tableArn);
// create proxy handler
const lambdaFunction = registerAPIEndpoint(
this,
Expand All @@ -268,7 +269,7 @@ export class ModelsApi extends Construct {
environment
},
config.lambdaConfig.pythonRuntime,
lambdaExecutionRole,
lambdaRole,
vpc.vpc,
securityGroups,
);
Expand Down Expand Up @@ -340,7 +341,7 @@ export class ModelsApi extends Construct {
[commonLambdaLayer],
f,
config.lambdaConfig.pythonRuntime,
lambdaExecutionRole,
lambdaRole,
vpc.vpc,
securityGroups,
);
Expand Down
4 changes: 2 additions & 2 deletions lib/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,8 +893,8 @@ const RawConfigSchema = z
permissionsBoundaryAspect: z
.object({
permissionsBoundaryPolicyName: z.string(),
rolePrefix: z.string().optional(),
policyPrefix: z.string().optional(),
rolePrefix: z.string().max(20).optional(),
policyPrefix: z.string().max(20).optional(),
instanceProfilePrefix: z.string().optional(),
})
.optional(),
Expand Down
2 changes: 1 addition & 1 deletion lib/serve/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class LisaServeApplicationStack extends Stack {
// const rotateManagementKeyLambdaId = createCdkId([id, 'RotateManagementKeyLambda'])
// const rotateManagementKeyLambda = new Function(this, rotateManagementKeyLambdaId, {
// functionName: rotateManagementKeyLambdaId,
// runtime: Runtime.PYTHON_3_12,
// runtime: config.lambdaConfig.pythonRuntime,
// handler: 'management_key.rotate_management_key',
// code: Code.fromAsset('./lambda/'),
// timeout: Duration.minutes(1),
Expand Down
1 change: 1 addition & 0 deletions lib/stages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export class LisaServeApplicationStage extends Stage {
vpc: networkingStack.vpc.vpc,
});
apiBaseStack.addDependency(coreStack);
apiBaseStack.addDependency(serveStack);
stacks.push(apiBaseStack);

const apiDeploymentStack = new LisaApiDeploymentStack(this, 'LisaApiDeployment', {
Expand Down
4 changes: 2 additions & 2 deletions test/cdk/stacks/chat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe.each(regions)('Chat Nag Pack Tests | Region Test: %s', (awsRegion) => {

test('AwsSolutions CDK NAG Errors', () => {
const errors = Annotations.fromStack(stack).findError('*', Match.stringLikeRegexp('AwsSolutions-.*'));
expect(errors.length).toBe(29);
expect(errors.length).toBe(17);
});

test('NIST800.53r5 CDK NAG Warnings', () => {
Expand All @@ -120,6 +120,6 @@ describe.each(regions)('Chat Nag Pack Tests | Region Test: %s', (awsRegion) => {

test('NIST800.53r5 CDK NAG Errors', () => {
const errors = Annotations.fromStack(stack).findError('*', Match.stringLikeRegexp('NIST.*'));
expect(errors.length).toBe(17);
expect(errors.length).toBe(14);
});
});

0 comments on commit 36264e9

Please sign in to comment.