@leverege/build-tools 2.2.1 → 2.2.3

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/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [2.2.3] = 2020-04-22
5
+ ### Updated
6
+ - (Helmify) Added a CLI interface for Helmup
7
+
4
8
  ## [2.1.5] - 2020-03-08
5
9
  ### Updated
6
10
  - (Helmup) Simplified Timescale installation process
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ const fs = require('fs');
4
+
5
+ const path = require('path');
6
+
7
+ const inquirer = require('inquirer');
8
+
9
+ module.exports = {
10
+ getCurrentDirectoryBase: () => {
11
+ return path.basename(process.cwd());
12
+ },
13
+ directoryExists: filePath => {
14
+ return fs.existsSync(filePath);
15
+ },
16
+ overwriteDirectory: dirName => {
17
+ const question = [{
18
+ name: 'overwriteDirectory',
19
+ type: 'confirm',
20
+ message: `Overwrite existing ${dirName} directory?`
21
+ }];
22
+ return inquirer.prompt(question);
23
+ }
24
+ };
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+
3
+ const fs = require('fs');
4
+
5
+ const fsp = fs.promises;
6
+
7
+ const path = require('path');
8
+
9
+ const inquirer = require('inquirer');
10
+
11
+ const chalk = require('chalk');
12
+
13
+ const execa = require('execa');
14
+
15
+ const yaml = require('js-yaml');
16
+
17
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
18
+
19
+ const packageJson = require(`${packageJsonPath}`);
20
+
21
+ function sanitizePackageName(name) {
22
+ let validName = name; // Remove npm lib name (e.g. @leverege)
23
+
24
+ const isNpm = name.startsWith('@');
25
+
26
+ if (isNpm) {
27
+ const splitStr = name.split('/');
28
+ validName = splitStr[1]; // returns name after npm domain
29
+ }
30
+
31
+ return validName;
32
+ }
33
+
34
+ function transformGoFormat(input) {
35
+ return input.replace(/-/g, '_');
36
+ }
37
+
38
+ async function createChartYAML(chart) {
39
+ try {
40
+ await fsp.writeFile('helm/Chart.yaml', yaml.safeDump(chart), 'utf8');
41
+ } catch (err) {
42
+ console.error('Failed to create Chart.yaml file', chalk.red.bold('ERROR'));
43
+ process.exit(1);
44
+ }
45
+ }
46
+
47
+ async function createValuesYAML(name, version, redisAccess, securityContext, preemptible, service) {
48
+ const openPort = service !== 'none';
49
+ const portFromEnv = process.env.PORT || process.env.SERVER_PORT;
50
+ const portNumber = parseInt(portFromEnv) || 8080;
51
+ const values = {
52
+ [name]: {
53
+ "VERSION": version,
54
+ "PREMPTIBLE": preemptible,
55
+ ...(openPort && {
56
+ port: portNumber
57
+ })
58
+ },
59
+ "redisClient": {
60
+ "label": redisAccess
61
+ },
62
+ "securityContext": {
63
+ "readOnlyRootFs": !securityContext
64
+ },
65
+ "cloud": {
66
+ "provider": "gcp",
67
+ "project": "",
68
+ "region": "",
69
+ "sqlInstance": ""
70
+ }
71
+ };
72
+
73
+ try {
74
+ await fsp.writeFile('helm/values.yaml', yaml.safeDump(values), 'utf8');
75
+ } catch (err) {
76
+ console.error('Failed to create values.yaml file', chalk.red.bold('ERROR'));
77
+ process.exit(1);
78
+ }
79
+ }
80
+
81
+ async function createServiceYAML(name, serviceType) {
82
+ try {
83
+ await execa('helminit-svc', [name, serviceType]);
84
+ } catch (err) {
85
+ console.error('Failed to create service.yaml file', chalk.red.bold('ERROR'));
86
+ process.exit(1);
87
+ }
88
+ }
89
+
90
+ async function createDeploymentYAML(name, enableFirebase, enableBigQuery) {
91
+ try {
92
+ await execa('helminit-dp', [name, enableFirebase, enableBigQuery]);
93
+ } catch (err) {
94
+ console.error('Failed to create deployment.yaml file', chalk.red.bold('ERROR'));
95
+ process.exit(1);
96
+ }
97
+ }
98
+
99
+ async function createScalerYAML(name) {
100
+ try {
101
+ await execa('helminit-scaler', [name]);
102
+ } catch (err) {
103
+ console.error('Failed to create scaler.yaml file', chalk.red.bold('ERROR'));
104
+ process.exit(1);
105
+ }
106
+ }
107
+
108
+ async function createTemplates() {
109
+ const helmQuestions = [{
110
+ name: 'redisAccess',
111
+ type: 'confirm',
112
+ message: 'Does this chart need access to Redis (e.g. cache, limit, lock options)?'
113
+ }, {
114
+ name: 'preemptible',
115
+ type: 'confirm',
116
+ message: 'Is this server stateless and can survive occasional unavailability?'
117
+ }, {
118
+ name: 'securityContext',
119
+ type: 'confirm',
120
+ message: 'Does this server need special write access to filesystem?'
121
+ }, {
122
+ name: 'service',
123
+ type: 'rawlist',
124
+ message: 'Does this server need a TCP port open for internal or external access?',
125
+ choices: ['none', 'internal', 'external'],
126
+ default: ['none']
127
+ }, {
128
+ name: 'scaler',
129
+ type: 'confirm',
130
+ message: 'Does this server need horizontal scaling?'
131
+ }, {
132
+ name: 'cloud',
133
+ type: 'rawlist',
134
+ message: 'By default, which cloud platform will this server be deployed on?',
135
+ choices: ['GCP', 'AWS'],
136
+ default: ['GCP']
137
+ }, {
138
+ name: 'cloudServices',
139
+ type: 'checkbox',
140
+ message: 'Which these cloud services does this server need? (space to choose, enter to submit)',
141
+ choices: ['firebase', 'bigquery']
142
+ }];
143
+ const helmOptions = await inquirer.prompt(helmQuestions);
144
+ return helmOptions;
145
+ }
146
+
147
+ module.exports = {
148
+ bootstrap: async () => {
149
+ let name = sanitizePackageName(packageJson.name);
150
+ const appVersion = packageJson.version;
151
+ const keepNameQuestion = [{
152
+ name: 'keepPackageName',
153
+ type: 'confirm',
154
+ message: `Helm chart name will default to ${name}. Do you want to keep the default setting?`
155
+ }];
156
+ const keepName = await inquirer.prompt(keepNameQuestion);
157
+
158
+ if (!keepName.keepPackageName) {
159
+ const keepNameQuestion = [{
160
+ name: 'renamePackage',
161
+ type: 'input',
162
+ message: 'Please enter a new name for the Helm chart',
163
+ validate: str => {
164
+ const helmRegex = RegExp('^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$');
165
+
166
+ if (helmRegex.test(str)) {
167
+ return true;
168
+ } else {
169
+ return 'Special characters are not allowed. Please enter a valid name.';
170
+ }
171
+ }
172
+ }];
173
+ const newName = await inquirer.prompt(keepNameQuestion);
174
+ name = newName.renamePackage;
175
+ }
176
+
177
+ console.log(chalk.green(`\nBootstrapping a new Helm chart for ${name}...\n`));
178
+ const chart = {
179
+ name,
180
+ version: "1.0.0",
181
+ appVersion
182
+ };
183
+ const chartSafeName = transformGoFormat(name);
184
+
185
+ try {
186
+ await execa('mkdir', ['helm']);
187
+ await execa('mkdir', ['helm/templates']);
188
+ } catch (err) {
189
+ console.error('Failed to create directories', chalk.red.bold('ERROR'));
190
+ process.exit(1);
191
+ }
192
+
193
+ const templateOpts = await createTemplates();
194
+ const {
195
+ service,
196
+ scaler,
197
+ cloudServices,
198
+ redisAccess,
199
+ securityContext,
200
+ preemptible
201
+ } = templateOpts;
202
+
203
+ if (service !== 'none') {
204
+ const serviceType = service === 'internal' ? 'ClusterIP' : 'NodePort';
205
+ await createServiceYAML(chartSafeName, serviceType);
206
+ }
207
+
208
+ if (scaler) {
209
+ await createScalerYAML(chartSafeName);
210
+ }
211
+
212
+ const enableFirebase = cloudServices.includes('firebase');
213
+ const enableBigQuery = cloudServices.includes('bigquery');
214
+ await createDeploymentYAML(name, enableFirebase, enableBigQuery);
215
+ await createValuesYAML(chartSafeName, appVersion, redisAccess, securityContext, preemptible, service);
216
+ console.log(chalk.green.bold(`\n\n Helm chart for ${name} is now ready \n`));
217
+ console.log(chalk.yellow.bold('Add any additional env vars to value.yaml and deployment.yaml prior to deployment'));
218
+ }
219
+ };
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ require('dotenv').config(); // attempt to autodetect envs
5
+
6
+
7
+ const chalk = require('chalk');
8
+
9
+ const clear = require('clear');
10
+
11
+ const figlet = require('figlet');
12
+
13
+ const execa = require('execa');
14
+
15
+ const files = require('./files');
16
+
17
+ const helm = require('./helm');
18
+
19
+ clear();
20
+ console.log(chalk.yellow(figlet.textSync('Helmify', {
21
+ horizontalLayout: 'full'
22
+ })));
23
+
24
+ const run = async () => {
25
+ if (files.directoryExists('helm')) {
26
+ console.log(chalk.red.bold('\nDetected existing Helm configs\n'));
27
+ const overwrite = await files.overwriteDirectory('Helm');
28
+
29
+ if (!overwrite.overwriteDirectory) {
30
+ console.log(chalk.green('\nNo changes made - exiting... \n'));
31
+ process.exit();
32
+ }
33
+
34
+ console.log(chalk.yellow.bold('\nDeleting existing Helm directory...\n'));
35
+ const deleteHelmDir = await execa('rm', ['-rf', 'helm']);
36
+
37
+ if (deleteHelmDir.failed) {
38
+ console.log(chalk.red.bold('\nFailed to delete existing Helm directory\n'));
39
+ process.exit(1);
40
+ }
41
+ }
42
+
43
+ helm.bootstrap();
44
+ };
45
+
46
+ run();
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@leverege/build-tools",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "build": "npm run compile",
8
8
  "compile": "rm -fr lib && npm run compile_server && npm run compile_web",
