Skip to content

Smoke test Gantry services #11

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/infra/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ app.synth();

## Usage

### Gantry service

In a Gantry resource file:

```diff
kind: Service

# ...

+ hooks:
+ beforeAllowTraffic: aws-codedeploy-hook-BeforeAllowTraffic
```

### Lambda function (CDK)

In a CDK stack:
Expand Down
1 change: 1 addition & 0 deletions packages/infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"aws-sdk-client-mock": "3.1.0-beta.0",
"aws-sdk-client-mock-jest": "3.1.0-beta.0",
"isomorphic-git": "1.25.3",
"msw": "2.0.12",
"pino-pretty": "10.3.1"
},
"skuba": {
Expand Down
4 changes: 2 additions & 2 deletions packages/infra/src/constructs/lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'path';

import { Duration, aws_lambda } from 'aws-cdk-lib';

export const LAMBDA_HOOK_PROPS: aws_lambda.FunctionProps = {
export const createLambdaHookProps = (): aws_lambda.FunctionProps => ({
code: aws_lambda.Code.fromAsset(
path.join(__dirname, '..', 'assets', 'handlers'),
),
Expand All @@ -19,4 +19,4 @@ export const LAMBDA_HOOK_PROPS: aws_lambda.FunctionProps = {
runtime: aws_lambda.Runtime.NODEJS_20_X,

timeout: Duration.seconds(300),
};
});
135 changes: 135 additions & 0 deletions packages/infra/src/constructs/network.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Stack } from 'aws-cdk-lib';
import { Vpc } from 'aws-cdk-lib/aws-ec2';

import { getNetworkConfig } from './network';

describe('getNetworkConfig', () => {
const fromLookup = jest
.spyOn(Vpc, 'fromLookup')
.mockImplementation((scope, id) => new Vpc(scope, id));

afterEach(fromLookup.mockClear);

it('processes the development Managed Network', () => {
const construct = new Stack();

expect(
getNetworkConfig(construct, {
type: 'seek-managed-network',
name: 'development',
}),
).toMatchInlineSnapshot(
{ vpc: expect.any(Object) },
`
{
"description": "BeforeAllowTraffic hook deployed to the development managed network",
"suffix": "-managed-network-dev",
"vpc": Any<Object>,
}
`,
);

expect(fromLookup).toHaveBeenCalledTimes(1);

const [constructArg, ...restArgs] = fromLookup.mock.lastCall!;

expect(constructArg).toBe(construct);
expect(restArgs).toMatchInlineSnapshot(`
[
"Vpc-managed-network-dev",
{
"tags": {
"Name": "Gantry Development Managed Network",
},
"vpcName": "Gantry Development Managed Network",
},
]
`);
});

it('processes the production Managed Network', () => {
const construct = new Stack();

expect(
getNetworkConfig(construct, {
type: 'seek-managed-network',
name: 'production',
}),
).toMatchInlineSnapshot(
{ vpc: expect.any(Object) },
`
{
"description": "BeforeAllowTraffic hook deployed to the production managed network",
"suffix": "-managed-network-prod",
"vpc": Any<Object>,
}
`,
);

expect(fromLookup).toHaveBeenCalledTimes(1);

const [constructArg, ...restArgs] = fromLookup.mock.lastCall!;

expect(constructArg).toBe(construct);
expect(restArgs).toMatchInlineSnapshot(`
[
"Vpc-managed-network-prod",
{
"tags": {
"Name": "Gantry Production Managed Network",
},
"vpcName": "Gantry Production Managed Network",
},
]
`);
});

it('processes a custom VPC', () => {
const construct = new Stack();

expect(
getNetworkConfig(construct, {
type: 'vpc',
id: 'mock-id',
label: 'mock-label',
}),
).toMatchInlineSnapshot(
{ vpc: expect.any(Object) },
`
{
"description": "BeforeAllowTraffic hook deployed to the mock-label VPC (mock-id)",
"suffix": "-vpc-mock-label",
"vpc": Any<Object>,
}
`,
);

expect(fromLookup).toHaveBeenCalledTimes(1);

const [constructArg, ...restArgs] = fromLookup.mock.lastCall!;

expect(constructArg).toBe(construct);
expect(restArgs).toMatchInlineSnapshot(`
[
"Vpc-vpc-mock-label",
{
"vpcId": "mock-id",
},
]
`);
});

it('processes null', () => {
const construct = new Stack();

expect(getNetworkConfig(construct, null)).toMatchInlineSnapshot(`
{
"description": "BeforeAllowTraffic hook deployed outside of a VPC",
"suffix": "",
"vpc": undefined,
}
`);

expect(fromLookup).not.toHaveBeenCalled();
});
});
96 changes: 96 additions & 0 deletions packages/infra/src/constructs/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import type { Construct } from 'constructs';

export type Network =
| {
/**
* A VPC in the Managed Network.
*/
type: 'seek-managed-network';

/**
* The name of the Managed Network environment.
*/
name: 'development' | 'production';
}
| {
/**
* A custom VPC.
*/
type: 'vpc';

/**
* The ID of the VPC.
*/
id: string;

/**
* A kebab-cased label for this network.
*
* This is used as a suffix for the Lambda function name and has a maximum
* length of 21 characters.
*
* ```bash
* aws-codedeploy-hook-BeforeAllowTraffic-vpc-${label}
* ```
*/
label: string;
};

const SEEK_MANAGED_NETWORK_NAMES = {
development: 'Gantry Development Managed Network',
production: 'Gantry Production Managed Network',
};

const SEEK_MANAGED_NETWORK_SUFFIXES = {
development: 'dev',
production: 'prod',
};

export const getNetworkConfig = (
scope: Construct,
network: Network | null,
): {
description: string;
suffix: string;
vpc: ec2.IVpc | undefined;
} => {
switch (network?.type) {
case 'seek-managed-network': {
const name = SEEK_MANAGED_NETWORK_NAMES[network.name];

const suffix = `-managed-network-${
SEEK_MANAGED_NETWORK_SUFFIXES[network.name]
}`;

return {
description: `BeforeAllowTraffic hook deployed to the ${network.name} managed network`,
suffix,
vpc: ec2.Vpc.fromLookup(scope, `Vpc${suffix}`, {
tags: { Name: name },
vpcName: name,
}),
};
}

case 'vpc': {
const suffix = `-vpc-${network.label}`;

return {
description: `BeforeAllowTraffic hook deployed to the ${network.label} VPC (${network.id})`,
suffix,
vpc: ec2.Vpc.fromLookup(scope, `Vpc${suffix}`, {
vpcId: network.id,
}),
};
}

case undefined: {
return {
description: 'BeforeAllowTraffic hook deployed outside of a VPC',
suffix: '',
vpc: undefined,
};
}
}
};
34 changes: 33 additions & 1 deletion packages/infra/src/constructs/stack.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, assertions } from 'aws-cdk-lib';
import { App, assertions, aws_ec2 } from 'aws-cdk-lib';

import { HookStack } from './stack';

Expand All @@ -24,3 +24,35 @@ it('returns expected CloudFormation stack', () => {

expect(JSON.parse(json)).toMatchSnapshot();
});

it('supports additional networks', () => {
jest
.spyOn(aws_ec2.Vpc, 'fromLookup')
.mockImplementation((scope, id) => new aws_ec2.Vpc(scope, id));

const app = new App();

const stack = new HookStack(app, undefined, {
additionalNetworks: [
{
type: 'seek-managed-network',
name: 'development',
},
{
type: 'vpc',
id: 'mock-id',
label: 'mock-label',
},
],
});

const template = assertions.Template.fromStack(stack);

template.resourceCountIs('AWS::Lambda::Function', 3);

template.resourcePropertiesCountIs(
'AWS::Lambda::Function',
{ VpcConfig: {} },
2,
);
});
Loading