@sap/cds-dk 8.7.2 → 8.8.0

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 (81) hide show
  1. package/CHANGELOG.md +39 -3
  2. package/bin/bind.js +7 -8
  3. package/bin/cds.js +2 -4
  4. package/bin/compile/index.js +2 -1
  5. package/bin/deploy/index.js +10 -5
  6. package/bin/lint.js +10 -4
  7. package/bin/watch.js +48 -7
  8. package/lib/bind/{cfKeyProvider.js → cf.js} +43 -53
  9. package/lib/bind/k8s.js +138 -0
  10. package/lib/bind/plugin.js +48 -0
  11. package/lib/bind/shared.js +267 -0
  12. package/lib/build/buildTaskEngine.js +1 -1
  13. package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +0 -2
  14. package/lib/build/plugins/helm/files/subcharts/web-application/Chart.yaml +1 -3
  15. package/lib/build/plugins/helm/files/subcharts/web-application/templates/service-binding.yaml +3 -0
  16. package/lib/build/plugins/helm/files/subcharts/web-application/values.schema.json +3 -0
  17. package/lib/build/plugins/helm/files/subcharts/web-application/values.yaml +2 -1
  18. package/lib/build/util.js +1 -1
  19. package/lib/compile/to-mermaid.js +1 -2
  20. package/lib/deploy/to-hana/hana.js +4 -4
  21. package/lib/deploy/to-hana/mtaUtil.js +0 -13
  22. package/lib/import/common.js +6 -7
  23. package/lib/init/constants.js +1 -1
  24. package/lib/init/merge.js +42 -33
  25. package/lib/init/projectReader.js +15 -7
  26. package/lib/init/registries/mta.js +9 -3
  27. package/lib/init/template/approuter/files/mta.yaml.hbs +2 -2
  28. package/lib/init/template/approuter/files/package.json +1 -1
  29. package/lib/init/template/approuter/files/xs-app.json.hbs +0 -11
  30. package/lib/init/template/approuter/index.js +2 -2
  31. package/lib/init/template/cloud-logging/files/package.json.hbs +12 -1
  32. package/lib/init/template/cloud-logging/files/values.yaml.hbs +0 -2
  33. package/lib/init/template/cloud-logging/index.js +8 -15
  34. package/lib/init/template/destination/files/values.yaml +4 -4
  35. package/lib/init/template/destination/index.js +1 -1
  36. package/lib/init/template/html5-repo/files/app-package.json.hbs +0 -2
  37. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +25 -0
  38. package/lib/init/template/html5-repo/files/mta.yaml.hbs +1 -2
  39. package/lib/init/template/html5-repo/files/package.json.hbs +1 -1
  40. package/lib/init/template/html5-repo/files/ui-resources/package.json.hbs +11 -0
  41. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +4 -7
  42. package/lib/init/template/html5-repo/files/values.yaml.hbs +6 -4
  43. package/lib/init/template/html5-repo/index.js +22 -10
  44. package/lib/init/template/http/index.js +1 -1
  45. package/lib/init/template/ias/files/mta.yaml.hbs +1 -1
  46. package/lib/init/template/ias/files/sidecar.package.json.hbs +12 -0
  47. package/lib/init/template/ias/index.js +1 -3
  48. package/lib/init/template/multitenancy/files/mta.yaml.hbs +8 -10
  49. package/lib/init/template/multitenancy/index.js +1 -1
  50. package/lib/init/template/portal/files/mta.yaml.hbs +9 -0
  51. package/lib/init/template/portal/index.js +3 -1
  52. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +119 -119
  53. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
  54. package/lib/init/template/telemetry/index.js +0 -13
  55. package/lib/init/template/typer/files/package.json +3 -0
  56. package/lib/init/template/workzone-standard/files/cdm.json.hbs +97 -0
  57. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +23 -0
  58. package/lib/init/template/workzone-standard/index.js +70 -39
  59. package/lib/init/template/xsuaa/files/mta.yaml.hbs +2 -2
  60. package/lib/init/template/xsuaa/files/values.yaml.hbs +0 -2
  61. package/lib/init/template/xsuaa/index.js +22 -10
  62. package/lib/lint/Linter.js +242 -201
  63. package/lib/lint/io.js +4 -7
  64. package/lib/mtx/util/cf.js +1 -2
  65. package/lib/util/cf.js +104 -84
  66. package/lib/util/command.js +1 -2
  67. package/lib/util/git.js +1 -1
  68. package/npm-shrinkwrap.json +60 -43
  69. package/package.json +1 -1
  70. package/bin/deploy/to-cf.js +0 -0
  71. package/bin/deploy/via-mta.js +0 -0
  72. package/lib/bind/KubernetesApiAccessor.js +0 -81
  73. package/lib/bind/bindingManager.js +0 -89
  74. package/lib/bind/bindingResolver.js +0 -54
  75. package/lib/bind/bindingUtil.js +0 -129
  76. package/lib/bind/index.js +0 -127
  77. package/lib/bind/kubernetesCredentialsProvider.js +0 -84
  78. package/lib/bind/userCdsrcJsonStorageProvider.js +0 -92
  79. package/lib/init/template/destination/files/package.sidecar.json +0 -7
  80. package/lib/init/template/html5-repo/files/package.sidecar.json +0 -7
  81. package/lib/init/template/telemetry/files/package.json.hbs +0 -18