9
- "compile_server": "NODE_ENV=server babel -d lib/server src/ && cp src/dnsify/accounts.json.enc lib/server/dnsify/accounts.json.enc",
9
+ "compile_server": "NODE_ENV=server babel -d lib/server src/",
10
10
  "compile_web": "NODE_ENV=web babel -d lib/web src/",
11
11
  "licenseCheck": "license-checker --summary --failOn GPL",
12
12
  "prepublintOnly": "npm run lint && npm run build",
@@ -17,14 +17,16 @@
17
17
  "circleate": "src/circleate",
18
18
  "decrypt-secrets": "src/decrypt-secrets",
19
19
  "dirty-git": "src/dirty-git",
20
- "dnsify": "lib/server/dnsify/index.js",
21
20
  "dockreate": "src/dockreate",
22
21
  "encrypt-secrets": "src/encrypt-secrets",
23
22
  "getfbcfg": "src/getfbcfg",
24
23
  "getjson": "src/getjson",
25
24
  "git-tar": "src/git-tar",
26
25
  "helmdn": "src/helmdn",
27
- "helmify": "src/helmify",
26
+ "helmify": "lib/server/helmify/index.js",
27
+ "helminit-svc": "src/helminit-svc",
28
+ "helminit-dp": "src/helminit-dp",
29
+ "helminit-scaler": "src/helminit-scaler",
28
30
  "helmup": "src/helmup",
