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

fix: deployment resolution #11492

Merged
merged 11 commits into from
Nov 15, 2024
51 changes: 33 additions & 18 deletions packages/deployment-service/cdk/lib/cdk-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { createLambda } from './create-lambda'
import { createS3Buckets } from './create-S3-bucket'
import { createSqsQueues, QueueNames } from './create-sqs'
import { createPolicies } from './create-policies'
import { Role } from 'aws-cdk-lib/aws-iam'
import { Effect, Role } from 'aws-cdk-lib/aws-iam'
import config from '../../config.json'
import * as cdk from 'aws-cdk-lib'

Expand Down Expand Up @@ -71,11 +71,21 @@ export const createStack = async () => {

const api = createApi(stack, 'apigateway', undefined)
const vpc = createVpc(stack, 'vpc')
const buckets = createS3Buckets(stack, usercodeStack, envStage)
const buckets = createS3Buckets(usercodeStack, envStage)
const queues = createSqsQueues(stack)
const database = createDatabase(stack, 'database', databaseName, vpc)
const secretManager = database.secret

const AOC = new cdk.aws_cloudfront.CfnOriginAccessControl(usercodeStack, 's3-origin', {
originAccessControlConfig: {
name: 'distro-to-s3',
originAccessControlOriginType: 's3',
signingBehavior: 'always',
signingProtocol: 'sigv4',
description: 'Allow distros to access S3',
},
})

if (!secretManager) {
throw new Error('Failed to create rds secret')
}
Expand All @@ -101,7 +111,12 @@ export const createStack = async () => {
const functionSetups: { [s: string]: FunctionSetup } = {
sqs: {
handler: createFileLoc('sqs', 'handle'),
policies: [...policies.commonBackendPolicies, policies.cloudFrontPolicy, policies.route53Policy],
policies: [
...policies.commonBackendPolicies,
policies.cloudFrontPolicy,
policies.route53Policy,
policies.originAccessControlPolicy,
],
queues: [
queues[QueueNames.CODEBUILD_EXECUTOR],
queues[QueueNames.CODEBUILD_VERSION_DEPLOY],
Expand Down Expand Up @@ -239,23 +254,23 @@ export const createStack = async () => {
* is to add the migration script to a second stack which required the first stack
*/

// const migrationHandler = createLambda({
// stack,
// name: 'cloud-deployment-migration',
// entrypoint: 'bundle/migration-run.zip',
// handler: createFileLoc('migration-run', 'migrationRun'),
// runtime: aws_lambda.Runtime.NODEJS_18_X,
// env,
// vpc,
// })
const migrationHandler = createLambda({
stack,
name: 'cloud-deployment-migration',
entrypoint: 'bundle/migration-run.zip',
handler: createFileLoc('migration-run', 'migrationRun'),
runtime: aws_lambda.Runtime.NODEJS_18_X,
env,
vpc,
})

// policies.commonBackendPolicies.forEach((policy) => migrationHandler.addToRolePolicy(policy))
policies.commonBackendPolicies.forEach((policy) => migrationHandler.addToRolePolicy(policy))

// Object.values(policies)
// .filter((policy) => policy instanceof PolicyStatement)
// .forEach((policy) => migrationHandler.addToRolePolicy(policy as PolicyStatement))
Object.values(policies)
.filter((policy) => policy instanceof PolicyStatement)
.forEach((policy) => migrationHandler.addToRolePolicy(policy as PolicyStatement))

// const numberOfMigrations = await getNumberOfMigrations()
const numberOfMigrations = await getNumberOfMigrations()

// createStackEventHandler(stack, 'migration-event', migrationHandler, `${numberOfMigrations}`)
createStackEventHandler(stack, 'migration-event', migrationHandler, `${numberOfMigrations}`)
}
79 changes: 56 additions & 23 deletions packages/deployment-service/cdk/lib/create-S3-bucket.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Stack, Bucket, BucketOptions, PolicyStatement } from '@reapit/ts-scripts/src/cdk'
import { aws_s3, PhysicalName, aws_iam } from 'aws-cdk-lib'
import { aws_s3, PhysicalName, aws_iam, Stack } from 'aws-cdk-lib'
import { ServicePrincipal } from 'aws-cdk-lib/aws-iam'

export enum BucketNames {
LIVE = 'v2-cloud-deployment-live',
Expand All @@ -8,20 +8,33 @@ export enum BucketNames {
REPO_CACHE = 'v2-cloud-deployment-repo-cache',
}

export const createBucket = (stack: Stack, bucketName: string, options?: BucketOptions): aws_s3.Bucket => {
type BucketOptions = {
public?: boolean
get?: boolean
list?: boolean
put?: boolean
stack?: Stack
}

export const createBucket = ({
stack,
bucketName,
options,
}: {
stack: Stack
bucketName: string
options?: BucketOptions
}): aws_s3.Bucket => {
const bucket = new aws_s3.Bucket(options?.stack || stack, bucketName, {
publicReadAccess: true,
websiteIndexDocument: options?.public ? 'index.html' : undefined,
bucketName: bucketName || PhysicalName.GENERATE_IF_NEEDED,
// blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL,
// accessControl: aws_s3.BucketAccessControl.PRIVATE,
// objectOwnership: aws_s3.ObjectOwnership.OBJECT_WRITER,
blockPublicAccess: new aws_s3.BlockPublicAccess({
blockPublicAcls: false,
ignorePublicAcls: false,
blockPublicAccess: {
blockPublicPolicy: false,
blockPublicAcls: false,
restrictPublicBuckets: false,
}),
ignorePublicAcls: false,
},
publicReadAccess: false,
})

const actions: string[] = []
Expand All @@ -36,17 +49,37 @@ export const createBucket = (stack: Stack, bucketName: string, options?: BucketO
}

bucket.addToResourcePolicy(
new PolicyStatement({
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
actions,
resources: [bucket.arnForObjects('*')],
principals: [new aws_iam.ArnPrincipal('*')],
}),
)

// allows cloudfront to access this bucket
bucket.addToResourcePolicy(
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
actions: ['s3:Get*'],
resources: [bucket.arnForObjects('*')],
principals: [new ServicePrincipal('cloudfront.amazonaws.com')],
}),
)

bucket.addToResourcePolicy(
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
actions: ['s3:Get*'],
resources: [bucket.arnForObjects('*')],
principals: [new ServicePrincipal('codebuild.amazonaws.com')],
}),
)

return bucket
}

export const createS3Buckets = (stack: Stack, usercodeStack: Stack, envStage: string): Record<BucketNames, Bucket> => {
export const createS3Buckets = (usercodeStack: Stack, envStage: string): Record<BucketNames, aws_s3.IBucket> => {
const bucketOptions: {
[k in BucketNames]: BucketOptions
} = {
Expand All @@ -55,32 +88,32 @@ export const createS3Buckets = (stack: Stack, usercodeStack: Stack, envStage: st
get: true,
list: true,
put: true,
stack: usercodeStack,
},
[BucketNames.LOG]: {
put: true,
stack: usercodeStack,
},
[BucketNames.REPO_CACHE]: {
put: true,
get: true,
stack: usercodeStack,
},
[BucketNames.VERSION]: {
get: true,
list: true,
put: true,
stack: usercodeStack,
},
}

return (Object.keys(bucketOptions) as Array<BucketNames>).reduce<{ [k in BucketNames]: Bucket }>(
return (Object.keys(bucketOptions) as Array<BucketNames>).reduce<{ [k in BucketNames]: aws_s3.IBucket }>(
(buckets, bucketName) => {
buckets[bucketName] = createBucket(
bucketOptions[bucketName].stack || stack,
`${bucketName}-${envStage}`,
bucketOptions[bucketName],
)
// const existingBucket =
// Bucket.fromBucketName(usercodeStack, `lookup-${bucketName}`, `${bucketName}-${envStage}`)

// buckets[bucketName] = existingBucket ?? createBucket({
buckets[bucketName] = createBucket({
stack: usercodeStack,
bucketName: `${bucketName}-${envStage}`,
options: bucketOptions[bucketName],
})
return buckets
},
{} as any,
Expand Down
14 changes: 12 additions & 2 deletions packages/deployment-service/cdk/lib/create-policies.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Project, ISecret, Effect, PolicyStatement, Bucket, Stack, Topic } from '@reapit/ts-scripts/src/cdk'
import { Project, ISecret, Effect, PolicyStatement, Stack, Topic } from '@reapit/ts-scripts/src/cdk'
import { AccountPrincipal, ArnPrincipal, CompositePrincipal, Policy, Role } from 'aws-cdk-lib/aws-iam'
import config from '../../config.json'
import { aws_sqs as sqs } from 'aws-cdk-lib'
import { BucketNames } from './create-S3-bucket'
import { IBucket } from 'aws-cdk-lib/aws-s3'

export enum PolicyNames {
// lambdaInvoke = 'lambdaInvoke',
Expand All @@ -12,6 +13,7 @@ export enum PolicyNames {
sqsPolices = 'sqsPolicies',
secretManagerPolicy = 'secretManagerPolicy',
S3BucketPolicy = 'S3BucketPolicy',
originAccessControlPolicy = 'originAccessControlPolicy',
}

type namedPolicyType = {
Expand All @@ -32,7 +34,7 @@ export const createPolicies = ({
codebuildSnsTopic,
githubPemSecretArn,
}: {
buckets: { [s: string]: Bucket }
buckets: { [s: string]: IBucket }
queues: { [s: string]: sqs.IQueue }
secretManager: ISecret
codeBuild: Project
Expand Down Expand Up @@ -164,6 +166,12 @@ export const createPolicies = ({
],
})

const originAccessControlPolicy = new PolicyStatement({
actions: ['cloudfront:ListOriginAccessControls'],
effect: Effect.ALLOW,
resources: ['*'],
})

// create a policy that allows the lambda to do what it needs to do in the usercode stack
const usercodePolicy = new Policy(usercodeStack, 'UsercodePolicy')
usercodePolicy.addStatements(
Expand All @@ -173,6 +181,7 @@ export const createPolicies = ({
codebuildSnssubscriptionPolicy,
codebuildExecPolicy,
parameterStorePolicy,
originAccessControlPolicy,
)
const usercodeStackRoleName = `${usercodeStack.stackName}-UsercodeStackRole`
// create a role that lambdas can assume in the usercode stack, with the policy we just created
Expand Down Expand Up @@ -216,6 +225,7 @@ export const createPolicies = ({
commonBackendPolicies,
codebuildExecPolicy,
cloudFrontPolicy,
originAccessControlPolicy,
route53Policy,
sqsPolicies,
secretManagerPolicy,
Expand Down
10 changes: 9 additions & 1 deletion packages/deployment-service/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ module.exports = {
testPathIgnorePatterns: ['<rootDir>/dist'],
collectCoverageFrom: ['<rootDir>/src/**/*.ts'],
coverageDirectory: './src/tests/coverage',
coveragePathIgnorePatterns: ['<rootDir>[/\\\\](node_modules|src/types|src/tests|src/scripts)[/\\\\]', '.d.ts'],
coveragePathIgnorePatterns: [
'<rootDir>[/\\\\](node_modules|src/types|src/tests|src/scripts)[/\\\\]',
'.d.ts',
'src/http.ts',
'src/sqs.ts',
'src/sns.ts',
'src/local-http.ts',
'src/migration-run.ts',
],
reporters: ['default', 'github-actions'],
coverageThreshold: {
global: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export class DeployProvider {
Bucket: process.env.DEPLOYMENT_LIVE_BUCKET_NAME as string,
Key: `${prefix}/${fileName}`,
Body: buffer,
ACL: 'public-read',
// ACL: 'public-read',
ContentType: mimeType,
Metadata: {
['Content-Type']: mimeType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PipelineSetupWorkflow } from '../pipeline-setup-workflow'
import { PusherProvider, SqsProvider } from '../../events'
import { PipelineProvider } from '../pipeline-provider'
import { SQS, S3 } from 'aws-sdk'
import { CloudFrontClient } from '@aws-sdk/client-cloudfront'
import { CloudFrontClient, ListOriginAccessControlsCommand } from '@aws-sdk/client-cloudfront'
import { Route53Client } from '@aws-sdk/client-route-53'
import { S3Provider } from '../../s3'
import { v4 as uuid } from 'uuid'
Expand Down Expand Up @@ -84,12 +84,28 @@ describe('PipelineSetupWorkflow', () => {
const pipelineId = uuid()
const developerId = uuid()

mockCloudFrontClient.send.mockImplementationOnce(() => ({
Distribution: {
DomainName: 'domain',
Id: 'id',
},
}))
mockCloudFrontClient.send.mockImplementation((event) => {
console.log('event', event)
if (event instanceof ListOriginAccessControlsCommand) {
return {
OriginAccessControlList: {
Items: [
{
Name: 'distro-to-s3',
Id: 'AOCID',
},
],
},
}
}

return {
Distribution: {
DomainName: 'domain',
Id: 'id',
},
}
})

mockRoute53Client.send.mockImplementationOnce(() => ({
ChangeInfo: {
Expand Down
Loading
Loading