@sap/cds-dk 4.9.7 → 6.0.1

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.
Files changed (143) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/bin/activate.js +4 -4
  3. package/bin/add.js +11 -5
  4. package/bin/bind.js +16 -28
  5. package/bin/build/all.js +2 -2
  6. package/bin/build/index.js +12 -19
  7. package/bin/cds.js +16 -17
  8. package/bin/compile/index.js +12 -3
  9. package/bin/deploy/index.js +14 -14
  10. package/bin/deploy/to-hana.js +4 -4
  11. package/bin/deploy/to-sqlite.js +1 -1
  12. package/bin/env.js +13 -4
  13. package/bin/eval.js +1 -1
  14. package/bin/fix-redirects.js +1 -1
  15. package/bin/import.js +44 -179
  16. package/bin/repl.js +1 -1
  17. package/bin/watch.js +10 -15
  18. package/lib/bind/BindingManager.js +87 -0
  19. package/lib/bind/bindingResolver.js +14 -85
  20. package/lib/bind/bindingUtil.js +18 -11
  21. package/lib/bind/cfKeyProvider.js +58 -29
  22. package/lib/bind/kubernetesCredentialsProvider.js +44 -21
  23. package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
  24. package/lib/cds.js +13 -0
  25. package/lib/client/base_command.js +3 -2
  26. package/lib/client/extend.js +6 -7
  27. package/lib/client/helper/fs_helper.js +7 -8
  28. package/lib/client/settings_manager.js +14 -10
  29. package/lib/compile/index.js +9 -10
  30. package/lib/compile/openapi/csdl2openapi.js +151 -43
  31. package/lib/compile/openapi/index.js +2 -2
  32. package/lib/compile/to-xsuaa.js +3 -2
  33. package/lib/import/common.js +342 -27
  34. package/lib/import/importapi.js +81 -13
  35. package/lib/import/message.js +4 -4
  36. package/lib/import/odata/index.js +141 -0
  37. package/lib/import/{utils → odata/metadataconverter}/MetadataConverter.js +2 -2
  38. package/lib/import/{utils → odata/metadataconverter}/MetadataConverterFactory.js +1 -1
  39. package/lib/import/{utils → odata/util}/MultiMap.js +0 -0
  40. package/lib/import/{v2parser.js → odata/v2parser.js} +405 -468
  41. package/lib/import/{v4parser.js → odata/v4parser.js} +311 -322
  42. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Aggregation.V1.json +0 -0
  43. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Authorization.V1.json +0 -0
  44. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Capabilities.V1.json +0 -0
  45. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Core.V1.json +0 -0
  46. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Measures.V1.json +0 -0
  47. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Validation.V1.json +0 -0
  48. package/lib/import/openapi/importOpenAPI.js +521 -0
  49. package/lib/import/openapi/index.js +19 -0
  50. package/lib/import/openapi/utilities.js +38 -0
  51. package/lib/index.js +1 -1
  52. package/lib/init/constants.js +7 -2
  53. package/lib/init/index.js +10 -3
  54. package/lib/init/template/_merging/existences.js +79 -19
  55. package/lib/init/template/approuter/files/mta.yaml.hbs +25 -11
  56. package/lib/init/template/approuter/files/package.json +1 -1
  57. package/lib/init/template/approuter/files/xs-app.json.hbs +7 -0
  58. package/lib/init/template/approuter/index.js +61 -54
  59. package/lib/init/template/auditlog/index.js +2 -2
  60. package/lib/init/template/data/index.js +2 -2
  61. package/lib/init/template/hana/files/mta.yaml.hbs +17 -17
  62. package/lib/init/template/hana/index.js +8 -4
  63. package/lib/init/template/helm/index.js +39 -0
  64. package/lib/init/template/helm/update/index.js +28 -0
  65. package/lib/init/template/java/index.js +7 -9
  66. package/lib/init/template/kibana-logging/index.js +5 -6
  67. package/lib/init/template/mta/index.js +4 -4
  68. package/lib/init/template/mtx/files/java/package.json +9 -9
  69. package/lib/init/template/mtx/files/mta.yaml.hbs +0 -7
  70. package/lib/init/template/mtx/files/xs-security.json.hbs +11 -20
  71. package/lib/init/template/mtx/index.js +50 -57
  72. package/lib/init/template/nodejs/files/package.json.hbs +2 -2
  73. package/lib/init/template/notebook/index.js +2 -0
  74. package/lib/init/template/templateBase.js +6 -4
  75. package/lib/init/template/xsuaa/index.js +9 -5
  76. package/lib/init/util/fsUtil.js +25 -10
  77. package/lib/init/util/logger.js +1 -1
  78. package/lib/init/util/mvnArchetypeUtil.js +4 -10
  79. package/lib/init/util/projectReader.js +16 -8
  80. package/lib/init/util/templateUtil.js +30 -34
  81. package/lib/k8s/ChartAdder.js +65 -0
  82. package/lib/k8s/ChartBuilder.js +186 -0
  83. package/lib/k8s/Context.js +71 -0
  84. package/lib/k8s/ImageUtil.js +21 -0
  85. package/lib/k8s/Model.d.ts +53 -0
  86. package/lib/k8s/auditlog/index.js +29 -0
  87. package/lib/k8s/auditlog/templates/auditlog.yaml +1 -0
  88. package/lib/k8s/buildModel.js +70 -0
  89. package/lib/k8s/connectivity/index.js +74 -0
  90. package/lib/k8s/connectivity/templates/connectivity-binding.yaml +9 -0
  91. package/lib/k8s/connectivity/templates/connectivity-proxy-info.yaml +16 -0
  92. package/lib/k8s/connectivity/templates/connectivity.yaml +1 -0
  93. package/lib/k8s/deployment/index.js +81 -0
  94. package/lib/k8s/deployment/subchart/Chart.yaml +6 -0
  95. package/lib/k8s/deployment/subchart/templates/_helpers.tpl +156 -0
  96. package/lib/k8s/deployment/subchart/templates/api-rule.yaml +24 -0
  97. package/lib/k8s/deployment/subchart/templates/deployment.yaml +110 -0
  98. package/lib/k8s/deployment/subchart/templates/network-policy.yaml +16 -0
  99. package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +11 -0
  100. package/lib/k8s/deployment/subchart/templates/secret.yaml +10 -0
  101. package/lib/k8s/deployment/subchart/templates/service-binding.yaml +27 -0
  102. package/lib/k8s/deployment/subchart/templates/service.yaml +11 -0
  103. package/lib/k8s/deployment/subchart/values-lint.yaml +8 -0
  104. package/lib/k8s/deployment/subchart/values.schema.json +449 -0
  105. package/lib/k8s/deployment/subchart/values.yaml +76 -0
  106. package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +81 -0
  107. package/lib/k8s/destinations/index.js +40 -0
  108. package/lib/k8s/destinations/lib.js +16 -0
  109. package/lib/k8s/destinations/templates/destinations.yaml +1 -0
  110. package/lib/k8s/docker/push.js +8 -0
  111. package/lib/k8s/event-mesh/index.js +53 -0
  112. package/lib/k8s/event-mesh/lib.js +9 -0
  113. package/lib/k8s/event-mesh/templates/event-mesh.yaml +1 -0
  114. package/lib/k8s/hana-deployer/index.js +38 -0
  115. package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +41 -0
  116. package/lib/k8s/helm/Release.js +80 -0
  117. package/lib/k8s/html5-apps-deployer/index.js +43 -0
  118. package/lib/k8s/html5-apps-deployer/templates/_html5_deployment_helpers.tpl +16 -0
  119. package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +53 -0
  120. package/lib/k8s/html5-apps-repo-host/index.js +26 -0
  121. package/lib/k8s/html5-apps-repo-host/lib.js +16 -0
  122. package/lib/k8s/html5-apps-repo-host/templates/html5-apps-repo-host.yaml +1 -0
  123. package/lib/k8s/index.js +153 -0
  124. package/lib/k8s/modules.js +36 -0
  125. package/lib/k8s/multitenancy/index.js +24 -0
  126. package/lib/k8s/multitenancy/lib.js +25 -0
  127. package/lib/k8s/multitenancy/templates/saas-registry.yaml +19 -0
  128. package/lib/k8s/readProject.js +14 -0
  129. package/lib/k8s/resources/helm/chart/templates/NOTES.txt +11 -0
  130. package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +221 -0
  131. package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +167 -0
  132. package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +103 -0
  133. package/lib/k8s/util.js +186 -0
  134. package/lib/k8s/wrapFeature.js +55 -0
  135. package/lib/k8s/xsuaa/index.js +64 -0
  136. package/lib/k8s/xsuaa/lib.js +14 -0
  137. package/lib/k8s/xsuaa/templates/xsuaa.yaml +1 -0
  138. package/lib/util/exec.js +1 -1
  139. package/lib/util/write.js +1 -1
  140. package/lib/watch/node-watch.js +43 -14
  141. package/npm-shrinkwrap.json +1221 -2743
  142. package/package.json +5 -5
  143. 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