29
31
  "k8x": "src/k8x",
30
32
  "overwhelm": "src/overwhelm",
@@ -40,14 +42,17 @@
40
42
  "@google-cloud/kms": "^1.6.3",
41
43
  "async": "^3.2.0",
42
44
  "chalk": "^3.0.0",
43
- "cloudflare": "^2.7.0",
44
- "command-line-args": "^5.1.1",
45
- "command-line-usage": "^5.0.5",
45
+ "clear": "^0.1.0",
46
46
  "commander": "^2.20.3",
47
+ "dotenv": "^8.2.0",
48
+ "execa": "^4.0.0",
49
+ "figlet": "^1.3.0",
47
50
  "fs-extra": "^8.1.0",
48
51
  "git-branch": "^2.0.1",
49
52
  "glob": "^7.1.6",
53
+ "inquirer": "^7.1.0",
50
54
  "js-yaml": "^3.13.1",
55
+ "minimist": "^1.2.5",
51
56
  "ora": "^3.4.0",
52
57
  "parse-gitignore": "^1.0.1",
53
58
  "readline-sync": "^1.4.10",
package/src/circleate CHANGED
@@ -93,7 +93,7 @@ jobs:
93
93
  smoke-test:
94
94
  # smoke test checks for license, security, and linting
95
95
  docker:
