Garbage In, Garbage Out

On two occasions I have been asked [by members of Parliament], ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?’ I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.

Charles BabbagePassages from the Life of a Philosopher

I’m updating an older stack in CDK and is my custom I’m writing tests. When writing tests for an existing stack, I’ll incorporate the output from the deployed Cloudformation templates. This ensures that the test is realistic and works toward my main goal, which is to minimize disruption to the infrastructure when updating the code. A good example is security groups–changing the description is enough to require replacement. If you have a multi-stack deployment with ingress/egress relationships between security groups, that will cause problems. Testing for that upfront saves time.

When working with a CDK application, you can have code in three places:

  • lib/ and subdirectories: application code
  • bin/: props for deploying your stacks
  • test/: props for testing your code, and the test code itself

I want to focus on bin and test. For this testing strategy to work, your test properties need to follow what you have in bin closely. These are simplified props for a stack that will create an EFS access point and mount it in a container:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
accessPoints: {
'/var/data': {
createAcl: {
ownerGid: '1000',
ownerUid: '48',
permissions: '0770',
},
path: '/data',
posixUser: {
gid: '1000',
uid: '48',
}
}
},
cpu: 256,
instanceCount: 1,
memoryLimit: 512,

I can test for the existence of that mount (and associated filesystem) like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const stack = new stack(props);
const template = Template.fromStack(stack);
template.hasResourceProperties("AWS::ECS::TaskDefinition", {
"ContainerDefinitions": [
{
...
"MountPoints": [
{
"ContainerPath": "/var/data",
"ReadOnly": false,
"SourceVolume": {
"Fn::Join": [
"",
[
"efs",
{
"Fn::ImportValue": "Stack:ExportsOutputRefFoo"
}
]
]
}
}
],
...
});

This was a substantial refactoring, but I was still surprised when the test failed because no mount points could be found. I debugged various parts of the code before finally looking at the test props that I was feeding the test:

1
2
3
4
accessPoints: {},
cpu: 256,
instanceCount: 1,
memoryLimit: 512,

The moral of the story? Verify your input before you start debugging. The code did exactly what I asked it to do.