- switch (node.type) {
172
- case 'MAP': {
173
- actions && actions.visitMap && actions.visitMap(node, index)
174
- const [collection] = collectionStack
175
- if (collection && collection.type === 'SEQ') { collectionStack.unshift(node); shifted = true}
176
- break;
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
- case 'SEQ':
179
- break;
180
- case 'PAIR':
181
- if (node.value && node.value.items) collectionStack.unshift(node.value)
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.type === 'SEQ' || node.type === 'FLOW_SEQ') && templateNode && templateNode.items) {
185
+ if (YAML.isSeq(node) && templateNode && templateNode.items) {
189
186
  actions.mergeCollection && actions.mergeCollection(node, templateNode)
190
- node.type === 'SEQ' && _traverseYAMLCollection(node, actions, templateNode)
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.type === 'PAIR') {
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.type === 'MAP') {
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.get(relationship.allWithinKeyPath[0])
273
+ const parent = _getYAMLProperty(target, relationship.allWithinKeyPath)
277
274
  for (const child of parent.items) {
278
- const c = child.get(relationship.inKeyPath[0])
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.type === 'MAP' && !childIsArray) {
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 (templateNode.type === 'SEQ' || templateNode.type === 'FLOW_SEQ') {
300
- const [,parent] = collectionStack
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}) => parent.get(keyPath[0]) === targetNode)
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.type === 'SEQ') {
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(YAML.createNode({ [relationship.into]: pair }))
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;