
Destinations are very handy and powerful mechanism to facilitate access to target systems and devices.
When it comes to SAP BTP destinations, the idea is to manage both subaccount and instance level destinations (and/or their certificates) as shared configuration resources on a provider subaccount level.
That way, the destinations configurations can be stored as versioned assets in a source repository and need to be maintained only once per provider, thus, without incurring application runtime tie-in.
Last but not least, BTP destination service is used as a self-configuration tool.
Table of Contents |
PS.
Bootstrap destinations definitions.
Even, if there is no intrinsic BTP CLI command to assist in creation of destinations from service bindings, this can be achieved quite easily with a bit of jq gimmick by applying service binding credentials to a json payload template, for instance:
{
"init_data": {
"subaccount": {
"destinations": [
{
"Description": "dest-httpbin",
"Type": "HTTP",
"clientId": "sb-clone12847c4c89544b4f9234b26ede429f62!b282590|destination-xsappname!b62",
"HTML5.DynamicDestination": "true",
"HTML5.Timeout": "60000",
"Authentication": "OAuth2ClientCredentials",
"Name": "dest-httpbin",
"tokenServiceURL": "https://<subdomain>.authentication.us10.hana.ondemand.com/oauth/token",
"ProxyType": "Internet",
"URL": "https://httpbin.org",
"tokenServiceURLType": "Dedicated",
"clientSecret": "<clientSecret>"
},
{
"Description": "SAP Destination Service APIs",
"Type": "HTTP",
"clientId": "sb-clone12847c4c89544b4f9234b26ede429f62!b282590|destination-xsappname!b62",
"HTML5.DynamicDestination": "true",
"HTML5.Timeout": "60000",
"Authentication": "OAuth2ClientCredentials",
"Name": "destination-service",
"tokenServiceURL": "https://<subdomain>.authentication.us10.hana.ondemand.com/oauth/token",
"ProxyType": "Internet",
"URL": "https://destination-configuration.cfapps.us10.hana.ondemand.com/destination-configuration/v1",
"tokenServiceURLType": "Dedicated",
"clientSecret": "<clientSecret>"
}
],
"certificates": [
],
"existing_certificates_policy": "update",
"existing_destinations_policy": "update"
}
}
}
Alternatively, one could resort to using SAP Cloud SDK built-in service binding destinations.
The below nodejs code snippet demonstrates how to leverage SAP Cloud SDK with its service binding destinations with the likes of service manager and destinations services.
apiVersion: serverless.kyma-project.io/v1alpha2
kind: Function
metadata:
name: {{ .Values.services.srv.name }}
labels:
{{- include "app.labels" . | nindent 4 }}
app: {{ .Values.services.srv.name }}
spec:
runtime: {{ .Values.services.srv.runtime }}
# runtimeImageOverride: {{ .Values.services.srv.runtimeImageOverride }}
source:
inline:
dependencies: |
{
"name": "{{ .Values.services.srv.name }}",
"version": "0.0.1",
"dependencies": {
"axios":"latest"
,"debug": "latest"
,"@sap/xsenv": "latest"
,"@sap-cloud-sdk/http-client": "latest"
,"@sap-cloud-sdk/connectivity": "latest"
,"@sap-cloud-sdk/resilience": "latest"
,"async-retry": "latest"
}
}
source: |
const debug = require('debug')('{{ .Values.services.srv.name }}:function');
const NOT_FOUND = 'Not Found';
const xsenv = require('@sap/xsenv');
const services = xsenv.getServices({
sm: { label: 'service-manager', name: 'saas-sm' }
,
dest: { label: 'destination' }
});
console.log('saas-sm: ', services.sm);
const readServices = xsenv.readServices();
console.log('readServices: ', readServices);
const httpClient = require('@sap-cloud-sdk/http-client');
const cloudSdkConnectivity = require('@sap-cloud-sdk/connectivity');
const { retrieveJwt, decodeJwt, Destination } = require('@sap-cloud-sdk/connectivity');
const { setGlobalLogLevel, createLogger } = require('@sap-cloud-sdk/util');
const { retry } = require ('@sap-cloud-sdk/resilience');
const { resilience } = require ('@sap-cloud-sdk/resilience');
const ResilienceOptions = {
retry: 10,
circuitBreaker: false,
timeout: 300*1000 // 5 minutes in milliseconds
};
const retryme = require('async-retry');
setGlobalLogLevel('debug');
const logger = createLogger('http-logs');
module.exports = {
main: async function (event, context) {
const req = event.extensions.request;
const message = `Hello World`
+ ` from the Kyma Function ${context['function-name']}`
+ ` running on ${context.runtime}!`
+ ` with the request headers ${JSON.stringify(req.headers,0,2)}`;
console.log(message);
if (typeof req.path !== undefined) {
console.log('path: ', JSON.stringify(req.path,0,2))
}
if (typeof req.params !== undefined) {
console.log('params: ', JSON.stringify(req.params,0,2))
}
if (typeof req.url !== undefined) {
console.log('url: ', JSON.stringify(req.url,0,2))
}
if (typeof req.authInfo !== undefined) {
console.log('authInfo: ', JSON.stringify(req.authInfo,0,2))
}
const { pathname } = new URL(req.url || '', `https://${req.headers.host}`)
console.log('pathname: ', pathname)
const url = require("url");
var url_parts = url.parse(req.url);
console.log(url_parts);
console.log(url_parts.pathname);
// returns an array with paths
let path_array = req.url.match('^[^?]*')[0].split('/').slice(1);
console.log(path_array)
console.log(req.url.match('^[^?]*')[0])
if (!path_array?.length) return 'Please use an API verb';
const actions = [
{ name: 'offerings', verb: 'service_offerings', dest: 'saas-sm', url: '/v1/' },
{ name: 'plans', verb: 'service_plans', dest: 'saas-sm', url: '/v1/' },
{ name: 'instances', verb: 'service_instances', dest: 'saas-sm', url: '/v1/' },
{ name: 'bindings', verb: 'service_bindings', dest: 'saas-sm', url: '/v1/' },
{ name: 'instanceDestinations', verb: 'instanceDestinations', dest: 'faas-dest-x509', url: '/destination-configuration/v1/' },
{ name: 'subaccountDestinations', verb: 'subaccountDestinations', dest: 'faas-dest-x509' , url: '/destination-configuration/v1/' }
];
const action = actions.find( ({ name }) => name === path_array[1] )
console.log('action found: ', action)
if (path_array[0] == 'srv' && action !== undefined) {
path_array = req.url.match('^[^?]*')[0].split('/').slice(2);
console.log('path_array: ', path_array)
const queryString = req.query;
console.log('queryString: ', queryString)
const urlParams = new URLSearchParams(queryString);
const params = req.params;
console.log('params: ', params)
try {
// https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations#service-binding-environment-variables
const endpoint = path_array[1] !== undefined ? '/' + path_array[1] : '';
console.log(endpoint)
let res = await httpClient.executeHttpRequest({ destinationName: action.dest }, {
method: 'GET',
url: action.url + action.verb + endpoint
});
return res.data;
} catch (err) {
console.log(err.stack);
return err.message;
}
}
}
}
scaleConfig:
maxReplicas: 5
minReplicas: 3
resourceConfiguration:
function:
profile: S
env: ## https://kyma-project.io/docs/kyma/latest/05-technical-reference/00-configuration-parameters/svls-02-environment-variables/#node-js-runtime-specific-environment-variables
- name: FUNC_TIMEOUT ## Specifies the number of seconds in which a runtime must execute the code.
value: '1800'
- name: REQ_MB_LIMIT ## payload body size limit in megabytes.
value: "10"
- name: DEBUG
value: '{{ .Values.services.srv.name }}:*'
- name: SERVICE_BINDING_ROOT
value: /bindings
secretMounts:
- secretName: {{ .Values.services.sm.bindingSecretName }}
mountPath: "/bindings/saas-sm"
- secretName: {{ .Values.services.dest.bindingSecretNamex509 }}
mountPath: "/bindings/faas-dest-x509"
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
16 | |
12 | |
12 | |
10 | |
8 | |
8 | |
8 | |
8 | |
7 | |
6 |