Skip to content

Commit 55abae3

Browse files
authored
feat: v2 redis components (#67)
* Create elasticache redis component * Create elasticache redis tests * Create upstash redis v2 component * Create upstash redis tests * Add elasticache redis arg documentation * Update typing and docs * Add default elasticache configuration tests * Implement elasticache redis ping integration test
1 parent c3c9f6d commit 55abae3

File tree

14 files changed

+2218
-243
lines changed

14 files changed

+2218
-243
lines changed

package-lock.json

Lines changed: 1445 additions & 238 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"@pulumi/pulumi": "^3.146.0",
4141
"@pulumi/random": "^4.17.0",
4242
"@pulumiverse/grafana": "^0.16.3",
43-
"@upstash/pulumi": "^0.3.14",
43+
"@upstash/pulumi": "^0.5.0",
4444
"yaml": "^2.7.1"
4545
},
4646
"devDependencies": {
@@ -51,13 +51,16 @@
5151
"@aws-sdk/client-ecs": "^3.766.0",
5252
"@aws-sdk/client-efs": "^3.758.0",
5353
"@aws-sdk/client-elastic-load-balancing-v2": "^3.764.0",
54+
"@aws-sdk/client-elasticache": "^3.901.0",
5455
"@aws-sdk/client-route-53": "^3.782.0",
56+
"@aws-sdk/client-secrets-manager": "^3.906.0",
5557
"@aws-sdk/client-servicediscovery": "^3.758.0",
5658
"@studion/prettier-config": "^0.1.0",
5759
"@types/node": "^22",
5860
"exponential-backoff": "^3.1.2",
5961
"http-status": "^2.1.0",
6062
"husky": "^9.1.7",
63+
"ioredis": "^5.8.1",
6164
"lint-staged": "^16.1.2",
6265
"nanospinner": "^1.2.2",
6366
"pathe": "^2.0.3",

src/components/redis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class Redis extends pulumi.ComponentResource {
2828
username = 'default';
2929

3030
constructor(name: string, args: RedisArgs, opts: RedisOptions) {
31-
super('studion:Redis', name, {}, opts);
31+
super('studion:LegacyRedis', name, {}, opts);
3232

3333
const project = pulumi.getProject();
3434
const stack = pulumi.getStack();
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import * as aws from '@pulumi/aws';
2+
import * as pulumi from '@pulumi/pulumi';
3+
import * as awsx from '@pulumi/awsx';
4+
import { commonTags } from '../../../constants';
5+
6+
type RedisArgs = {
7+
vpc: pulumi.Input<awsx.ec2.Vpc>;
8+
/**
9+
* Version number of the cache engine to be used
10+
* @default '7.1'
11+
*/
12+
engineVersion?: string;
13+
/**
14+
* Instance type for cache nodes
15+
* @default 'cache.t4g.micro'
16+
*/
17+
nodeType?: string;
18+
/**
19+
* The name of the parameter group to associate with this cache cluster.
20+
* @default 'default.redis7'
21+
*/
22+
parameterGroupName?: pulumi.Input<string>;
23+
tags?: pulumi.Input<{
24+
[key: string]: pulumi.Input<string>;
25+
}>;
26+
};
27+
28+
export const defaults = {
29+
engineVersion: '7.1',
30+
nodeType: 'cache.t4g.micro',
31+
parameterGroupName: 'default.redis7',
32+
};
33+
34+
export class ElastiCacheRedis extends pulumi.ComponentResource {
35+
name: string;
36+
vpc: pulumi.Output<awsx.ec2.Vpc>;
37+
cluster: aws.elasticache.Cluster;
38+
securityGroup: aws.ec2.SecurityGroup;
39+
subnetGroup: aws.elasticache.SubnetGroup;
40+
41+
constructor(
42+
name: string,
43+
args: RedisArgs,
44+
opts: pulumi.ComponentResourceOptions = {},
45+
) {
46+
super('studion:Redis:ElastiCache', name, {}, opts);
47+
const argsWithDefaults = Object.assign({}, defaults, args);
48+
49+
this.name = name;
50+
this.vpc = pulumi.output(argsWithDefaults.vpc);
51+
52+
this.securityGroup = this.createSecurityGroup();
53+
this.subnetGroup = this.createSubnetGroup();
54+
this.cluster = this.createRedisCluster(
55+
argsWithDefaults.engineVersion,
56+
argsWithDefaults.nodeType,
57+
argsWithDefaults.parameterGroupName,
58+
argsWithDefaults.tags,
59+
);
60+
61+
this.registerOutputs();
62+
}
63+
64+
private createRedisCluster(
65+
engineVersion: RedisArgs['engineVersion'],
66+
nodeType: RedisArgs['nodeType'],
67+
parameterGroupName: RedisArgs['parameterGroupName'],
68+
tags: RedisArgs['tags'],
69+
) {
70+
return new aws.elasticache.Cluster(
71+
`${this.name}-cluster`,
72+
{
73+
engine: 'redis',
74+
engineVersion: engineVersion,
75+
nodeType: nodeType,
76+
numCacheNodes: 1,
77+
securityGroupIds: [this.securityGroup.id],
78+
subnetGroupName: this.subnetGroup.name,
79+
parameterGroupName: parameterGroupName,
80+
port: 6379,
81+
tags: { ...commonTags, ...tags },
82+
},
83+
{ parent: this },
84+
);
85+
}
86+
87+
private createSubnetGroup() {
88+
return new aws.elasticache.SubnetGroup(
89+
`${this.name}-subnet-group`,
90+
{
91+
subnetIds: this.vpc.isolatedSubnetIds,
92+
tags: commonTags,
93+
},
94+
{ parent: this },
95+
);
96+
}
97+
98+
private createSecurityGroup() {
99+
return new aws.ec2.SecurityGroup(
100+
`${this.name}-security-group`,
101+
{
102+
vpcId: this.vpc.vpcId,
103+
ingress: [
104+
{
105+
protocol: 'tcp',
106+
fromPort: 6379,
107+
toPort: 6379,
108+
cidrBlocks: [this.vpc.vpc.cidrBlock],
109+
},
110+
],
111+
tags: commonTags,
112+
},
113+
{ parent: this },
114+
);
115+
}
116+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as pulumi from '@pulumi/pulumi';
2+
import * as upstash from '@upstash/pulumi';
3+
import { Password } from '../../../components/password';
4+
5+
export type RedisArgs = {
6+
dbName: pulumi.Input<string>;
7+
/**
8+
* Primary region for the database
9+
* @default 'us-east-1'
10+
*/
11+
primaryRegion?: pulumi.Input<
12+
| 'us-east-1'
13+
| 'us-west-1'
14+
| 'us-west-2'
15+
| 'eu-central-1'
16+
| 'eu-west-1'
17+
| 'sa-east-1'
18+
| 'ap-southeast-1'
19+
| 'ap-southeast-2'
20+
>;
21+
};
22+
23+
const defaults = {
24+
region: 'global',
25+
primaryRegion: 'us-east-1',
26+
};
27+
28+
export interface RedisOptions extends pulumi.ComponentResourceOptions {
29+
provider: upstash.Provider;
30+
}
31+
32+
export class UpstashRedis extends pulumi.ComponentResource {
33+
instance: upstash.RedisDatabase;
34+
password: Password;
35+
username = 'default';
36+
37+
constructor(name: string, args: RedisArgs, opts: RedisOptions) {
38+
super('studion:Redis:Upstash', name, {}, opts);
39+
40+
const argsWithDefaults = Object.assign({}, defaults, args);
41+
42+
const dbName =
43+
argsWithDefaults.dbName ?? `${pulumi.getProject()}-${pulumi.getStack()}`;
44+
45+
this.instance = new upstash.RedisDatabase(
46+
name,
47+
{
48+
databaseName: dbName,
49+
region: argsWithDefaults.region,
50+
primaryRegion: argsWithDefaults.primaryRegion,
51+
eviction: true,
52+
tls: true,
53+
},
54+
{ provider: opts.provider, parent: this },
55+
);
56+
57+
this.password = new Password(
58+
`${name}-database-password`,
59+
{ value: this.instance.password },
60+
{ parent: this },
61+
);
62+
63+
this.registerOutputs();
64+
}
65+
}

src/v2/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export { EcsService } from './components/ecs-service';
22
export { WebServer } from './components/web-server';
33
export { WebServerBuilder } from './components/web-server/builder';
44
export { WebServerLoadBalancer } from './components/web-server/load-balancer';
5+
export { ElastiCacheRedis } from './components/redis/elasticache-redis';
6+
export { UpstashRedis } from './components/redis/upstash-redis';
57

68
import { OtelCollectorBuilder } from './otel/builder';
79
import { OtelCollector } from './otel';

tests/ecs-service/index.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ const programArgs: InlineProgramArgs = {
2727
};
2828

2929
describe('EcsService component deployment', () => {
30-
const region = process.env.AWS_REGION || 'us-east-2';
30+
const region = process.env.AWS_REGION;
31+
if (!region) {
32+
throw new Error('AWS_REGION environment variable is required');
33+
}
34+
3135
const ctx: EcsTestContext = {
3236
outputs: {},
3337
config: {

tests/ecs-service/persistent-storage.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,10 @@ export function testEcsServiceWithStorage(ctx: EcsTestContext) {
260260
it('should successfully write to and read from EFS volume', async () => {
261261
const ecsServiceWithStorage = ctx.outputs.ecsServiceWithStorage.value;
262262
const clusterName = ctx.outputs.cluster.value.name;
263-
const region = process.env.AWS_REGION || 'us-east-2';
263+
const region = process.env.AWS_REGION;
264+
if (!region) {
265+
throw new Error('AWS_REGION environment variable is required');
266+
}
264267
const logsClient = new CloudWatchLogsClient({ region });
265268

266269
const listCommand = new ListTasksCommand({

0 commit comments

Comments
 (0)