Skip to content

Commit

Permalink
feat: Configure basic Hydro survey stack
Browse files Browse the repository at this point in the history
Uses human-friendly bucket names with an account suffix only in
non-production accounts.
  • Loading branch information
l0b0 committed Sep 27, 2023
1 parent bb49258 commit a3e9b83
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 11 deletions.
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@linz:registry=https://npm.pkg.github.com/
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

Code to deploy AWS LI Hydro surveys infrastructure.

## Infrastructure

The result of deploying this should be a stack called "HydroSurveyStack" containing the following:

- A landing zone bucket where survey providers can upload their surveys.
- A processing bucket where Hydro employees can work on surveys.
- A consumption bucket where the public can retrieve survey processing outputs.

## Setup

Prerequisites:
Expand Down
47 changes: 40 additions & 7 deletions lib/hydro-survey-stack.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,49 @@
import { Stack, StackProps } from 'aws-cdk-lib';
import { Duration, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';

Check failure on line 1 in lib/hydro-survey-stack.ts

View workflow job for this annotation

GitHub Actions / build

Run autofix to sort these imports!
import { Bucket, StorageClass } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
import { AwsAccounts } from '@linz/accounts';
import { LinzAccountName } from '@linz/accounts/dist/aws.account.name';

export const surveyLandingZoneBucketLogicalId = 'SurveyLandingZone';
export const surveyProcessingBucketLogicalId = 'SurveyProcessing';
export const surveyPublicationsBucketLogicalId = 'SurveyPublications';

export class HydroSurveyStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);

// The code that defines your stack goes here
let bucketNameSuffix = '';
if (props?.env?.account !== AwsAccounts.byNameProd(LinzAccountName.HydroSurveys).id) {
bucketNameSuffix = `-${props?.env?.account}`;
}

// example resource
// const queue = new sqs.Queue(this, 'HydroSurveyQueue', {
// visibilityTimeout: cdk.Duration.seconds(300)
// });
new Bucket(this, surveyLandingZoneBucketLogicalId, {
bucketName: `ttwlinz-hydro-survey-landing-zone${bucketNameSuffix}`,
removalPolicy: RemovalPolicy.RETAIN,
});
new Bucket(this, surveyProcessingBucketLogicalId, {
bucketName: `ttwlinz-hydro-survey-processing${bucketNameSuffix}`,
removalPolicy: RemovalPolicy.RETAIN,
versioned: true,
});
new Bucket(this, surveyPublicationsBucketLogicalId, {
bucketName: `ttwlinz-hydro-survey-publications${bucketNameSuffix}`,
lifecycleRules: [
{
transitions: [
{
storageClass: StorageClass.INFREQUENT_ACCESS,
transitionAfter: Duration.days(365),
},
{
storageClass: StorageClass.GLACIER,
transitionAfter: Duration.days(8 * 365),
},
],
},
],
removalPolicy: RemovalPolicy.RETAIN,
versioned: true,
});
}
}
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"organization": true
},
"dependencies": {
"@linz/accounts": "^3.37.0",
"@linzjs/style": "^5.0.0",
"@types/node": "^20.7.0",
"aws-cdk": "2.97.0",
Expand Down
114 changes: 110 additions & 4 deletions test/hydro-survey-stack.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,85 @@
import { test } from 'node:test';

import { App } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { CfnBucket } from 'aws-cdk-lib/aws-s3';

import { HydroSurveyStack } from '../lib/hydro-survey-stack';
import {
HydroSurveyStack,
surveyLandingZoneBucketLogicalId,
surveyProcessingBucketLogicalId,
surveyPublicationsBucketLogicalId,
} from '../lib/hydro-survey-stack';

test('Should be able to instantiate stack', () => {
const app = new App();
new HydroSurveyStack(app, anyStackId());
const app = new App();

const stack = new HydroSurveyStack(app, anyStackId(), {
env: { account: anyAwsAccountId(), region: anyAwsRegionName() },
});

const template = Template.fromStack(stack);

test('Template configuration', () => {
template.templateMatches({
Resources: {
[getLogicalId(surveyLandingZoneBucketLogicalId)]: {
DeletionPolicy: 'Retain',
UpdateReplacePolicy: 'Retain',
},
[getLogicalId(surveyProcessingBucketLogicalId)]: {
DeletionPolicy: 'Retain',
UpdateReplacePolicy: 'Retain',
Properties: {
VersioningConfiguration: {
Status: 'Enabled',
},
},
},
[getLogicalId(surveyPublicationsBucketLogicalId)]: {
DeletionPolicy: 'Retain',
UpdateReplacePolicy: 'Retain',
Properties: {
LifecycleConfiguration: {
Rules: [
{
Status: 'Enabled',
Transitions: [
{
StorageClass: 'STANDARD_IA',
TransitionInDays: 365,
},
{
StorageClass: 'GLACIER',
TransitionInDays: 8 * 365,
},
],
},
],
},
VersioningConfiguration: {
Status: 'Enabled',
},
},
},
},
});
});

function getLogicalId(constructId: string): string {
return stack.getLogicalId(stack.node.tryFindChild(constructId)?.node.defaultChild as CfnBucket);
}

function anyAwsAccountId(): string {
// 12 digits, starting with a non-zero digit
const digits = [1 + anyNonNegativeInteger(9)];

for (let counter = 0; counter < 11; counter++) {
digits.push(anyNonNegativeInteger(10));
}

return digits.join('');
}

function anyStackId(): string {
// 1 to 64 alphanumeric characters, starting with an alpha character
const alphas = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
Expand All @@ -22,6 +93,41 @@ function anyStackId(): string {
return result;
}

function anyAwsRegionName(): string {
const allRegions = [
'af-south-1',
'ap-east-1',
'ap-northeast-1',
'ap-northeast-2',
'ap-northeast-3',
'ap-south-1',
'ap-south-2',
'ap-southeast-1',
'ap-southeast-2',
'ap-southeast-3',
'ap-southeast-4',
'ca-central-1',
'eu-central-1',
'eu-central-2',
'eu-north-1',
'eu-south-1',
'eu-south-2',
'eu-west-1',
'eu-west-2',
'eu-west-3',
'il-central-1',
'me-central-1',
'me-south-1',
'sa-east-1',
'us-east-1',
'us-east-2',
'us-west-1',
'us-west-2',
];
const randomIndex = anyNonNegativeInteger(allRegions.length);
return allRegions[randomIndex]!;
}

function anyNonNegativeInteger(stop: number): number {
return Math.floor(Math.random() * stop);
}

0 comments on commit a3e9b83

Please sign in to comment.