@sap/cds-dk 4.9.7 → 6.0.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 +77 -0
- package/bin/activate.js +4 -4
- package/bin/add.js +11 -5
- package/bin/bind.js +16 -28
- package/bin/build/all.js +2 -2
- package/bin/build/index.js +12 -19
- package/bin/cds.js +22 -19
- package/bin/compile/index.js +12 -3
- package/bin/deploy/index.js +15 -9
- package/bin/deploy/to-hana.js +4 -4
- package/bin/deploy/to-sqlite.js +1 -1
- package/bin/env.js +13 -4
- package/bin/eval.js +1 -1
- package/bin/fix-redirects.js +1 -1
- package/bin/import.js +44 -179
- package/bin/repl.js +1 -1
- package/bin/watch.js +10 -15
- package/lib/bind/BindingManager.js +87 -0
- package/lib/bind/bindingResolver.js +15 -87
- package/lib/bind/bindingUtil.js +18 -11
- package/lib/bind/cfKeyProvider.js +58 -29
- package/lib/bind/kubernetesCredentialsProvider.js +44 -21
- package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
- package/lib/cds.js +13 -0
- package/lib/client/base_command.js +3 -2
- package/lib/client/extend.js +6 -7
- package/lib/client/helper/fs_helper.js +7 -8
- package/lib/client/settings_manager.js +14 -10
- package/lib/compile/index.js +9 -10
- package/lib/compile/openapi/csdl2openapi.js +151 -43
- package/lib/compile/openapi/index.js +2 -2
- package/lib/compile/to-xsuaa.js +3 -2
- package/lib/import/common.js +342 -27
- package/lib/import/importapi.js +81 -13
- package/lib/import/message.js +4 -4
- package/lib/import/odata/index.js +141 -0
- package/lib/import/{utils → odata/metadataconverter}/MetadataConverter.js +2 -2
- package/lib/import/{utils → odata/metadataconverter}/MetadataConverterFactory.js +1 -1
- package/lib/import/{utils → odata/util}/MultiMap.js +0 -0
- package/lib/import/{v2parser.js → odata/v2parser.js} +405 -468
- package/lib/import/{v4parser.js → odata/v4parser.js} +311 -322
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Aggregation.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Authorization.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Capabilities.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Core.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Measures.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Validation.V1.json +0 -0
- package/lib/import/openapi/importOpenAPI.js +521 -0
- package/lib/import/openapi/index.js +19 -0
- package/lib/import/openapi/utilities.js +38 -0
- package/lib/index.js +1 -1
- package/lib/init/constants.js +7 -2
- package/lib/init/index.js +10 -3
- package/lib/init/template/_merging/existences.js +79 -19
- package/lib/init/template/approuter/files/mta.yaml.hbs +25 -11
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/xs-app.json.hbs +7 -0
- package/lib/init/template/approuter/index.js +61 -54
- package/lib/init/template/auditlog/index.js +2 -2
- package/lib/init/template/data/index.js +2 -2
- package/lib/init/template/hana/files/mta.yaml.hbs +17 -17
- package/lib/init/template/hana/index.js +8 -4
- package/lib/init/template/helm/index.js +39 -0
- package/lib/init/template/helm/update/index.js +28 -0
- package/lib/init/template/java/index.js +7 -9
- package/lib/init/template/kibana-logging/index.js +5 -6
- package/lib/init/template/lint/index.js +1 -1
- package/lib/init/template/mta/index.js +4 -4
- package/lib/init/template/mtx/files/java/package.json +9 -9
- package/lib/init/template/mtx/files/mta.yaml.hbs +0 -7
- package/lib/init/template/mtx/files/xs-security.json.hbs +11 -20
- package/lib/init/template/mtx/index.js +50 -57
- package/lib/init/template/nodejs/files/package.json.hbs +2 -2
- package/lib/init/template/notebook/index.js +2 -0
- package/lib/init/template/templateBase.js +8 -6
- package/lib/init/template/xsuaa/index.js +9 -5
- package/lib/init/util/fsUtil.js +25 -10
- package/lib/init/util/logger.js +1 -1
- package/lib/init/util/mvnArchetypeUtil.js +4 -10
- package/lib/init/util/projectReader.js +17 -9
- package/lib/init/util/templateUtil.js +30 -34
- package/lib/k8s/ChartAdder.js +65 -0
- package/lib/k8s/ChartBuilder.js +186 -0
- package/lib/k8s/Context.js +71 -0
- package/lib/k8s/ImageUtil.js +21 -0
- package/lib/k8s/Model.d.ts +53 -0
- package/lib/k8s/auditlog/index.js +29 -0
- package/lib/k8s/auditlog/templates/auditlog.yaml +1 -0
- package/lib/k8s/buildModel.js +70 -0
- package/lib/k8s/connectivity/index.js +80 -0
- package/lib/k8s/connectivity/templates/connectivity-binding.yaml +9 -0
- package/lib/k8s/connectivity/templates/connectivity-proxy-info.yaml +20 -0
- package/lib/k8s/connectivity/templates/connectivity.yaml +1 -0
- package/lib/k8s/deployment/index.js +81 -0
- package/lib/k8s/deployment/subchart/Chart.yaml +6 -0
- package/lib/k8s/deployment/subchart/templates/_helpers.tpl +156 -0
- package/lib/k8s/deployment/subchart/templates/api-rule.yaml +24 -0
- package/lib/k8s/deployment/subchart/templates/deployment.yaml +110 -0
- package/lib/k8s/deployment/subchart/templates/network-policy.yaml +16 -0
- package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +11 -0
- package/lib/k8s/deployment/subchart/templates/secret.yaml +10 -0
- package/lib/k8s/deployment/subchart/templates/service-binding.yaml +27 -0
- package/lib/k8s/deployment/subchart/templates/service.yaml +11 -0
- package/lib/k8s/deployment/subchart/values-lint.yaml +8 -0
- package/lib/k8s/deployment/subchart/values.schema.json +449 -0
- package/lib/k8s/deployment/subchart/values.yaml +76 -0
- package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +81 -0
- package/lib/k8s/destinations/index.js +40 -0
- package/lib/k8s/destinations/lib.js +16 -0
- package/lib/k8s/destinations/templates/destinations.yaml +1 -0
- package/lib/k8s/docker/push.js +8 -0
- package/lib/k8s/event-mesh/index.js +53 -0
- package/lib/k8s/event-mesh/lib.js +9 -0
- package/lib/k8s/event-mesh/templates/event-mesh.yaml +1 -0
- package/lib/k8s/hana-deployer/index.js +38 -0
- package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +41 -0
- package/lib/k8s/helm/Release.js +80 -0
- package/lib/k8s/html5-apps-deployer/index.js +43 -0
- package/lib/k8s/html5-apps-deployer/templates/_html5_deployment_helpers.tpl +16 -0
- package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +53 -0
- package/lib/k8s/html5-apps-repo-host/index.js +26 -0
- package/lib/k8s/html5-apps-repo-host/lib.js +16 -0
- package/lib/k8s/html5-apps-repo-host/templates/html5-apps-repo-host.yaml +1 -0
- package/lib/k8s/index.js +153 -0
- package/lib/k8s/modules.js +36 -0
- package/lib/k8s/multitenancy/index.js +24 -0
- package/lib/k8s/multitenancy/lib.js +25 -0
- package/lib/k8s/multitenancy/templates/saas-registry.yaml +19 -0
- package/lib/k8s/readProject.js +14 -0
- package/lib/k8s/resources/helm/chart/templates/NOTES.txt +11 -0
- package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +221 -0
- package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +167 -0
- package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +103 -0
- package/lib/k8s/util.js +186 -0
- package/lib/k8s/wrapFeature.js +55 -0
- package/lib/k8s/xsuaa/index.js +64 -0
- package/lib/k8s/xsuaa/lib.js +17 -0
- package/lib/k8s/xsuaa/templates/xsuaa.yaml +1 -0
- package/lib/lint/generators.js +1 -1
- package/lib/util/exec.js +1 -1
- package/lib/util/write.js +1 -1
- package/lib/watch/node-watch.js +43 -14
- package/npm-shrinkwrap.json +923 -2705
- package/package.json +5 -5
- package/lib/import/index.js +0 -235
|
@@ -146,7 +146,7 @@ module.exports = class TemplateUtil {
|
|
|
146
146
|
const _getYAMLProperty = (object, keyPath) => keyPath.reduce((p, n) => p && p.get(n), object)
|
|
147
147
|
|
|
148
148
|
const _validateConstraints = (node, dict, index) => {
|
|
149
|
-
const _addToMap = existences => existences
|
|
149
|
+
const _addToMap = existences => existences && existences
|
|
150
150
|
.filter(existence => {
|
|
151
151
|
if (!existence.in) return true
|
|
152
152
|
const neededParent = existence.in && dict.get(existence.in.ref) && dict.get(existence.in.ref).node
|
|
@@ -168,30 +168,27 @@ module.exports = class TemplateUtil {
|
|
|
168
168
|
|
|
169
169
|
function _traverseYAMLNode(node, index, actions, templateNode) {
|
|
170
170
|
let shifted = false
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
if (YAML.isMap(node)) {
|
|
172
|
+
actions && actions.visitMap && actions.visitMap(node, index)
|
|
173
|
+
const [collection] = collectionStack
|
|
174
|
+
if (YAML.isSeq(collection)) {
|
|
175
|
+
collectionStack.unshift(node)
|
|
176
|
+
shifted = true
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
break;
|
|
183
|
-
case 'PLAIN':
|
|
184
|
-
break;
|
|
178
|
+
} else if (YAML.isPair(node) && node.value && node.value.items) {
|
|
179
|
+
collectionStack.unshift(node.value)
|
|
180
|
+
} else if (YAML.isScalar(node) && semantics.forceOverwrite && templateNode) {
|
|
181
|
+
node.value = templateNode.value
|
|
185
182
|
}
|
|
186
183
|
|
|
187
184
|
if (node.items) {
|
|
188
|
-
if ((node
|
|
185
|
+
if (YAML.isSeq(node) && templateNode && templateNode.items) {
|
|
189
186
|
actions.mergeCollection && actions.mergeCollection(node, templateNode)
|
|
190
|
-
|
|
187
|
+
_traverseYAMLCollection(node, actions, templateNode)
|
|
191
188
|
} else {
|
|
192
189
|
_traverseYAMLCollection(node, actions, templateNode)
|
|
193
190
|
}
|
|
194
|
-
} else if (node.value && node.value.items) {
|
|
191
|
+
} else if (node.value && node.value.items || node.value && semantics.forceOverwrite) {
|
|
195
192
|
_traverseYAMLNode(node.value, index, actions, templateNode && templateNode.value)
|
|
196
193
|
}
|
|
197
194
|
|
|
@@ -218,12 +215,12 @@ module.exports = class TemplateUtil {
|
|
|
218
215
|
if (templateIndex !== undefined) templateIndexMap.set(templateIndex, i)
|
|
219
216
|
}
|
|
220
217
|
if (!templateCollection) _traverseYAMLNode(node, i, actions)
|
|
221
|
-
})
|
|
218
|
+
})
|
|
222
219
|
|
|
223
220
|
if (!templateCollection) return
|
|
224
221
|
|
|
225
222
|
templateCollection.items.forEach((templateNode, templateIndex) => {
|
|
226
|
-
if (templateNode
|
|
223
|
+
if (YAML.isPair(templateNode)) {
|
|
227
224
|
const i = keyIndexMap.get(templateNode.key.value)
|
|
228
225
|
const [collection] = collectionStack
|
|
229
226
|
const targetNode = collection.items && collection.items[i]
|
|
@@ -232,7 +229,7 @@ module.exports = class TemplateUtil {
|
|
|
232
229
|
} else if (actions.mergePair) {
|
|
233
230
|
actions.mergePair(collection, templateNode)
|
|
234
231
|
}
|
|
235
|
-
} else if (templateNode
|
|
232
|
+
} else if (YAML.isMap(templateNode)) {
|
|
236
233
|
const targetNode = collection.items[templateIndexMap.get(templateIndex)]
|
|
237
234
|
if (actions.mergeCollection) actions.mergeCollection(targetNode, templateNode)
|
|
238
235
|
if (targetNode) {
|
|
@@ -273,9 +270,9 @@ module.exports = class TemplateUtil {
|
|
|
273
270
|
existence.collection.delete(existence.index)
|
|
274
271
|
|
|
275
272
|
relationships && relationships.forEach(relationship => {
|
|
276
|
-
const parent = target
|
|
273
|
+
const parent = _getYAMLProperty(target, relationship.allWithinKeyPath)
|
|
277
274
|
for (const child of parent.items) {
|
|
278
|
-
const c = child
|
|
275
|
+
const c = _getYAMLProperty(child, relationship.inKeyPath)
|
|
279
276
|
const i = c.items.findIndex(node =>
|
|
280
277
|
node.get(relationship.into) === _getProperty(existence.json, relationship.existenceKeyPath)
|
|
281
278
|
)
|
|
@@ -290,14 +287,13 @@ module.exports = class TemplateUtil {
|
|
|
290
287
|
_traverseYAMLNode(target.contents, null, {
|
|
291
288
|
mergePair: (collection, templateNode) => {
|
|
292
289
|
const childIsArray = false//templateNode.value.type !== 'MAP' && !!templateNode.value.items
|
|
293
|
-
if (collection
|
|
290
|
+
if (YAML.isMap(collection) && !childIsArray) {
|
|
294
291
|
collection.add(templateNode)
|
|
295
|
-
//this.fsUtil.writeYAML(targetPath, target)
|
|
296
292
|
}
|
|
297
293
|
},
|
|
298
294
|
mergeCollection: (targetNode, templateNode) => {
|
|
299
|
-
if (
|
|
300
|
-
|
|
295
|
+
if (YAML.isSeq(templateNode)) {
|
|
296
|
+
let [,parent] = collectionStack
|
|
301
297
|
const missingExistences = semantics.existences
|
|
302
298
|
.filter(e => e)
|
|
303
299
|
.filter(existence => {
|
|
@@ -307,13 +303,15 @@ module.exports = class TemplateUtil {
|
|
|
307
303
|
return !existence.in || requiredParent === parent
|
|
308
304
|
|
|
309
305
|
})
|
|
310
|
-
.filter(({keyPath}) =>
|
|
306
|
+
.filter(({keyPath}) => {
|
|
307
|
+
if (keyPath.length > collectionStack.length + 1) return
|
|
308
|
+
if (keyPath.length > 1) parent = collectionStack[keyPath.length]
|
|
309
|
+
return _getYAMLProperty(parent, keyPath) === targetNode
|
|
310
|
+
})
|
|
311
311
|
.filter(({ref}) => !existenceMap.get(ref))
|
|
312
312
|
missingExistences.forEach(existence => {
|
|
313
313
|
const templateNode = templateExistenceMap.get(existence.ref).node
|
|
314
|
-
templateNode.spaceBefore = existence.spaceBefore
|
|
315
314
|
targetNode.add(templateNode)
|
|
316
|
-
//this.fsUtil.writeYAML(targetPath, target)
|
|
317
315
|
})
|
|
318
316
|
}
|
|
319
317
|
},
|
|
@@ -329,7 +327,7 @@ module.exports = class TemplateUtil {
|
|
|
329
327
|
// 7. Create missing relationships
|
|
330
328
|
_traverseYAMLNode(target.contents, null, {
|
|
331
329
|
mergeCollection: (targetNode, templateNode) => {
|
|
332
|
-
if (templateNode
|
|
330
|
+
if (YAML.isSeq(templateNode)) {
|
|
333
331
|
const targetJSON = YAML.parse(String(targetNode))
|
|
334
332
|
const relationship = semantics.relationships && semantics.relationships
|
|
335
333
|
.filter(r => r)
|
|
@@ -347,8 +345,7 @@ module.exports = class TemplateUtil {
|
|
|
347
345
|
)
|
|
348
346
|
.map(({ref}) => existenceMap.get(ref).node.get(relationship.into))
|
|
349
347
|
missingPairs.forEach(pair => {
|
|
350
|
-
targetNode.add(
|
|
351
|
-
//await this.fsUtil.writeYAML(targetPath, target)
|
|
348
|
+
targetNode.add({ [relationship.into]: pair })
|
|
352
349
|
})
|
|
353
350
|
}
|
|
354
351
|
}
|
|
@@ -361,5 +358,4 @@ module.exports = class TemplateUtil {
|
|
|
361
358
|
_isObject(item) {
|
|
362
359
|
return item && typeof item === 'object' && !Array.isArray(item)
|
|
363
360
|
}
|
|
364
|
-
}
|
|
365
|
-
|
|
361
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
const { yaml } = require('./modules');
|
|
4
|
+
const fs = require('fs').promises;
|
|
5
|
+
const FsUtil = require('../../lib/init/util/fsUtil')
|
|
6
|
+
const TemplateUtil = require('../../lib/init/util/templateUtil')
|
|
7
|
+
|
|
8
|
+
class ChartAdder {
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param {{ builder: import('./ChartBuilder') }} param0
|
|
12
|
+
*/
|
|
13
|
+
constructor({ builder, template, chartValues, chartDependencies, app, mergeSemantics }) {
|
|
14
|
+
this.builder = builder;
|
|
15
|
+
this.template = template;
|
|
16
|
+
this.chartValues = chartValues;
|
|
17
|
+
this.app = app;
|
|
18
|
+
this.mergeSemantics = mergeSemantics;
|
|
19
|
+
this.dependencies = chartDependencies;
|
|
20
|
+
this.templateUtil = new TemplateUtil(null, new FsUtil());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async execute() {
|
|
24
|
+
await this.copyTemplates();
|
|
25
|
+
await this.addChartValues();
|
|
26
|
+
await this.addChartFile();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async copyTemplates() {
|
|
30
|
+
await this.builder.addAllTemplates(this.template);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async addChartValues() {
|
|
34
|
+
const values = this.chartValues;
|
|
35
|
+
const valuesYaml = yaml.stringify(JSON.parse(JSON.stringify(values)));
|
|
36
|
+
|
|
37
|
+
await this.builder.mergeChartFile('values.yaml', valuesYaml, this.mergeSemantics)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async addChartFile() {
|
|
41
|
+
const chart = {
|
|
42
|
+
apiVersion: 'v2',
|
|
43
|
+
name: this.app.name,
|
|
44
|
+
description: this.app.description,
|
|
45
|
+
type: 'application',
|
|
46
|
+
version: this.app.version, // should be Chart version
|
|
47
|
+
appVersion: this.app.version,
|
|
48
|
+
dependencies: this.dependencies
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const header = `#
|
|
52
|
+
# This file is generated by "cds add helm"
|
|
53
|
+
`
|
|
54
|
+
|
|
55
|
+
const chartYaml = header + yaml.stringify(JSON.parse(JSON.stringify(chart)));
|
|
56
|
+
await this.builder.writeChartFile('Chart.yaml', chartYaml);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getSavedValues() {
|
|
60
|
+
return yaml.parse(await fs.readFile(this.builder.chartPath('values.yaml'), 'utf-8'));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
module.exports = ChartAdder;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const { cds, yaml } = require('./modules');
|
|
2
|
+
const { readFileIfExists, throwError, parseMd5File, writeMd5File } = require('./util');
|
|
3
|
+
const FsUtil = require('../../lib/init/util/fsUtil')
|
|
4
|
+
const TemplateUtil = require('../../lib/init/util/templateUtil')
|
|
5
|
+
const fs = require('fs').promises;
|
|
6
|
+
const Path = require('path');
|
|
7
|
+
const LOG = cds.log('k8s');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const crypto = require('crypto');
|
|
14
|
+
|
|
15
|
+
class ChartBuilder {
|
|
16
|
+
constructor({ path, rootPath = process.cwd() }) {
|
|
17
|
+
this.path = path;
|
|
18
|
+
this.rootPath = rootPath;
|
|
19
|
+
this.tackedFiles = [];
|
|
20
|
+
this.oldTrackingData = undefined;
|
|
21
|
+
this.newTrackingData = {};
|
|
22
|
+
this.templateUtil = new TemplateUtil(null, new FsUtil());
|
|
23
|
+
this.ended = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async begin() {
|
|
27
|
+
await this.loadOldTrackingData();
|
|
28
|
+
await this.makeDirs();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async makeDirs() {
|
|
32
|
+
await fs.mkdir(Path.join(this.path, 'templates'), { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async writeChartFile(fileName, data) {
|
|
36
|
+
if (this.ended) throwError `Internal Error: Cannot write file ${fileName} after end()`;
|
|
37
|
+
|
|
38
|
+
const filePath = Path.join(this.path, fileName);
|
|
39
|
+
if (!await this.checkAndWarnIfChartFileIsModified(filePath)) {
|
|
40
|
+
await fs.writeFile(filePath, data);
|
|
41
|
+
}
|
|
42
|
+
this.track(filePath, data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async mergeChartFile(fileName, data, mergeSemantics) {
|
|
46
|
+
const filePath = Path.join(this.path, fileName);
|
|
47
|
+
const modified = await this.isChartFileModified(filePath);
|
|
48
|
+
if (modified) {
|
|
49
|
+
LOG(`Merging updates into modified file "${filePath}"`);
|
|
50
|
+
const newYamlContent = yaml.parseDocument(data);
|
|
51
|
+
|
|
52
|
+
await this.templateUtil.mergeYAML(filePath, newYamlContent, null, mergeSemantics);
|
|
53
|
+
} else {
|
|
54
|
+
await this.writeChartFile(fileName, data);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.track(filePath, data);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async addChartFile(sourcePath, { fileName = Path.basename(sourcePath), merge = false, optional = false, mergeSemantics } = {}) {
|
|
61
|
+
if (!Path.isAbsolute(sourcePath)) {
|
|
62
|
+
sourcePath = Path.join(this.rootPath, sourcePath);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const data = await readFileIfExists(sourcePath, 'utf-8');
|
|
66
|
+
if (typeof data === "undefined") {
|
|
67
|
+
if (optional)
|
|
68
|
+
return false;
|
|
69
|
+
else
|
|
70
|
+
throwError `File *${sourcePath}* not found.`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (merge) {
|
|
74
|
+
await this.mergeChartFile(fileName, data, mergeSemantics);
|
|
75
|
+
} else {
|
|
76
|
+
await this.writeChartFile(fileName, data);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async addTemplate(sourcePath, sourceFileName, { targetFileName = sourceFileName, merge = false } = {}) {
|
|
83
|
+
await this.addChartFile(Path.join(sourcePath, sourceFileName), { fileName: Path.join('templates', targetFileName), merge });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async addSubchartFiles(sourceBasePath, subchart) {
|
|
87
|
+
await this.addAllFiles(sourceBasePath, Path.join('charts', subchart));
|
|
88
|
+
await this.addAllFiles(Path.join(sourceBasePath, 'templates'), Path.join('charts', subchart, 'templates'));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async addAllTemplates(sourceBasePath, templatesDir = 'templates') {
|
|
92
|
+
await this.addAllFiles(Path.join(sourceBasePath, templatesDir), 'templates');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async addAllFiles(sourcePath, targetPath = '.') {
|
|
96
|
+
await fs.mkdir(this.chartPath(targetPath), { recursive: true });
|
|
97
|
+
for (const sourceFile of await fs.readdir(sourcePath, { withFileTypes: true })) {
|
|
98
|
+
if (sourceFile.isFile()) {
|
|
99
|
+
await this.addChartFile(Path.join(sourcePath, sourceFile.name), { fileName: Path.join(targetPath, sourceFile.name) });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
async loadOldTrackingData() {
|
|
106
|
+
this.oldTrackingData = await parseMd5File(Path.join(this.path, '.cds-add-helm-files.md5'));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async track(path, data) {
|
|
110
|
+
const key = pathKey(this.path, path);
|
|
111
|
+
this.newTrackingData[key] = hash(data);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async checkAndWarnIfChartFileIsModified(path) {
|
|
115
|
+
if (await this.isChartFileModified(path)) {
|
|
116
|
+
LOG.warn(modifiedMsg(path));
|
|
117
|
+
return true;
|
|
118
|
+
} else {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async isChartFileModified(path) {
|
|
124
|
+
const data = await readFileIfExists(path, 'utf-8');
|
|
125
|
+
if(data == undefined) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const key = pathKey(this.path, path);
|
|
130
|
+
const currentHash = hash(data);
|
|
131
|
+
return this.oldTrackingData != undefined && this.oldTrackingData[key] != currentHash;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async end() {
|
|
135
|
+
this.ended = true;
|
|
136
|
+
|
|
137
|
+
for (const path of this.obsoleteFiles()) {
|
|
138
|
+
if (await this.isChartFileModified(path)) {
|
|
139
|
+
LOG.warn(`Kept no longer used file "${path}" because it is modified`);
|
|
140
|
+
} else {
|
|
141
|
+
LOG.warn(`Removing no longer used file "${path}" from chart`);
|
|
142
|
+
try {
|
|
143
|
+
await fs.unlink(path);
|
|
144
|
+
} catch(error) {
|
|
145
|
+
if (error.code !== 'ENOENT') throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
await this.writeNewTrackingData();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
obsoleteFiles() {
|
|
154
|
+
return Object.keys(this.oldTrackingData).filter( path => !this.newTrackingData[path]).map( path => this.a(path) );
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async writeNewTrackingData() {
|
|
158
|
+
await writeMd5File(Path.join(this.path, '.cds-add-helm-files.md5'), this.newTrackingData)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
a(path) {
|
|
162
|
+
return Path.join(this.path, path)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
chartPath(...path) {
|
|
166
|
+
return Path.join(this.path, ...path);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function hash(data) {
|
|
171
|
+
return crypto.createHash('md5').update(data).digest('base64');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function pathKey(basePath, path) {
|
|
175
|
+
return Path.posix.normalize(toPosix(Path.relative(basePath, path)))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function toPosix(path) {
|
|
179
|
+
return path.replace(/\\/g, '/');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function modifiedMsg(path) {
|
|
183
|
+
return `Chart file "${path}" has been modified and will not be updated.`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = ChartBuilder;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const { throwError } = require('./util');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The context holds elements that are used to build up the CAP project. It allows
|
|
5
|
+
* decoupling between this objects. It should be used in a way that the Context is initialized
|
|
6
|
+
* with the elements (also referred to as "plugins") and each of them implements the required
|
|
7
|
+
* interface for the type of element.
|
|
8
|
+
*/
|
|
9
|
+
class Context {
|
|
10
|
+
constructor(objs) {
|
|
11
|
+
this.modified = true;
|
|
12
|
+
this.objs = objs || {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async get(name) {
|
|
16
|
+
if (this.objs[name]) {
|
|
17
|
+
if (typeof this.objs[name] === 'function') {
|
|
18
|
+
this.objs[name] = await this.objs[name](this) || {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return this.objs[name];
|
|
22
|
+
} else {
|
|
23
|
+
throwError `Unknown module *${name}*`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async loadAll() {
|
|
28
|
+
while (this.modified) {
|
|
29
|
+
this.modified = false;
|
|
30
|
+
for (const name in this.objs) {
|
|
31
|
+
await this.get(name);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
add(name, obj) {
|
|
37
|
+
this.modified = true;
|
|
38
|
+
this.objs[name] = obj;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
has(name) {
|
|
42
|
+
return !!this.objs[name];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async filter(name) {
|
|
46
|
+
await this.loadAll();
|
|
47
|
+
return Object.values(this.objs).filter( obj => obj[name] );
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async forEach(name, call) {
|
|
51
|
+
const filtered = await this.filter(name);
|
|
52
|
+
for (const plugin of filtered) {
|
|
53
|
+
await call(plugin);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async collect(name) {
|
|
58
|
+
let obj = {};
|
|
59
|
+
await this.forEach(name, async (plugin) => obj = {...obj, ...await plugin[name]()});
|
|
60
|
+
return Object.keys(obj).length > 0 ? obj : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async collectBool(name) {
|
|
64
|
+
let bool = false;
|
|
65
|
+
await this.forEach(name, async (plugin) => bool = bool || await plugin[name]());
|
|
66
|
+
return bool;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = Context;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { throwError } = require('./util');
|
|
2
|
+
|
|
3
|
+
class ImageUtil {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config.images;
|
|
6
|
+
}
|
|
7
|
+
imageName(imageSuffix) {
|
|
8
|
+
const imagePrefix = this.config.imagePrefix || throwError `Expect *images.prefix* property.`;
|
|
9
|
+
return `${imagePrefix}-${imageSuffix}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get imageRegistry() {
|
|
13
|
+
return this.config.imageRegistry || throwError `Expect *images.registry* property.`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
fullImageName(suffix) {
|
|
17
|
+
return `${this.imageRegistry}/${this.imageName(suffix)}:latest`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = ImageUtil;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* CDS Kubernetes Configuration
|
|
4
|
+
*/
|
|
5
|
+
interface BaseConfig {
|
|
6
|
+
/** image prefix */
|
|
7
|
+
image: string,
|
|
8
|
+
cloudService: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface RegistryConfig {
|
|
12
|
+
imageRegistry: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface DeployConfig {
|
|
16
|
+
releaseName: string,
|
|
17
|
+
namespace: string,
|
|
18
|
+
imagePullSecret: string,
|
|
19
|
+
dbSecret: string,
|
|
20
|
+
domain: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Service {
|
|
24
|
+
binding: string;
|
|
25
|
+
name: string;
|
|
26
|
+
label: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ApplicationModel {
|
|
30
|
+
isJava: boolean;
|
|
31
|
+
destinations: Map<string,string>; // Unfortunately today needed at build time
|
|
32
|
+
services: Service[]; // Need to know if destination service is required?
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface BuildModel extends BaseConfig, ApplicationModel {
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Data required at image push and deploy time
|
|
40
|
+
*/
|
|
41
|
+
interface PushModel extends BaseConfig, RegistryConfig {
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Data required at chart add time
|
|
46
|
+
*/
|
|
47
|
+
interface AddChartModel extends BaseConfig, ApplicationModel {
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface Model extends BaseConfig, ApplicationModel {
|
|
51
|
+
registry : RegistryConfig,
|
|
52
|
+
deployment : DeployConfig
|
|
53
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const wrapFeature = require('../wrapFeature');
|
|
2
|
+
|
|
3
|
+
module.exports = wrapFeature('auditlog', {
|
|
4
|
+
getServiceBindings() {
|
|
5
|
+
return {
|
|
6
|
+
auditlog: {
|
|
7
|
+
serviceInstanceName: 'auditlog'
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
getChartCapabilities() {
|
|
13
|
+
return {
|
|
14
|
+
auditlog: {
|
|
15
|
+
serviceOfferingName: 'auditlog-api',
|
|
16
|
+
servicePlanName: 'default'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async addToChart(builder, { auditlog }) {
|
|
22
|
+
await builder.addAllTemplates(__dirname);
|
|
23
|
+
|
|
24
|
+
const { config } = auditlog;
|
|
25
|
+
if (config) {
|
|
26
|
+
await builder.addChartFile(config);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{- include "cap.service-instance" . }}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { throwError } = require('./util');
|
|
2
|
+
const readProject = require('./readProject');
|
|
3
|
+
const Path = require('path');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build data model of the CAP project for Kubernetes build, push, charts and chart deployment
|
|
7
|
+
*
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
async function buildModel(context) {
|
|
11
|
+
const k8s = await context.get('config');
|
|
12
|
+
const projectPath = await context.get('projectPath');
|
|
13
|
+
|
|
14
|
+
const project = await readProject(projectPath);
|
|
15
|
+
const cap = project.cap;
|
|
16
|
+
|
|
17
|
+
const services = buildServices(cap);
|
|
18
|
+
const isJava = typeof k8s.isJava !== 'undefined' ? k8s.isJava : cap.isJava;
|
|
19
|
+
const language = isJava ? 'Java' : 'Node';
|
|
20
|
+
|
|
21
|
+
const model = {
|
|
22
|
+
app: {
|
|
23
|
+
name: cap.appName,
|
|
24
|
+
description: cap.appDescription,
|
|
25
|
+
version: cap.appVersion
|
|
26
|
+
},
|
|
27
|
+
isMultiTenant: cap.isMultitenant,
|
|
28
|
+
isJava,
|
|
29
|
+
language,
|
|
30
|
+
services
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
model.getChartValues = function() {
|
|
34
|
+
return {
|
|
35
|
+
global: {
|
|
36
|
+
domain: null,
|
|
37
|
+
imagePullSecret: {
|
|
38
|
+
name: null
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
model.path = function(path = '.') {
|
|
45
|
+
return Path.join(projectPath, path);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return model;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildServices(cap) {
|
|
52
|
+
const prefix = `${cap.appName}-`;
|
|
53
|
+
let services = {};
|
|
54
|
+
for (const req of cap.requires) {
|
|
55
|
+
const resource = req.resource;
|
|
56
|
+
if (!resource.name.startsWith(prefix)) throwError `Unexpected resource name _${resource.name}_. Should start with _${prefix}_`;
|
|
57
|
+
const name = resource.name.substr(prefix.length);
|
|
58
|
+
|
|
59
|
+
if (name === 'db') continue; // TODO: hard coded with srv.bindings.db.secret;
|
|
60
|
+
|
|
61
|
+
// TODO: needs improvement
|
|
62
|
+
services[name] = {
|
|
63
|
+
label: resource.service
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return services;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = buildModel;
|