File without changes
File without changes
@@ -1,81 +0,0 @@
1
- const { yaml } = require('../cds').utils
2
- const { execSync } = require('child_process');
3
- const { bold } = require('../util/term');
4
-
5
- class KubernetesApiAccessor {
6
-
7
- constructor() {
8
-
9
- this.config = once( async() => {
10
- const configString = execSync('kubectl config view --raw').toString();
11
- const config = yaml.parse(configString);
12
-
13
- const currentContextName = config['current-context'];
14
- if (!currentContextName) throw new Error(`No current context in kubectl config.`);
15
- const currentContextEntry = config.contexts && config.contexts.find( entry => entry.name === currentContextName && entry.context && entry.context.cluster && entry.context.user );
16
- if (!currentContextEntry) throw new Error(`Current context not found in kubectl config.`);
17
- const currentContext = currentContextEntry.context;
18
- const clusterEntry = config.clusters && config.clusters.find( entry => entry.name === currentContext.cluster && entry.cluster && entry.cluster.server );
19
- if (!currentContextEntry) throw new Error(`Cluster ${bold(currentContext.cluster)} not found in kubectl config.`);
20
- const { cluster } = clusterEntry;
21
-
22
- const serverToContext = {};
23
- for (const clusterEntry of config.clusters) {
24
- const contextEntries = config.contexts.filter( contextEntry => contextEntry.context.cluster === clusterEntry.name );
25
- // Collect only cluster servers that are used in exactly one context, to clearly identify the context to be used
26
- if (contextEntries.length === 1) {
27
- serverToContext[clusterEntry.cluster.server] = contextEntries[0];
28
- }
29
- }
30
-
31
- return {
32
- namespace: currentContext.namespace || 'default',
33
- context: currentContextEntry,
34
- cluster,
35
- serverToContext
36
- }
37
- });
38
- }
39
-
40
- async context({cluster, namespace}) {
41
- const config = await this.config();
42
- let contextEntry;
43
-
44
- if (cluster && cluster !== config.cluster.server) {
45
- contextEntry = config.serverToContext[cluster] || throwError(`Cluster ${bold(cluster)} not found in kubectl configuration`);
46
- } else {
47
- contextEntry = config.context;
48
- }
49
-
50
- return {
51
- context: contextEntry.name,
52
- cluster: cluster || config.cluster.server,
53
- namespace: namespace || contextEntry.context.namespace || 'default'
54
- };
55
- }
56
-
57
- async get(options) {
58
- const {context, namespace, kind, name} = options;
59
- try {
60
- return JSON.parse(execSync(`kubectl --output json --context ${context} --namespace ${namespace} get ${kind} ${name}`).toString());
61
- }
62
- catch (error) {
63
- if (error.status === 1) return null
64
- else throw error
65
- }
66
- }
67
- }
68
-
69
- function once(func) {
70
- let result;
71
- return function(...args) {
72
- if (!result) result = func(...args);
73
- return result;
74
- }
75
- }
76
-
77
- function throwError(msg = 'Internal Error') {
78
- throw new Error(msg);
79
- }
80
-
81
- module.exports = KubernetesApiAccessor;
@@ -1,89 +0,0 @@
1
- const bindingResolver = require('./bindingResolver')
2
- const { logger } = require('./bindingUtil')
3
- const cds = require('../cds')
4
- const DEBUG = cds.log('cli')._debug // REVISIT: This loads cds.env too early!
5
-
6
- class BindingManager {
7
- constructor(options = {}) {
8
- this.cache = {}
9
- this.bindingsStr = ''
10
- this.options = options
11
- logger.log = DEBUG || !options.silent ? console.log : () => { }
12
- }
13
-
14
- getBindings() {
15
- const bindings = bindingResolver.getUnresolvedBindings(this.options)
16
- return bindings
17
- }
18
-
19
- async resolve(bindings) {
20
- if (!bindings) {
21
- bindings = this.getBindings()
22
- this.bindingsStr = JSON.stringify(bindings)
23
- }
24
-
25
- return await bindingResolver.resolveBindings({ ...this.options, bindings, cache: this.cache })
26
- }
27
-
28
- async bindingEnv(bindings) {
29
- if (!bindings) bindings = await this.resolve()
30
- if (bindings) {
31
- const VCAP_SERVICES = JSON.stringify(BindingManager.toVcapServices(bindings))
32
- return { VCAP_SERVICES }
33
- } else {
34
- return undefined
35
- }
36
- }
37
-
38
- async update({ onBeforeUpdate, onAfterUpdate } = {}) {
39
- const oldBindingsStr = this.bindingsStr
40
- const bindings = this.getBindings()
41
- const bindingsStr = JSON.stringify(bindings)
42
- this.bindingsStr = bindingsStr
43
-
44
- if (this.bindingsStr === oldBindingsStr) return // No change
45
- if (onBeforeUpdate) await onBeforeUpdate()
46
-
47
- try {
48
- if (this.pendingUpdate) await this.pendingUpdate
49
- } catch (error) {
50
- // ignore
51
- }
52
- this.pendingUpdate = undefined
53
-
54
- if (this.bindingsStr !== bindingsStr) return // Next update in progress
55
-
56
- let resolvedBindings
57
- try {
58
- this.pendingUpdate = this.resolve(bindings)
59
- resolvedBindings = await this.pendingUpdate
60
- } catch (error) {
61
- console.error(error.message)
62
- return
63
- }
64
-
65
- if (this.bindingsStr !== bindingsStr) return // Next update in progress
66
-
67
- const env = Object.assign({ VCAP_SERVICES: '{}' }, await this.bindingEnv(resolvedBindings));
68
- if (onAfterUpdate) onAfterUpdate({ bindings: resolvedBindings, env })
69
- }
70
-
71
- static toVcapServices(bindings) {
72
- const services = {}
73
-
74
- for (const name in bindings) {
75
- const binding = bindings[name]
76
- const vcap = binding.binding.vcap || {}
77
- const type = vcap.type || vcap.label
78
- const serviceEntries = services[type] = services[type] || []
79
-
80
- // setting the 'name' property is important to make CAP match the CDS service to the VCAP service
81
- // it is also added to the 'vcap' property by 'cds bind'
82
- serviceEntries.push({ ...vcap, name, credentials: binding.credentials })
83
- }
84
-
85
- return services
86
- }
87
- }
88
-
89
- module.exports = BindingManager
@@ -1,54 +0,0 @@
1
- const cds = require('../cds');
2
- const path = require('path');
3
- const { platformText, bindingText, logger } = require('./bindingUtil');
4
- const { bold } = require('../util/term');
5
-
6
- class BindingResolver {
7
- getUnresolvedBindings({ cwd = process.cwd(), env } = {}) {
8
- if (!env) {
9
- env = cds.env.for('cds', path.resolve(cwd)) // REVISIT: This might load cds.env too early!
10
- }
11
- const bindings = {}
12
- const requires = env.requires || {}
13
- for (const name in requires) {
14
- const service = requires[name]
15
- if (service && service.binding && !service.binding.resolved) {
16
- bindings[name] = { ...service.binding, kind: service.kind }
17
- }
18
- }
19
- return bindings
20
- }
21
-
22
- async resolveBindings({ bindings, cwd = process.cwd(), env, cache = {} }) {
23
- bindings = bindings || this.getUnresolvedBindings({ cwd, env })
24
- if (Object.keys(bindings).length === 0) return undefined
25
- logger.log(`resolving cloud service bindings...`)
26
-
27
- const resolvedBindings = {}
28
-
29
- // run in parallel
30
- await Promise.all(Object.keys(bindings).map(async name => {
31
- const binding = bindings[name]
32
- const cacheKey = JSON.stringify(binding)
33
- let resolvedBinding
34
- if (!cache[cacheKey]) {
35
- resolvedBinding = cache[cacheKey] = await this.resolveBinding(name, binding)
36
- logger.log(`bound ${bold(name)} to ${platformText(binding)} ${bindingText(binding)}`)
37
- } else {
38
- resolvedBinding = cache[cacheKey]
39
- }
40
- resolvedBindings[name] = resolvedBinding
41
- }));
42
- return Object.keys(resolvedBindings).length > 0 ? resolvedBindings : undefined
43
- }
44
-
45
- resolveBinding(name, binding) {
46
- switch (binding.type) {
47
- case 'cf': return require('./cfKeyProvider').resolve(name, binding)
48
- case 'k8s': return require('./kubernetesCredentialsProvider').resolve(name, binding)
49
- default: throw new Error(`Unsupported binding type ${bold(binding.type)} for service ${bold(name)}.`)
50
- }
51
- }
52
- }
53
-
54
- module.exports = new BindingResolver()
@@ -1,129 +0,0 @@
1
- const { bold } = require('../util/term');
2
-
3
- const KindToRequiresNameMap = {
4
- xsuaa: 'auth', // REVISIT: remove with ^8
5
- 'xsuaa-auth': 'auth',
6
- 'ias-auth': 'auth',
7
- 'hana-cloud': 'db',
8
- hana: 'db',
9
- 'hana-mt': 'db', // For compatibility only
10
- 'sql-mt': 'db', // For compatibility only
11
- destinations: 'destinations',
12
- connectivity: 'connectivity',
13
- 'audit-log': 'auditlog',
14
- 'audit-log-to-restv2': 'auditlog',
15
- 'enterprise-messaging': 'messaging',
16
- 'enterprise-messaging-amqp': 'messaging',
17
- 'enterprise-messaging-http': 'messaging',
18
- 'enterprise-messaging-shared': 'messaging',
19
- 'redis-messaging': 'messaging',
20
- 'multitenancy': 'multitenancy'
21
- }
22
-
23
- const PreferredKinds = {
24
- multitenancy: 1, 'hana-cloud': 1, xsuaa: 1, 'xsuaa-auth': 1, 'ias-auth': 1, 'saas-registry': 1
25
- };
26
-
27
- const PLATFORM_TYPES = {
28
- 'cf': 'Cloud Foundry',
29
- 'k8s': 'Kubernetes'
30
- }
31
-
32
- class BindingUtil {
33
- static getRequiresNameFromKind(service, kind) {
34
- const requiresName = KindToRequiresNameMap[kind];
35
- if (!requiresName) throw new Error(`Unknown CDS service name for service ${bold(service)}. Please specify as argument for ${bold('cds bind')}.`)
36
- return requiresName;
37
- }
38
-
39
- /**
40
- * Determine kind value for services based on VCAP binding
41
- * information in the configuration.
42
- */
43
- static determineKinds(services) {
44
- const vcapServices = {}
45
-
46
- // Build VCAP_SERVICES
47
- for (const service of services) {
48
- service.kindCandidates = []
49
-
50
- const { vcap = {} } = service.binding || {};
51
- let { label, type } = vcap;
52
- type = type || label;
53
- if (!type) continue;
54
-
55
- if (!vcapServices[type]) vcapServices[type] = [];
56
- vcapServices[type].push({ credentials: { dummy: 'dummy' }, ...vcap, service });
57
- }
58
-
59
- // Auto configure services based on VCAP_SERVICES
60
-
61
- const cds = require('../cds');
62
- const env = cds.env.for('cds', process.cwd()); // REVISIT: This might load cds.env too early!
63
-
64
- if (!env._find_credentials_for_required_service) {
65
- throw new Error(`Please provide a service kind or update @sap/cds version to use the kind detection.`);
66
- }
67
-
68
- const requires = env.requires.kinds || {};
69
- for (const [kind, service] of Object.entries(requires)) {
70
- const vcapService = env._find_credentials_for_required_service(kind, service, vcapServices);
71
- if (!vcapService) continue;
72
- const isUniqueKind = !Object.values(requires).some(otherService =>
73
- otherService !== service && otherService.kind === kind
74
- )
75
- if (isUniqueKind) vcapService.service.kindCandidates.push(kind);
76
- }
77
-
78
- // Check if one "kind" per service was found
79
- for (const service of services) {
80
- const preferredKind = service.kindCandidates.find(kind => kind in PreferredKinds);
81
- if (preferredKind) {
82
- service.kind = preferredKind;
83
- } else if (service.kindCandidates.length === 1) {
84
- service.kind = service.kindCandidates[0];
85
- }
86
- delete service.kindCandidates;
87
- }
88
- }
89
-
90
- static async checkIsJavaProject(projectPath) {
91
- const { constants, promises: fsp } = require('fs');
92
- const path = require('path');
93
- const pomXmlPath = path.join(projectPath, 'pom.xml');
94
- try {
95
- await fsp.access(pomXmlPath, constants.F_OK);
96
- return true;
97
- } catch (err) {
98
- return false;
99
- }
100
- }
101
-
102
- static platformText(binding) {
103
- if (!PLATFORM_TYPES[binding.type]) throw new Error(`Unexpected binding type ${bold(binding.type)}.`);
104
- return PLATFORM_TYPES[binding.type]
105
- }
106
-
107
- static bindingText(binding) {
108
- switch (binding.type) {
109
- case 'cf':
110
- if (binding.key) {
111
- return `managed service ${bold(binding.instance + ':' + binding.key)}`
112
- } else {
113
- return `user provided service ${bold(binding.instance)}`
114
- }
115
- case 'k8s':
116
- if (binding.name) {
117
- return `service binding ${bold(binding.name)}`
118
- } else {
119
- return `secret ${bold(binding.secret)}`
120
- }
121
- default:
122
- throw new Error('Internal Error');
123
- }
124
- }
125
-
126
- static logger = { log: console.log }
127
- }
128
-
129
- module.exports = BindingUtil
package/lib/bind/index.js DELETED
@@ -1,127 +0,0 @@
1
- const cds = require('../cds');
2
-
3
- const BindingResolver = require('./bindingResolver');
4
- const { determineKinds, getRequiresNameFromKind, checkIsJavaProject, platformText, bindingText, logger } = require('./bindingUtil');
5
- const { bold } = require('../util/term');
6
-
7
-
8
- module.exports = { bind, storeBindings, parseProfile, mergeCredentials }
9
-
10
- /**
11
- * Executes cds build in the directory defined by cds.root.
12
- *
13
- * @param {object} options - command options as defined by build command.
14
- */
15
- async function bind(options) {
16
- let on;
17
- switch (options.on) {
18
- case 'k8s':
19
- case 'kubernetes':
20
- on = 'k8s';
21
- break;
22
- case undefined:
23
- case null:
24
- case 'cf':
25
- case 'cloudfoundry':
26
- on = 'cf';
27
- break;
28
- default:
29
- throw `Invalid value '${options.on}' for option --on.`;
30
- }
31
-
32
- const onText = { 'cf': 'Cloud Foundry', 'k8s': 'Kubernetes' }[on];
33
- if (!options['to-app-services']) logger.log(`Retrieving data from ${bold(onText)}...`);
34
-
35
- // run in parallel
36
- const targets = options.to.split(/,/g);
37
- const resolvedServices = await Promise.all(targets.map(async target => {
38
- let binding;
39
- if (on === 'k8s') {
40
- binding = { type: on, name: target };
41
- } else if (on === 'cf') {
42
- const [instance, key] = target.split(/:/);
43
- binding = { type: on, instance, key };
44
- } else {
45
- throw new Error('Internal Error');
46
- }
47
- const resolvedService = await BindingResolver.resolveBinding(undefined, binding);
48
- resolvedService._target = target;
49
- delete resolvedService.credentials;
50
- resolvedService.binding.resolved = false;
51
- return resolvedService;
52
- }));
53
-
54
- if (options.kind) {
55
- resolvedServices.forEach(service => service.kind = options.kind);
56
- } else {
57
- determineKinds(resolvedServices);
58
- }
59
-
60
- for (const resolvedService of resolvedServices) {
61
- if (options.serviceArg) {
62
- resolvedService.name = options.serviceArg;
63
- } else if (resolvedService.kind) {
64
- resolvedService.name = getRequiresNameFromKind(resolvedService.binding.instance, resolvedService.kind);
65
- }
66
-
67
- let { kind, name, binding } = resolvedService;
68
- name = name ?? `custom-service:${binding.instance || binding.secret}`
69
- resolvedService.name = resolvedService.name ?? name
70
- const kindText = kind ? ` with kind ${bold(kind)}` : ''
71
- const nameText = bold(name)
72
-
73
- logger.log(`Binding ${nameText} to ${platformText(binding)} ${bindingText(binding)}${kindText}`);
74
- }
75
-
76
- return resolvedServices;
77
- }
78
-
79
- async function storeBindings(resolvedServices, options) {
80
- // prepare for different providers
81
- const userCdsrcJsonStorageProvider = require('./userCdsrcJsonStorageProvider');
82
-
83
- await userCdsrcJsonStorageProvider.storeServices(resolvedServices, {
84
- ...options
85
- });
86
-
87
- const isJavaProject = await checkIsJavaProject(cds.root);
88
- if (!cds.cli.options['to-app-services']) {
89
- logger.log(`\nTo run with cloud bindings use ${bold(runCmd(options.for, isJavaProject))}`);
90
- }
91
- }
92
-
93
- function runCmd(profile, isJavaProject) {
94
- profile = parseProfile(profile);
95
- if (isJavaProject) {
96
- const p = profile === 'hybrid' ? '' : ` --profile ${profile}`;
97
- return `cds bind --exec${p} mvn spring-boot:run`;
98
- } else {
99
- const p = profile === 'development' ? '' : ` --profile ${profile}`;
100
- return `cds watch${p}`;
101
- }
102
- }
103
-
104
- function parseProfile(profile) {
105
- if (!profile) return 'hybrid';
106
- else if (profile === '-') return 'development';
107
- else return profile;
108
- }
109
-
110
- function mergeCredentials(credentials, customCredentials) {
111
- if (!customCredentials) return credentials;
112
-
113
- for (const key in customCredentials) {
114
- if (typeof customCredentials[key] === 'object') {
115
- if (credentials[key] && typeof credentials[key] !== 'object') {
116
- throw `Cannot merge credentials: '${key}' is not an object`;
117
- }
118
- if (!credentials[key]) {
119
- credentials[key] = {};
120
- }
121
- credentials[key] = mergeCredentials(credentials[key], customCredentials[key]);
122
- } else {
123
- credentials[key] = customCredentials[key];
124
- }
125
- }
126
- return credentials;
127
- }
@@ -1,84 +0,0 @@
1
- const KubernetesApiAccessor = require('./KubernetesApiAccessor');
2
- const { mergeCredentials } = require('./index');
3
-
4
- class KubernetesCredentialsProvider {
5
- async init() {
6
- this.api = new KubernetesApiAccessor();
7
- }
8
-
9
- async resolve(name, binding) {
10
- if (!this.initPromise) {
11
- this.initPromise = this.init();
12
- }
13
- await this.initPromise;
14
-
15
-
16
- const { context, namespace, cluster } = await this.api.context({ cluster: binding.cluster, namespace: binding.namespace });
17
- let { name: bindingName, secret, credentials: customCredentials } = binding;
18
-
19
- let instance;
20
- let secretOrBindingResource = 'Secret';
21
-
22
- if (bindingName) {
23
- const bindingResource = await this.api.get({ context, namespace, kind: 'servicebindings', name: bindingName });
24
- if (bindingResource === null) {
25
- // Fallback to secret instead of service binding;
26
- secret = bindingName;
27
- bindingName = undefined;
28
- secretOrBindingResource = 'Service Binding or Secret';
29
- } else {
30
- const { spec } = bindingResource
31
- if (!spec.secretName || !spec.serviceInstanceName) {
32
- throw new Error('Internal error')
33
- }
34
- secret = spec.secretName;
35
- instance = spec.serviceInstanceName;
36
- }
37
- }
38
-
39
- const secretResource = await this.api.get({ context, namespace, kind: 'secrets', name: secret });
40
- if (secretResource === null) throw new Error(`${secretOrBindingResource} '${secret}' not found.`);
41
-
42
- const serviceBinding = this._readBinding(name, secretResource);
43
- const { credentials } = serviceBinding;
44
- const vcap = { ...serviceBinding, credentials: undefined }
45
-
46
- const resolvedBinding = {
47
- binding: {
48
- type: 'k8s',
49
- name: bindingName,
50
- cluster,
51
- instance,
52
- namespace,
53
- secret: secret,
54
- resolved: true,
55
- vcap
56
- },
57
- credentials: mergeCredentials(credentials, customCredentials)
58
- };
59
-
60
- return resolvedBinding;
61
- }
62
-
63
- _readBinding(name, secret) {
64
- const { data } = secret;
65
- const properties = {};
66
- for (const name in data) {
67
- const value = Buffer.from(data[name], 'base64').toString()
68
- properties[name] = value;
69
- }
70
-
71
- let parseBinding
72
- try {
73
- // eslint-disable-next-line
74
- parseBinding = require('@sap/cds/lib/env/serviceBindings').parseBinding;
75
- } catch (error) {
76
- //
77
- }
78
- if (!parseBinding) throw new Error(`Please update your @sap/cds version for Kubernetes support`)
79
-
80
- return parseBinding('/', name, properties);
81
- }
82
- }
83
-
84
- module.exports = new KubernetesCredentialsProvider();
@@ -1,92 +0,0 @@
1
- const os = require('os');
2
- const path = require('path');
3
-
4
- const fs = require('fs').promises;
5
- const { bold } = require('../util/term');
6
- const { logger } = require('./bindingUtil');
7
-
8
- const gitUtil = require('../util/git');
9
-
10
- const CDSRC_FILENAME = '.cdsrc-private.json';
11
-
12
- class UserCdsrcJsonStorageProvider {
13
-
14
- async storeServices(services, options = {}) {
15
- let jsonPath = options.out || CDSRC_FILENAME;
16
-
17
- if (jsonPath === CDSRC_FILENAME) {
18
- await gitUtil.ensureFileIsGitignored(CDSRC_FILENAME);
19
- }
20
-
21
- jsonPath = jsonPath.replace(/~/g, os.homedir());
22
- if (path.extname(jsonPath) !== '.json') {
23
- jsonPath = path.join(jsonPath, '.cdsrc.json');
24
- }
25
-
26
- const jsonObject = await this.readJsonSafely(jsonPath);
27
- let cdsRoot;
28
- if (path.basename(jsonPath) === 'package.json') {
29
- jsonObject.cds = jsonObject.cds || {};
30
- cdsRoot = jsonObject.cds;
31
- } else {
32
- cdsRoot = jsonObject;
33
- }
34
-
35
- const profileKey = `[${options.for}]`;
36
- const features = cdsRoot.features && cdsRoot.features[profileKey];
37
- if (features) delete features.emulate_vcap_services;
38
-
39
- cdsRoot.requires = cdsRoot.requires || {};
40
- let section;
41
- section = cdsRoot.requires[profileKey] = cdsRoot.requires[profileKey] || {};
42
-
43
- for (const service of services) {
44
- const { name, kind, binding, credentials } = service;
45
- const requireService = section[name] = section[name] || {};
46
- // store optional custom credentials
47
- binding.credentials = this._getServiceCredentials(binding, services, options);
48
-
49
- Object.assign(requireService, { binding: { ...binding, vcap: undefined }, credentials, kind, vcap: { name } });
50
- }
51
-
52
- await this.writeJsonFile(jsonPath, jsonObject);
53
- logger.log(`Saving bindings to ${bold(jsonPath)} in profile ${bold(options.for)}`);
54
- }
55
-
56
- async readJsonSafely(filePath) {
57
- try {
58
- return JSON.parse(await fs.readFile(filePath));
59
- } catch (err) {
60
- return {};
61
- }
62
- }
63
-
64
- async writeJsonFile(filePath, content) {
65
- await fs.mkdir(path.dirname(filePath), { recursive: true })
66
- await fs.writeFile(filePath, JSON.stringify(content, null, 2));
67
- await fs.chmod(filePath, 0o600); // -rw-------
68
- }
69
-
70
- _getServiceCredentials(binding, services, options) {
71
- const { to, 'to-app-services': app, credentials } = options;
72
- if (!credentials) {
73
- return
74
- }
75
- // toService has format <service-name>[:<key>]
76
- const toServices = to ? to.split(',') : services.map(s => s.binding.instance);
77
- const toService = toServices.find(toService => binding.type === 'cf' ?
78
- toService === binding.instance || toService === binding.instance + ':' + binding.key :
79
- toService === binding.secret);
80
- if (!toService) {
81
- // must never happen
82
- throw new Error(`Internal error: Service ${binding.instance}:${binding.key} not found`);
83
- }
84
- // app based binding always requires service name as key in credentials
85
- if (!app && toServices.length === 1) {
86
- return credentials[toService] || credentials;
87
- }
88
- return credentials[toService];
89
- }
90
- }
91
-
92
- module.exports = new UserCdsrcJsonStorageProvider();
@@ -1,7 +0,0 @@
1
- {
2
- "cds": {
3
- "requires": {
4
- "destinations": true
5
- }
6
- }
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "cds": {
3
- "requires": {
4
- "html5-repo": true
5
- }
6
- }
7
- }