@infoxchange/make-it-so 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,14 +44,14 @@ if (deployConfig.isIxDeploy) {
44
44
 
45
45
  Deploys a serverless instance of a Next.js. IxNextjsSite extends [SST's NextjsSite](https://docs.sst.dev/constructs/NextjsSite) and takes the exact same props.
46
46
 
47
- It will automatically create certificates and DNS records for any custom domains given (including alternative domain names which SST doesn't currently do). If the props `customDomain` is not set the first site domain provided by the IX deployment pipeline will be used as the primary custom domain and if there is more than one domain the rest will be used as alternative domain names. Explicitly setting `customDomain` to `undefined` will
47
+ It will automatically create certificates and DNS records for any custom domains given (including alternative domain names which SST doesn't currently do). If the props `customDomain` is not set the first site domain provided by the IX deployment pipeline will be used as the primary custom domain and if there is more than one domain the rest will be used as alternative domain names. Explicitly setting `customDomain` to `undefined` will ensure no customDomain is used.
48
48
 
49
49
  It will also automatically attach the site to the standard IX VPC created in each workload account (unless you explicitly pass other VPC details or set the VPC-related props (see the SST doco) to `undefined`).
50
50
 
51
51
  ```typescript
52
52
  import { IxNextjsSite } from "@infoxchange/make-it-so/cdk-constructs";
53
53
 
54
- const site = new IxNextjsSite(stack, "site", {
54
+ const site = new IxNextjsSite(stack, "Site", {
55
55
  environment: {
56
56
  DATABASE_URL: process.env.DATABASE_URL || "",
57
57
  SESSION_SECRET: process.env.SESSION_SECRET || "",
@@ -64,6 +64,38 @@ const site = new IxNextjsSite(stack, "site", {
64
64
  });
65
65
  ```
66
66
 
67
+ ### CDK Construct - IxElasticache
68
+
69
+ Deploys an AWS Elasticache cluster, either the redis or the memcached flavour.
70
+
71
+ It will also automatically attach the cluster to the standard IX VPC created in each workload account (unless you explicitly pass a different VPC to be attached with the vpc prop or set the vpc prop to `undefined` which will stop any VPC being attached).
72
+
73
+ ```typescript
74
+ import { IxElasticache } from "@infoxchange/make-it-so/cdk-constructs";
75
+
76
+ const redisCluster = new IxElasticache(stack, "elasticache", {
77
+ autoMinorVersionUpgrade: true,
78
+ cacheNodeType: "cache.t2.small",
79
+ engine: "redis",
80
+ numCacheNodes: 1,
81
+ });
82
+ ```
83
+
84
+ #### Options:
85
+
86
+ | Prop | Type | Description |
87
+ | ------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
88
+ | vpc | IVpc | (optional) A VPC to attach if not using default IX VPC |
89
+ | vpcSubnetIds | string[] | (optional) List of IDs of subnets to be used if not using default IX VPC subnets |
90
+ | [...CfnCacheClusterProps] | | Any props accepted by [CfnCacheCluster](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_elasticache.CfnCacheCluster.html#construct-props) |
91
+
92
+ #### Properties:
93
+
94
+ | Properties | Type | Description |
95
+ | ---------------- | --------------- | ---------------------------------------------------------------- |
96
+ | connectionString | string | A string with all the details required to connect to the cluster |
97
+ | cluster | CfnCacheCluster | An AWS CDK CfnCacheCluster instance |
98
+
67
99
  ### CDK Construct - IxCertificate
68
100
 
69
101
  Creates a new DNS validated ACM certificate for a domain managed by IX.
@@ -71,13 +103,15 @@ Creates a new DNS validated ACM certificate for a domain managed by IX.
71
103
  ```typescript
72
104
  import { IxCertificate } from "@infoxchange/make-it-so/cdk-constructs";
73
105
 
74
- const domainCert = new IxCertificate(scope, "IxCertificate", {
106
+ const domainCert = new IxCertificate(scope, "ExampleDotComCertificate", {
75
107
  domainName: "example.com",
76
108
  subjectAlternativeNames: ["other-domain.com"],
77
109
  region: "us-east-1",
78
110
  });
79
111
  ```
80
112
 
113
+ #### Options:
114
+
81
115
  | Prop | Type | Description |
82
116
  | ----------------------- | -------- | --------------------------------------------------------------- |
83
117
  | domainName | string | Domain name for cert |
@@ -91,7 +125,7 @@ Creates a DNS record for a domain managed by IX. Route53 HostedZones for IX mana
91
125
  ```typescript
92
126
  import { IxDnsRecord } from "@infoxchange/make-it-so/cdk-constructs";
93
127
 
94
- new IxDnsRecord(scope, `DnsRecord-${domainNameLogicalId}`, {
128
+ new IxDnsRecord(scope, "IxDnsRecord", {
95
129
  type: "A",
96
130
  name: "example.com",
97
131
  value: "1.1.1.1",
@@ -99,6 +133,8 @@ new IxDnsRecord(scope, `DnsRecord-${domainNameLogicalId}`, {
99
133
  });
100
134
  ```
101
135
 
136
+ #### Options:
137
+
102
138
  | Prop | Type | Description |
103
139
  | ------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104
140
  | type | "A" \| "CNAME" \| "NS" \| "SOA" \| "ALIAS" | DNS record type |
@@ -115,9 +151,11 @@ Fetches the standard VPC and subnets that exist in all IX workload aws accounts.
115
151
  ```typescript
116
152
  import { IxVpcDetails } from "@infoxchange/make-it-so/cdk-constructs";
117
153
 
118
- const vpcDetails = new IxVpcDetails(scope, id + "-IxVpcDetails");
154
+ const vpcDetails = new IxVpcDetails(scope, "VpcDetails");
119
155
  ```
120
156
 
157
+ #### Options:
158
+
121
159
  | Prop | Type | Description |
122
160
  | ----------------------- | -------- | --------------------------------------------------------------- |
123
161
  | domainName | string | Domain name for cert |
@@ -0,0 +1,17 @@
1
+ import { Construct } from "constructs";
2
+ import { CfnCacheCluster } from "aws-cdk-lib/aws-elasticache";
3
+ import { IVpc } from "aws-cdk-lib/aws-ec2";
4
+ type ConstructScope = ConstructorParameters<typeof Construct>[0];
5
+ type ConstructId = ConstructorParameters<typeof Construct>[1];
6
+ type CacheClusterProps = ConstructorParameters<typeof CfnCacheCluster>[2];
7
+ type Props = CacheClusterProps & {
8
+ vpc?: IVpc;
9
+ vpcSubnetIds?: string[];
10
+ };
11
+ export declare class IxElasticache extends Construct {
12
+ cluster: CfnCacheCluster;
13
+ connectionString: string;
14
+ constructor(scope: ConstructScope, id: ConstructId, { vpc, vpcSubnetIds, ...elasticacheProps }: Props);
15
+ }
16
+ export {};
17
+ //# sourceMappingURL=IxElasticache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IxElasticache.d.ts","sourceRoot":"","sources":["../../src/cdk-constructs/IxElasticache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,eAAe,EAAkB,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,IAAI,EAA6B,MAAM,qBAAqB,CAAC;AAMtE,KAAK,cAAc,GAAG,qBAAqB,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE,KAAK,WAAW,GAAG,qBAAqB,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,KAAK,iBAAiB,GAAG,qBAAqB,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1E,KAAK,KAAK,GAAG,iBAAiB,GAAG;IAC/B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,qBAAa,aAAc,SAAQ,SAAS;IAC1C,OAAO,EAAE,eAAe,CAAC;IAEzB,gBAAgB,EAAE,MAAM,CAAC;gBAGvB,KAAK,EAAE,cAAc,EACrB,EAAE,EAAE,WAAW,EACf,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,EAAE,KAAK;CAqFpD"}
@@ -0,0 +1,70 @@
1
+ import { Construct } from "constructs";
2
+ import { CfnCacheCluster, CfnSubnetGroup } from "aws-cdk-lib/aws-elasticache";
3
+ import { SecurityGroup, Peer, Port } from "aws-cdk-lib/aws-ec2";
4
+ import { StringParameter } from "aws-cdk-lib/aws-ssm";
5
+ import { Stack } from "aws-cdk-lib";
6
+ import { IxVpcDetails } from "./IxVpcDetails.js";
7
+ import deployConfig from "../deployConfig.js";
8
+ export class IxElasticache extends Construct {
9
+ cluster;
10
+ connectionString;
11
+ constructor(scope, id, { vpc, vpcSubnetIds, ...elasticacheProps }) {
12
+ super(scope, id);
13
+ // Setup cluster name
14
+ if (!elasticacheProps.clusterName && deployConfig.isIxDeploy) {
15
+ elasticacheProps.clusterName = `${Stack.of(this).stackName}`;
16
+ }
17
+ if (!elasticacheProps.cacheSubnetGroupName) {
18
+ // Setup VPC
19
+ if (!vpc && deployConfig.isIxDeploy) {
20
+ const vpcDetails = new IxVpcDetails(scope, id + "-IxVpcDetails");
21
+ vpc = vpcDetails.vpc;
22
+ }
23
+ // Setup VPC subnets
24
+ if (vpc && !vpcSubnetIds) {
25
+ if (deployConfig.isIxDeploy) {
26
+ vpcSubnetIds = [1, 2, 3].map((subnetNum) => StringParameter.valueForStringParameter(scope, `/vpc/subnet/private-${deployConfig.workloadGroup}/${subnetNum}/id`));
27
+ }
28
+ else {
29
+ vpcSubnetIds = vpc.privateSubnets.map((subnet) => subnet.subnetId);
30
+ if (!vpcSubnetIds.length) {
31
+ throw Error(`The vpc ${vpc.vpcId} has no private subnets.`);
32
+ }
33
+ }
34
+ }
35
+ }
36
+ // Setup cluster security group
37
+ if (vpc && vpcSubnetIds) {
38
+ const subnetGroup = new CfnSubnetGroup(scope, "ElasticacheSubnetGroup", {
39
+ subnetIds: vpcSubnetIds,
40
+ description: "Subnet group for redis",
41
+ });
42
+ elasticacheProps.cacheSubnetGroupName = subnetGroup.ref;
43
+ const namePrefix = elasticacheProps.clusterName
44
+ ? elasticacheProps.clusterName
45
+ : `${Stack.of(this).stackName}`;
46
+ const securityGroup = new SecurityGroup(scope, "ElasticacheSecurityGroup", {
47
+ vpc,
48
+ allowAllOutbound: true,
49
+ description: "Security group for Elasticache Cluster",
50
+ securityGroupName: `${namePrefix}-elasticache`,
51
+ });
52
+ for (const subnetIndex of [1, 2, 3]) {
53
+ const cidr = StringParameter.valueForStringParameter(scope, `/vpc/subnet/private-${deployConfig.workloadGroup}/${subnetIndex}/cidr`);
54
+ securityGroup.addIngressRule(Peer.ipv4(cidr), Port.tcp(6379), `Allow access to Elasticache cluster from private ${deployConfig.workloadGroup} subnet`);
55
+ }
56
+ elasticacheProps.vpcSecurityGroupIds = [securityGroup.securityGroupId];
57
+ }
58
+ // Create Redis Cluster
59
+ this.cluster = new CfnCacheCluster(scope, "RedisCluster", elasticacheProps);
60
+ if (elasticacheProps.engine === "redis") {
61
+ this.connectionString = `redis://${this.cluster.attrRedisEndpointAddress}:${this.cluster.attrRedisEndpointPort}`;
62
+ }
63
+ else if (elasticacheProps.engine === "memcached") {
64
+ this.connectionString = `${this.cluster.attrConfigurationEndpointAddress}:${this.cluster.attrConfigurationEndpointPort}`;
65
+ }
66
+ else {
67
+ throw Error(`Unsupported engine: ${elasticacheProps.engine}`);
68
+ }
69
+ }
70
+ }
@@ -15,7 +15,7 @@ export class IxVpcDetails extends Construct {
15
15
  return Vpc.fromLookup(scope, id + "-Vpc", { vpcId });
16
16
  }
17
17
  getVpcSubnet(scope) {
18
- const vpcSubnetIds = [1, 2, 3].map((subnetNum) => StringParameter.valueFromLookup(scope, `/vpc/subnet/private-${ixDeployConfig.workloadGroup}/${subnetNum}/id`));
18
+ const vpcSubnetIds = [1, 2, 3].map((subnetNum) => StringParameter.valueForStringParameter(scope, `/vpc/subnet/private-${ixDeployConfig.workloadGroup}/${subnetNum}/id`));
19
19
  return {
20
20
  subnetFilters: [SubnetFilter.byIds(vpcSubnetIds)],
21
21
  };
@@ -2,4 +2,5 @@ export * from "./IxVpcDetails.js";
2
2
  export * from "./IxCertificate.js";
3
3
  export * from "./IxDnsRecord.js";
4
4
  export * from "./IxNextjsSite.js";
5
+ export * from "./IxElasticache.js";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cdk-constructs/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cdk-constructs/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
@@ -2,3 +2,4 @@ export * from "./IxVpcDetails.js";
2
2
  export * from "./IxCertificate.js";
3
3
  export * from "./IxDnsRecord.js";
4
4
  export * from "./IxNextjsSite.js";
5
+ export * from "./IxElasticache.js";
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@infoxchange/make-it-so",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Makes deploying services to IX infra easy",
5
+ "repository": "github:infoxchange/make-it-so",
5
6
  "type": "module",
6
7
  "scripts": {
7
8
  "build": "tsc",
@@ -0,0 +1,111 @@
1
+ import { Construct } from "constructs";
2
+ import { CfnCacheCluster, CfnSubnetGroup } from "aws-cdk-lib/aws-elasticache";
3
+ import { IVpc, SecurityGroup, Peer, Port } from "aws-cdk-lib/aws-ec2";
4
+ import { StringParameter } from "aws-cdk-lib/aws-ssm";
5
+ import { Stack } from "aws-cdk-lib";
6
+ import { IxVpcDetails } from "./IxVpcDetails.js";
7
+ import deployConfig from "../deployConfig.js";
8
+
9
+ type ConstructScope = ConstructorParameters<typeof Construct>[0];
10
+ type ConstructId = ConstructorParameters<typeof Construct>[1];
11
+ type CacheClusterProps = ConstructorParameters<typeof CfnCacheCluster>[2];
12
+
13
+ type Props = CacheClusterProps & {
14
+ vpc?: IVpc;
15
+ vpcSubnetIds?: string[];
16
+ };
17
+
18
+ export class IxElasticache extends Construct {
19
+ cluster: CfnCacheCluster;
20
+
21
+ connectionString: string;
22
+
23
+ constructor(
24
+ scope: ConstructScope,
25
+ id: ConstructId,
26
+ { vpc, vpcSubnetIds, ...elasticacheProps }: Props,
27
+ ) {
28
+ super(scope, id);
29
+
30
+ // Setup cluster name
31
+ if (!elasticacheProps.clusterName && deployConfig.isIxDeploy) {
32
+ elasticacheProps.clusterName = `${Stack.of(this).stackName}`;
33
+ }
34
+
35
+ if (!elasticacheProps.cacheSubnetGroupName) {
36
+ // Setup VPC
37
+ if (!vpc && deployConfig.isIxDeploy) {
38
+ const vpcDetails = new IxVpcDetails(scope, id + "-IxVpcDetails");
39
+ vpc = vpcDetails.vpc;
40
+ }
41
+
42
+ // Setup VPC subnets
43
+ if (vpc && !vpcSubnetIds) {
44
+ if (deployConfig.isIxDeploy) {
45
+ vpcSubnetIds = [1, 2, 3].map((subnetNum) =>
46
+ StringParameter.valueForStringParameter(
47
+ scope,
48
+ `/vpc/subnet/private-${deployConfig.workloadGroup}/${subnetNum}/id`,
49
+ ),
50
+ );
51
+ } else {
52
+ vpcSubnetIds = vpc.privateSubnets.map((subnet) => subnet.subnetId);
53
+
54
+ if (!vpcSubnetIds.length) {
55
+ throw Error(`The vpc ${vpc.vpcId} has no private subnets.`);
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ // Setup cluster security group
62
+ if (vpc && vpcSubnetIds) {
63
+ const subnetGroup = new CfnSubnetGroup(scope, "ElasticacheSubnetGroup", {
64
+ subnetIds: vpcSubnetIds,
65
+ description: "Subnet group for redis",
66
+ });
67
+
68
+ elasticacheProps.cacheSubnetGroupName = subnetGroup.ref;
69
+
70
+ const namePrefix = elasticacheProps.clusterName
71
+ ? elasticacheProps.clusterName
72
+ : `${Stack.of(this).stackName}`;
73
+
74
+ const securityGroup = new SecurityGroup(
75
+ scope,
76
+ "ElasticacheSecurityGroup",
77
+ {
78
+ vpc,
79
+ allowAllOutbound: true,
80
+ description: "Security group for Elasticache Cluster",
81
+ securityGroupName: `${namePrefix}-elasticache`,
82
+ },
83
+ );
84
+
85
+ for (const subnetIndex of [1, 2, 3]) {
86
+ const cidr = StringParameter.valueForStringParameter(
87
+ scope,
88
+ `/vpc/subnet/private-${deployConfig.workloadGroup}/${subnetIndex}/cidr`,
89
+ );
90
+ securityGroup.addIngressRule(
91
+ Peer.ipv4(cidr),
92
+ Port.tcp(6379),
93
+ `Allow access to Elasticache cluster from private ${deployConfig.workloadGroup} subnet`,
94
+ );
95
+ }
96
+
97
+ elasticacheProps.vpcSecurityGroupIds = [securityGroup.securityGroupId];
98
+ }
99
+
100
+ // Create Redis Cluster
101
+ this.cluster = new CfnCacheCluster(scope, "RedisCluster", elasticacheProps);
102
+
103
+ if (elasticacheProps.engine === "redis") {
104
+ this.connectionString = `redis://${this.cluster.attrRedisEndpointAddress}:${this.cluster.attrRedisEndpointPort}`;
105
+ } else if (elasticacheProps.engine === "memcached") {
106
+ this.connectionString = `${this.cluster.attrConfigurationEndpointAddress}:${this.cluster.attrConfigurationEndpointPort}`;
107
+ } else {
108
+ throw Error(`Unsupported engine: ${elasticacheProps.engine}`);
109
+ }
110
+ }
111
+ }
@@ -23,7 +23,7 @@ export class IxVpcDetails extends Construct {
23
23
 
24
24
  private getVpcSubnet(scope: ConstructScope): SubnetSelection {
25
25
  const vpcSubnetIds = [1, 2, 3].map((subnetNum) =>
26
- StringParameter.valueFromLookup(
26
+ StringParameter.valueForStringParameter(
27
27
  scope,
28
28
  `/vpc/subnet/private-${ixDeployConfig.workloadGroup}/${subnetNum}/id`,
29
29
  ),
@@ -2,3 +2,4 @@ export * from "./IxVpcDetails.js";
2
2
  export * from "./IxCertificate.js";
3
3
  export * from "./IxDnsRecord.js";
4
4
  export * from "./IxNextjsSite.js";
5
+ export * from "./IxElasticache.js";