1
1
const yargs = require ( "yargs/yargs" ) ;
2
2
import inquirer from "inquirer" ;
3
+ import { domain } from "process" ;
3
4
import { exit } from "yargs" ;
4
5
const { hideBin } = require ( "yargs/helpers" ) ;
5
-
6
6
const execa = require ( "execa" ) ;
7
7
8
+ // reading and writing in out/ folder
9
+ const fs = require ( "fs" ) ;
10
+
8
11
require ( "dotenv" ) . config ( ) ;
9
12
10
13
const runHelperScript = async ( filename , params ) => {
@@ -23,7 +26,9 @@ const runHelperScript = async (filename, params) => {
23
26
} ;
24
27
25
28
const generateGoogleClusterCommand = ( argv ) => {
26
- return `$ gcloud beta container --project "${ argv . gcloudProjectId } " \\
29
+ // TODO: omit zone if it is intentionally left blank to support regional clusters
30
+ // note: this will involve modifying other gcloud commands that mention --zone
31
+ return `gcloud beta container --project "${ argv . gcloudProjectId } " \\
27
32
clusters create "${ argv . gcloudClusterName } " \\
28
33
--zone "${ argv . gcloudClusterZone } " \\
29
34
--no-enable-basic-auth \\
@@ -38,20 +43,20 @@ const generateGoogleClusterCommand = (argv) => {
38
43
--num-nodes "${ argv . gcloudClusterMinNodes } " \\
39
44
--enable-stackdriver-kubernetes \\
40
45
--enable-ip-alias \\
41
- --network "projects/$PROJECT_ID /global/networks/default" \\
42
- --subnetwork "projects/$PROJECT_ID /regions/${
43
- argv . gcloudClusterZone
44
- } /subnetworks/default" \\
46
+ --network "projects/${ argv . gcloudProjectId } /global/networks/default" \\
47
+ --subnetwork "projects/${ argv . gcloudProjectId } /regions/${
48
+ argv . gcloudClusterRegion
49
+ } /subnetworks/default" \\
45
50
--default-max-pods-per-node "110" \\
46
51
--addons HorizontalPodAutoscaling,HttpLoadBalancing \\
47
52
--enable-autoupgrade \\
48
53
--enable-autorepair \\${
49
- argv . gcloudClusterPreemtible ? "\n --preemtible \\" : ""
54
+ argv . gcloudClusterPreemptible ? "\n --preemptible \\" : ""
50
55
}
51
56
--enable-network-policy \\
52
57
--enable-autoscaling \\
53
58
--min-nodes "${ argv . gcloudClusterMinNodes } " \\
54
- --max-nodes "${ argv . gcloudClusterMaxNodes } "` ;
59
+ --max-nodes "${ argv . gcloudClusterMaxNodes } "\n ` ;
55
60
} ;
56
61
57
62
export async function cli ( args ) {
@@ -61,6 +66,12 @@ export async function cli(args) {
61
66
type : "string" ,
62
67
description : "Method for deploying Coder (gcloud, general-k8s)" ,
63
68
} )
69
+ . option ( "save-dir" , {
70
+ alias : "f" ,
71
+ type : "string" ,
72
+ default : "~/.config/launch-coder" ,
73
+ description : "Path to save config files" ,
74
+ } )
64
75
. option ( "domainType" , {
65
76
alias : "d" ,
66
77
type : "string" ,
@@ -86,6 +97,10 @@ export async function cli(args) {
86
97
type : "string" ,
87
98
default : "coder" ,
88
99
} )
100
+ . option ( "gcloud-cluster-region" , {
101
+ type : "string" ,
102
+ default : "us-central1" ,
103
+ } )
89
104
. option ( "gcloud-cluster-zone" , {
90
105
type : "string" ,
91
106
default : "us-central1-a" ,
@@ -94,7 +109,7 @@ export async function cli(args) {
94
109
type : "string" ,
95
110
default : "e2-highmem-4" ,
96
111
} )
97
- . option ( "gcloud-cluster-preemtible " , {
112
+ . option ( "gcloud-cluster-preemptible " , {
98
113
type : "boolean" ,
99
114
default : true ,
100
115
} )
@@ -110,8 +125,7 @@ export async function cli(args) {
110
125
type : "number" ,
111
126
default : 3 ,
112
127
} )
113
- // TODO: determine better naming for this:
114
- . option ( "gcloud-skip-confirm-prompt" , {
128
+ . option ( "skip-confirm-prompts" , {
115
129
type : "boolean" ,
116
130
} ) . argv ;
117
131
@@ -147,7 +161,7 @@ export async function cli(args) {
147
161
value : "gcloud" ,
148
162
} ,
149
163
{
150
- name : "Install Coder on my current cluster (sketchy) " ,
164
+ name : "Install Coder on my current cluster" ,
151
165
value : "k8s" ,
152
166
} ,
153
167
] ,
@@ -245,12 +259,13 @@ export async function cli(args) {
245
259
let pricing_info = "" ;
246
260
247
261
if (
262
+ argv . gcloudClusterRegion == "us-central1" &&
248
263
argv . gcloudClusterZone == "us-central1-a" &&
249
264
argv . gcloudClusterMachineType == "e2-highmem-4" &&
250
265
argv . gcloudClusterMinNodes == "1" &&
251
266
argv . gcloudClusterMaxNodes == "3" &&
252
267
argv . gcloudClusterAutoscaling &&
253
- argv . gcloudClusterPreemtible
268
+ argv . gcloudClusterPreemptible
254
269
) {
255
270
pricing_info =
256
271
"This cluster will cost you roughly $40-120/mo to run on Google Cloud depending on usage." +
@@ -275,7 +290,7 @@ export async function cli(args) {
275
290
276
291
// TODO: impliment ability to edit cluster command in the cli (wohoo)
277
292
278
- if ( ! argv . gcloudSkipConfirmPrompt ) {
293
+ if ( ! argv . skipConfirmPrompts ) {
279
294
const runCommand = await inquirer . prompt ( {
280
295
type : "confirm" ,
281
296
default : true ,
@@ -291,17 +306,94 @@ export async function cli(args) {
291
306
}
292
307
}
293
308
294
- const subprocess = execa ( "ping" , [ "google.com" , "-c" , "5" ] ) ;
295
- subprocess . stdout . pipe ( process . stdout ) ;
296
- const { stdout } = await subprocess ;
297
- console . log ( "WE KNOW THE PROCESS HAS COMPLETED" ) ;
309
+ // TODO: create different folders for each session
310
+ console . log (
311
+ "💾 FYI: All of these scripts are being saved in: " + argv . saveDir + "\n"
312
+ ) ;
298
313
299
- // execa("echo", ["unicorns"]).stdout.pipe(process.stdout);
300
- } else if ( argv . method == "k8s" ) {
301
- console . log ( "coming sooon moo" ) ;
302
- } else {
314
+ // switch to the absolute path of the home directory if the user included ~/
315
+ if ( argv . saveDir . startsWith ( "~/" ) ) {
316
+ const userHome = require ( "os" ) . homedir ( ) ;
317
+ argv . saveDir = argv . saveDir . replace ( "~/" , userHome + "/" ) ;
318
+ }
319
+
320
+ // create our out/ file to hold our creation script, among other things
321
+ await execa ( "mkdir" , [ "-p" , argv . saveDir ] ) . catch ( ( err ) => {
322
+ console . log ( err ) ;
323
+ } ) ;
324
+
325
+ // git init (or re-init so the user can easily source-control)
326
+ await execa ( "git" , [ "init" , argv . saveDir ] ) ;
327
+
328
+ // add our lovely script to the out folder
329
+ fs . writeFileSync (
330
+ argv . saveDir + "/createCluster.sh" ,
331
+ "#!/bin/sh\n" + gCloudCommand
332
+ ) ;
333
+ await fs . chmodSync ( argv . saveDir + "/createCluster.sh" , "755" ) ;
334
+
335
+ console . log ( "\n⏳ Creating your cluster. This will take a few minutes..." ) ;
336
+
337
+ try {
338
+ const subprocess = execa ( "/bin/sh" , [ argv . saveDir + "/createCluster.sh" ] ) ;
339
+ subprocess . stdout . pipe ( process . stdout ) ;
340
+ const { stdout } = await subprocess ;
341
+ console . log ( "------------" ) ;
342
+ console . log (
343
+ "✅" ,
344
+ `Cluster "${ argv . gcloudClusterName } " has been created!`
345
+ ) ;
346
+ } catch ( err ) {
347
+ console . log ( "❌" , "Process failed\n\n\n" , err . stderr ) ;
348
+ return ;
349
+ }
350
+
351
+ try {
352
+ await execa (
353
+ "gcloud" ,
354
+ `container clusters get-credentials ${ argv . gcloudClusterName } --zone ${ argv . gcloudClusterZone } ` . split (
355
+ " "
356
+ )
357
+ ) ;
358
+ console . log ( "✅" , "Added to kube context" ) ;
359
+ } catch ( err ) {
360
+ console . log ( "❌" , "Unable to add to kube context:\n\n\n" , err . stderr ) ;
361
+ return ;
362
+ }
363
+
364
+ // So now we can move on to installing Coder!
365
+ }
366
+
367
+ // if argv.method == "gcloud" at this point
368
+ // the script has succeeded in creating the cluster
369
+ // and switched context
370
+ if ( argv . method != "k8s" && argv . method != "gcloud" ) {
371
+ // TODO: standardize these
303
372
console . error ( "Error. Unknown method: " + argv . method ) ;
304
373
return ;
374
+ } else if ( argv . method == "k8s" ) {
375
+ // TODO: add checks to ensure the user has a cluster,
376
+ // and it has the necessary stuff for Coder
377
+ console . log (
378
+ "This script does not currently verify that you cluster is ready for Coder yet.\n\nWe recommend checking the docs before continuing:"
379
+ ) ;
380
+ console . log ( "\t➡️ https://coder.com/docs/setup/requirements" ) ;
381
+
382
+ if ( ! argv . skipConfirmPrompts ) {
383
+ const runCommand = await inquirer . prompt ( {
384
+ type : "confirm" ,
385
+ default : true ,
386
+ name : "runIt" ,
387
+ message : "Do you to proceed?" ,
388
+ } ) ;
389
+
390
+ if ( ! runCommand . runIt ) {
391
+ console . log (
392
+ `\nExited. If you have any questions, feel free reach out on Slack:\n\t➡️ https://cdr.co/join-community\n`
393
+ ) ;
394
+ return 0 ;
395
+ }
396
+ }
305
397
}
306
398
307
399
// determine which type of domain to use
@@ -314,16 +406,46 @@ export async function cli(args) {
314
406
message : "What type of domain would you like to use?" ,
315
407
choices : [
316
408
{
317
- name : `With a free domain from Coder (ex. [myname].${ process . env . CLOUDFLARE_DOMAIN } )` ,
409
+ name : `A free domain from Coder (ex. [myname].${ process . env . CLOUDFLARE_DOMAIN } )` ,
318
410
value : "auto" ,
319
411
} ,
320
412
{
321
- name : "With a domain name I own on Google CloudDNS" ,
413
+ name : "A domain name I own on Google CloudDNS" ,
322
414
value : "cloud-dns" ,
323
415
} ,
416
+ {
417
+ name : "Do not set up a domain for now" ,
418
+ value : "none" ,
419
+ } ,
324
420
] ,
325
421
} ) ) ,
326
422
} ;
327
423
}
424
+
425
+ // validate domainType
426
+ if ( argv . domainType == "auto" ) {
427
+ console . log ( "AUTOMA" ) ;
428
+ } else if ( argv . domainType == "cloud-dns" ) {
429
+ console . log ( "Well, this is coming soon 💀" ) ;
430
+ } else if ( argv . domainType == "none" ) {
431
+ console . log (
432
+ "\nWarning: This means you can't use Coder with DevURLs, a primary way of accessing web services\ninside of a Coder Workspace:\n" ,
433
+ "\t📄 Docs: https://coder.com/docs/environments/devurls\n" ,
434
+ "\t🌎 Alternative: https://ngrok.com/docs (you can nstall this in your images)\n\n"
435
+ ) ;
436
+
437
+ console . log (
438
+ "You can always add a domain later, and use a custom provider via our docs."
439
+ ) ;
440
+
441
+ // TODO: add confirmations
442
+ } else {
443
+ // TODO: standardize these
444
+ console . error ( "Error. Unknown domainType: " + argv . domainType ) ;
445
+ return ;
446
+ }
447
+
448
+ // install and access Coder
449
+
328
450
console . log ( "\n\nat the end with a long argv:" , Object . keys ( argv ) . length ) ;
329
451
}
0 commit comments