7
7
TerminateInstancesCommand ,
8
8
DescribeInstancesCommand
9
9
} from "@aws-sdk/client-ec2"
10
+ import { GetCommandInvocationCommand , SSMClient , SendCommandCommand , SendCommandCommandInput } from "@aws-sdk/client-ssm"
10
11
import { P0tionEC2Instance } from "../types"
11
12
import dotenv from "dotenv"
12
13
dotenv . config ( )
@@ -52,6 +53,24 @@ export const createEC2Client = async (): Promise<EC2Client> => {
52
53
return ec2
53
54
}
54
55
56
+ /**
57
+ * Create an SSM client object
58
+ * @returns <Promise<SSMClient>> an SSM client
59
+ */
60
+ export const createSSMClient = async ( ) : Promise < SSMClient > => {
61
+ const { accessKeyId, secretAccessKey, region } = getAWSVariables ( )
62
+
63
+ const ssm : SSMClient = new SSMClient ( {
64
+ credentials : {
65
+ accessKeyId : accessKeyId ,
66
+ secretAccessKey : secretAccessKey
67
+ } ,
68
+ region : region
69
+ } )
70
+
71
+ return ssm
72
+ }
73
+
55
74
/**
56
75
* Generate the command to be run by the EC2 instance
57
76
* @param r1csPath <string> path to r1cs file
@@ -63,23 +82,24 @@ export const generateVMCommand = (
63
82
r1csPath : string ,
64
83
zKeyPath : string ,
65
84
ptauPath : string ,
66
- verificationTranscriptPath : string
67
85
) : string [ ] => {
68
86
const command = [
69
87
"#!/usr/bin/env bash" ,
70
88
"sudo apt update" ,
71
- "sudo apt install awscli -y" , // install cli
89
+ "sudo apt install awscli -y" , // install aws cli
72
90
"curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash" , // install nvm
91
+ "source ~/.bashrc" ,
73
92
"nvm install 16" ,
74
93
"nvm use 16" ,
94
+ "npm install -g yarn" ,
75
95
"npm install -g snarkjs" ,
96
+ `aws s3 cp s3://${ r1csPath } /var/tmp/circuit.r1cs` ,
97
+ `aws s3 cp s3://${ zKeyPath } /var/tmp/genesisZkey.zkey` ,
98
+ `aws s3 cp s3://${ ptauPath } /var/tmp/ptau.ptau` ,
99
+ "npm install -g p0tion-api" ,
100
+ "p0tion-api /var/tmp/circuit.r1cs /var/tmp/genesisZkey.zkey /var/tmp/ptau.ptau" ,
76
101
]
77
- command . push ( `aws s3 cp s3://${ r1csPath } /tmp/circuit.r1cs` )
78
- command . push ( `aws s3 cp s3://${ zKeyPath } /tmp/zkey.zkey` )
79
- command . push ( `aws s3 cp s3://${ ptauPath } /tmp/ptau.ptau` )
80
- command . push ( "snarkjs zkey verify circuit.r1cs ptau.ptau zkey.zkey > /tmp/verify.txt" )
81
- command . push ( `aws s3 cp /tmp/verify.txt s3://${ verificationTranscriptPath } ` )
82
-
102
+
83
103
return command
84
104
}
85
105
@@ -169,27 +189,26 @@ const instancesTypes = {
169
189
/**
170
190
* Creates a new EC2 instance
171
191
* @param ec2 <EC2Client> the EC2 client to talk to AWS
192
+ * @param commands <string[]> the commands to be run on the EC2 instance
193
+ * @param instanceType <string> the type of instance to be created
194
+ * @param amiId <string> the AMI ID to be used
195
+ * @param keyName <string> the name of the key to be used
196
+ * @param roleArn <string> the ARN of the role to be used
172
197
* @returns <Promise<P0tionEC2Instance>> the instance that was created
173
198
*/
174
- export const createEC2Instance = async ( ec2 : EC2Client ) : Promise < P0tionEC2Instance > => {
175
- const { amiId, keyName, roleArn } = getAWSVariables ( )
176
-
177
- // @note Test only
178
- const commands = [
179
- "#!/usr/bin/env bash" ,
180
- "sudo apt update" ,
181
- "sudo apt install awscli -y" ,
182
- "touch /tmp/test.txt" ,
183
- "echo 'hello world' > /tmp/test.txt" ,
184
- "aws s3 cp /tmp/test.txt s3://p0tion-test-bucket/test.txt" ,
185
- "npm install -g p0tion-api" ,
186
- "p0tion-api " ,
187
- ]
199
+ export const createEC2Instance = async (
200
+ ec2 : EC2Client ,
201
+ commands : string [ ] ,
202
+ instanceType : string ,
203
+ amiId : string ,
204
+ keyName : string ,
205
+ roleArn : string
206
+ ) : Promise < P0tionEC2Instance > => {
188
207
189
208
// create the params
190
209
const params = {
191
210
ImageId : amiId ,
192
- InstanceType : "t2.micro" , // to be determined programmatically
211
+ InstanceType : instanceType , // to be determined programmatically
193
212
MaxCount : 1 ,
194
213
MinCount : 1 ,
195
214
KeyName : keyName ,
@@ -202,22 +221,27 @@ export const createEC2Instance = async (ec2: EC2Client): Promise<P0tionEC2Instan
202
221
}
203
222
204
223
// create command
205
- const command = new RunInstancesCommand ( params )
206
- const response = await ec2 . send ( command )
207
-
208
- if ( response . $metadata . httpStatusCode !== 200 ) {
209
- throw new Error ( "Could not create a new EC2 instance" )
210
- }
211
-
212
- const instance : P0tionEC2Instance = {
213
- InstanceId : response . Instances ! [ 0 ] . InstanceId ! ,
214
- ImageId : response . Instances ! [ 0 ] . ImageId ! ,
215
- InstanceType : response . Instances ! [ 0 ] . InstanceType ! ,
216
- KeyName : response . Instances ! [ 0 ] . KeyName ! ,
217
- LaunchTime : response . Instances ! [ 0 ] . LaunchTime ! . toISOString ( )
224
+ try {
225
+ const command = new RunInstancesCommand ( params )
226
+ const response = await ec2 . send ( command )
227
+
228
+ if ( response . $metadata . httpStatusCode !== 200 ) {
229
+ throw new Error ( "Could not create a new EC2 instance" )
230
+ }
231
+
232
+ const instance : P0tionEC2Instance = {
233
+ InstanceId : response . Instances ! [ 0 ] . InstanceId ! ,
234
+ ImageId : response . Instances ! [ 0 ] . ImageId ! ,
235
+ InstanceType : response . Instances ! [ 0 ] . InstanceType ! ,
236
+ KeyName : response . Instances ! [ 0 ] . KeyName ! ,
237
+ LaunchTime : response . Instances ! [ 0 ] . LaunchTime ! . toISOString ( )
238
+ }
239
+
240
+ return instance
241
+ } catch ( error : any ) {
242
+ console . log ( "[*] Debug" , error )
243
+ throw new Error ( "Could not deploy a new EC2 instance" )
218
244
}
219
-
220
- return instance
221
245
}
222
246
223
247
/**
@@ -232,10 +256,9 @@ export const checkEC2Status = async (ec2Client: EC2Client, instanceId: string):
232
256
} )
233
257
234
258
const response = await ec2Client . send ( command )
235
- if ( response . $metadata . httpStatusCode !== 200 ) {
259
+ if ( response . $metadata . httpStatusCode !== 200 )
236
260
throw new Error ( "Could not get the status of the EC2 instance" )
237
- }
238
-
261
+
239
262
return response . InstanceStatuses ! [ 0 ] . InstanceState ! . Name === "running"
240
263
}
241
264
@@ -281,7 +304,6 @@ export const startEC2Instance = async (ec2: EC2Client, instanceId: string) => {
281
304
* Stops an EC2 instance
282
305
* @param ec2 <EC2Client> the EC2 client to talk to AWS
283
306
* @param instanceId <string> the id of the instance to stop
284
- * @returns
285
307
*/
286
308
export const stopEC2Instance = async ( ec2 : EC2Client , instanceId : string ) => {
287
309
const command = new StopInstancesCommand ( {
@@ -313,3 +335,61 @@ export const terminateEC2Instance = async (ec2: EC2Client, instanceId: string) =
313
335
throw new Error ( "Could not terminate the EC2 instance" )
314
336
}
315
337
}
338
+
339
+ /**
340
+ * Run a command on a VM using SSM
341
+ * @param ssmClient <SSMClient> the SSM client to talk to AWS
342
+ * @param instanceId <string> the id of the instance to run the command on
343
+ * @param commands <string[]> the commands to run
344
+ * @return <Promise<any>> the command id
345
+ */
346
+ export const runCommandOnEC2 = async (
347
+ ssmClient : SSMClient ,
348
+ instanceId : string ,
349
+ commands : string [ ]
350
+ ) : Promise < any > => {
351
+ // the params for the command
352
+ const params : SendCommandCommandInput = {
353
+ DocumentName : "AWS-RunShellScript" ,
354
+ InstanceIds : [ instanceId ] ,
355
+ Parameters : {
356
+ "commands" : commands
357
+ } ,
358
+ TimeoutSeconds : 1200
359
+ }
360
+
361
+ try {
362
+ const response = await ssmClient . send ( new SendCommandCommand ( params ) )
363
+ if ( response . $metadata . httpStatusCode !== 200 ) {
364
+ throw new Error ( "Could not run the command on the EC2 instance" )
365
+ }
366
+ return response . Command ! . CommandId
367
+ } catch ( error : any ) {
368
+ throw new Error ( "Could not run the command on the EC2 instance" )
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Retrieve the output of a SSM command
374
+ * @param ssmClient <SSMClient> the SSM client to talk to AWS
375
+ * @param commandId <string> The id of the command to retrieve the output of
376
+ * @param instanceId <string> The id of the instance to retrieve the output of
377
+ * @return <Promise<any>> The output of the command
378
+ */
379
+ export const retrieveCommandOutput = async (
380
+ ssmClient : SSMClient ,
381
+ commandId : string ,
382
+ instanceId : string
383
+ ) : Promise < any > => {
384
+ const command = new GetCommandInvocationCommand ( {
385
+ CommandId : commandId ,
386
+ InstanceId : instanceId
387
+ } )
388
+
389
+ try {
390
+ const output = await ssmClient . send ( command )
391
+ return output . StandardOutputContent
392
+ } catch ( error : any ) {
393
+ throw new Error ( "Could not retrieve the output of the command" )
394
+ }
395
+ }
0 commit comments