96
- - image: circleci/node:12.10.0
96
+ - image: circleci/node:erbium
97
97
  steps:
98
98
  - checkout
99
99
  - restore_cache:
@@ -110,7 +110,7 @@ jobs:
110
110
 
111
111
  - run:
112
112
  name: Check for vulnerabilities
113
- command: npm audit --production
113
+ command: npm audit --production --audit-level=moderate
114
114
 
115
115
  - run:
116
116
  name: Check for LICENSE and README files
@@ -134,7 +134,7 @@ jobs:
134
134
  environment:
135
135
  TZ: 'America/New_York'
136
136
  docker:
137
- - image: circleci/node:12.10.0
137
+ - image: circleci/node:erbium
138
138
  # IF YOU NEED ANY OF THESE, UNCOMMENT TO USE
139
139
  # - image: redis
140
140
  # - image: circleci/mysql:5.7
@@ -195,7 +195,7 @@ jobs:
195
195
  build:
196
196
  # build should make dist image and prepare docker file
197
197
  docker:
198
- - image: circleci/node:12.10.0
198
+ - image: circleci/node:erbium
199
199
 
200
200
  steps:
201
201
  - checkout
@@ -281,7 +281,7 @@ jobs:
281
281
  smoke-test:
282
282
  # smoke test checks for license, security, and linting
283
283
  docker:
284
- - image: circleci/node:12.10.0
284
+ - image: circleci/node:erbium
285
285
  steps:
286
286
  - checkout
287
287
  - restore_cache:
@@ -298,7 +298,7 @@ jobs:
298
298
 
299
299
  - run:
300
300
  name: Check for vulnerabilities
301
- command: npm audit --production
301
+ command: npm audit --production --audit-level=moderate
302
302
 
303
303
  - run:
304
304
  name: Check for LICENSE and README files
@@ -320,7 +320,7 @@ jobs:
320
320
  coverage:
321
321
  # coverage runs tests, generates coverage, and saves docs
322
322
  docker:
323
- - image: circleci/node:12.10.0
323
+ - image: circleci/node:erbium
324
324
  # IF YOU NEED ANY OF THESE, UNCOMMENT TO USE
325
325
  # - image: redis
326
326
  # - image: circleci/mysql:5.7
@@ -473,7 +473,7 @@ jobs:
473
473
 
474
474
  build:
475
475
  docker:
476
- - image: circleci/node:12.10.0
476
+ - image: circleci/node:erbium
477
477
  steps:
478
478
  - checkout
479
479
  - restore_cache:
@@ -0,0 +1,174 @@
1
+ #!/bin/bash
2
+
3
+ dockerName=$1
4
+ chartName=${dockerName//-/_}
5
+
6
+ enableFirebase=$2
7
+ enableBigQuery=$3
8
+
9
+ if [ "$enableFirebase" = true ] ; then
10
+ firebaseVolume="
11
+ - name: rt-firebase-key
12
+ secret:
13
+ secretName: rt-data
14
+ - name: model-firebase-key
15
+ secret:
16
+ secretName: model-data
17
+ - name: model-firebase-config
18
+ secret:
19
+ secretName: model-data-config
20
+ - name: rt-firebase-config
21
+ secret:
22
+ secretName: rt-data-config"
23
+
24
+ firebaseVolumeMount='
25
+ - name: model-firebase-key
26
+ mountPath: "/home/node/.gcp/model-data"
27
+ - name: rt-firebase-key
28
+ mountPath: "/home/node/.gcp/rt-data"
29
+ - name: model-firebase-config
30
+ mountPath: "/home/node/.gcp/model-data-config"
31
+ - name: rt-firebase-config
32
+ mountPath: "/home/node/.gcp/rt-data-config"'
33
+ fi
34
+
35
+ if [ "$enableBigQuery" = true ] ; then
36
+ bigQueryVolume="
37
+ - name: big-query
38
+ secret:
39
+ secretName: big-query"
40
+
41
+ bigQueryVolumeMount='
42
+ - name: big-query
43
+ mountPath: "/home/node/.gcp/bigquery"'
44
+ fi
45
+
46
+ touch helm/templates/deployment.yaml
47
+
48
+ cat<<EODEPLOYMENT > helm/templates/deployment.yaml
49
+ {{- if .Values.cloud.provider -}}
50
+ {{- \$provider := .Values.cloud.provider -}}
51
+ apiVersion: apps/v1
52
+ kind: Deployment
53
+ metadata:
54
+ name: {{ .Chart.Name }}
55
+ labels:
56
+ app: {{ .Chart.Name }}
57
+ chart/version: {{ .Chart.Version }}
58
+ chart/appVersion: {{ .Chart.AppVersion }}
59
+ redis-client: {{ .Values.redisClient.label | quote }}
60
+ spec:
61
+ selector:
62
+ matchLabels:
63
+ app: {{ .Chart.Name }}
64
+ template:
65
+ metadata:
66
+ labels:
67
+ app: {{ .Chart.Name }}
68
+ chart/version: {{ .Chart.Version }}
69
+ chart/appVersion: {{ .Chart.AppVersion }}
70
+ redis-client: {{ .Values.redisClient.label | quote }}
71
+ annotations:
72
+ prometheus.io/port: '5111'
73
+ prometheus.io/scrape: 'true'
74
+ configmap.reloader.stakater.com/reload: "default-configs"
75
+ spec:
76
+ volumes: $firebaseVolume $bigQueryVolume
77
+ {{- if eq \$provider "gcp" }}
78
+ - name: pubsub-key
79
+ secret:
80
+ secretName: pubsub
81
+ {{- else if eq \$provider "aws" }}
82
+ - name: sns-sqs-key
83
+ secret:
84
+ secretName: sns-sqs
85
+ {{- end }}
86
+ - name: tmpfs
87
+ emptyDir: {}
88
+ {{- if .Values.$chartName.PREEMPTIBLE }}
89
+ tolerations:
90
+ - key: "preemptible"
91
+ operator: "Equal"
92
+ value: "true"
93
+ effect: "NoSchedule"
94
+ affinity:
95
+ nodeAffinity:
96
+ preferredDuringSchedulingIgnoredDuringExecution:
97
+ - preference:
98
+ matchExpressions:
99
+ - key: cloud.google.com/gke-preemptible
100
+ operator: Exists
101
+ weight: 100
102
+ {{- end }}
103
+ containers:
104
+ - name: {{ .Chart.Name }}
105
+ imagePullPolicy: Always
106
+ image: gcr.io/leverege-docker-images/$dockerName:{{ .Values.$chartName.VERSION }}
107
+ securityContext:
108
+ readOnlyRootFilesystem: {{ .Values.securityContext.readOnlyRootFs }}
109
+ runAsNonRoot: true
110
+ runAsUser: 1000
111
+ allowPrivilegeEscalation: false
112
+ # UNCOMMENT IF YOU NEED PROBES
113
+ # readinessProbe:
114
+ # httpGet:
115
+ # path: /healthz
116
+ # port: api
117
+ # initialDelaySeconds: 10
118
+ # livenessProbe:
119
+ # httpGet:
120
+ # path: /livenessProbe
121
+ # port: api
122
+ # initialDelaySeconds: 15
123
+ # periodSeconds: 60
124
+ ports:
125
+ {{- if .Values.$chartName.port }}
126
+ - name: api
127
+ containerPort: {{ .Values.$chartName.port }}
128
+ {{- end }}
129
+ - name: node-metrics
130
+ containerPort: 5111
131
+ resources:
132
+ requests:
133
+ cpu: "50m"
134
+ memory: "128Mi"
135
+ limits:
136
+ cpu: "100m"
137
+ memory: "256Mi"
138
+ volumeMounts: $firebaseVolumeMount $bigQueryVolumeMount
139
+ {{- if eq \$provider "gcp" }}
140
+ - name: pubsub-key
141
+ mountPath: "/home/node/.gcp/pubsub"
142
+ {{- else if eq \$provider "aws" }}
143
+ - name: sns-sqs-key
144
+ mountPath: "/home/node/.aws"
145
+ {{- end }}
146
+ - name: tmpfs
147
+ mountPath: "/tmp"
148
+ envFrom:
149
+ - configMapRef:
150
+ name: default-configs
151
+ env:
152
+ # UNCOMMENT THE FOLLOWING FOR ENV VARIABLES
153
+ # Assuming you have the following lines in values.yaml =>
154
+ #
155
+ # $chartName:
156
+ # MY_ENV_VARIABLE: hello-world
157
+ #
158
+ #
159
+ # - name: MY_ENV_VARIABLE
160
+ # value: {{ .Values.$chartName.MY_ENV_VARIABLE }}
161
+ #
162
+ #
163
+ # UNCOMMENT THE FOLLOWING FOR MOUNTING SECRETS
164
+ # Assuming you ran =>
165
+ # kubectl create secret generic my-secret --from-literal=my-password=password123
166
+ #
167
+ # - name: ENV_SECRET_NAME
168
+ # valueFrom:
169
+ # secretKeyRef:
170
+ # name: my-secret
171
+ # key: my-password
172
+
173
+ {{- end -}}
174
+ EODEPLOYMENT
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ chartName=$1
4
+
5
+ touch helm/templates/scaler.yaml
6
+
7
+ cat<<EOSCALER > helm/templates/scaler.yaml
8
+ apiVersion: autoscaling/v1
9
+ kind: HorizontalPodAutoscaler
10
+ metadata:
11
+ name: {{ .Chart.Name }}
12
+ spec:
13
+ scaleTargetRef:
14
+ apiVersion: apps/v1
15
+ kind: Deployment
16
+ name: {{ .Chart.Name }}
17
+ minReplicas: {{ .Values.horizontalScaler.minReplicas }}
18
+ maxReplicas: {{ .Values.horizontalScaler.maxReplicas }}
19
+ targetCPUUtilizationPercentage: {{ .Values.horizontalScaler.maxCPU }}
20
+ EOSCALER
@@ -0,0 +1,21 @@
1
+ #!/bin/bash
2
+
3
+ chartName=$1
4
+ serviceType=$2
5
+
6
+ touch helm/templates/service.yaml
7
+
8
+ cat<<EOSERVICE > helm/templates/service.yaml
9
+ kind: Service
10
+ apiVersion: v1
11
+ metadata:
12
+ name: {{ .Chart.Name }}
13
+ spec:
14
+ type: $serviceType
15
+ selector:
16
+ app: {{ .Chart.Name }}
17
+ ports:
18
+ - protocol: TCP
19
+ port: {{ .Values.$chartName.serverPort }}
20
+ targetPort: api
21
+ EOSERVICE
package/src/helmup CHANGED
@@ -458,7 +458,7 @@ function doInstall() {
458
458
  ;;
459
459
 
460
460
  "reloader")
461
- helm upgrade --install reloader stable/reloader
461
+ helm upgrade --install reloader stakater/reloader
462
462
  ;;
463
463
 
464
464
  *)