@sap/hdi-dynamic-deploy

2.7.4 • Public • Published

@sap/hdi-dynamic-deploy

@sap/hdi-dynamic-deploy is a Node.js-based http server for dynamic deployment to SAP HANA DI (HDI) containers, HDI Dynamic Deployer for short. The HDI Dynamic Deployer can be used in XS Advanced (XSA) and in SAP Cloud Platform (SAP CP)/Cloud Foundry (CF) to deploy database content to dynamically created containers (e.g. created via the Instance Manager).

The dynamic deployer is built upon @sap/hdi-deploy which should be used directly if a static deployment at deploytime is sufficient.

README.md

Installation:

Dynamic deployment:

Integration into a Database Module

Usually, @sap/hdi-dynamic-deploy gets installed via a package.json-based dependency inside your application's db module:

db/package.json:

{
  "name": "deploy",
  "dependencies": {
    "@sap/hdi-dynamic-deploy": "2.7.4"
  },
  "scripts": {
    "start": "node node_modules/@sap/hdi-dynamic-deploy/"
  }
}

Configuration of the dynamic deployer

The dynamic deployer needs to be configured via the following environment variables:

  • PORT: port the HTTP server listens to
  • hdi_dynamic_deploy_user: username for HTTP basic authentication
  • hdi_dynamic_deploy_password: password for HTTP basic authentication
  • ENFORCE_AUDITING: force usage of audit logging. If audit logging cannot be enabled, the server will throw an error and stop.
  • ENFORCE_V2: force usage of the V2 audit logging API. If audit logging V2 cannot be enabled, the server will throw an error and stop.
  • STRUCTUREDLOGGING: allows dynamic deployer logs to be in structured format when set to true.
  • AUDIT_LOG_TENANT: specifies the tenant to use for audit logging. Likely this will be the subaccount-id where your app is deployed. If this is not specified you may be unable to view the logs.

Note: Any client that knows the hdi_dynamic_deploy_user and the corresponding password will indirectly be able to read the database artifacts contained in the dynamic deploy server.

If an auditlog service is bound to the dynamic deployer, invalid authentication attempts will be logger accordingly.

The PORT variable is automatically set by XSA. ENFORCE_AUDITING, ENFORCE_V2, username and password have to be given e.g. via the mta.yaml file (see example below). When using the XSA deploy-service, a strong generated password will be used. In other use cases, sufficient password strength has to be ensured!

modules:
  - name: db
    type: com.sap.xs.hdi-dynamic
    path: db
    properties:
      hdi_dynamic_deploy_user: ${generated-user}
      hdi_dynamic_deploy_password: ${generated-password}
    provides:
    - name: db_deployment
      properties:
         url: ${default-url}
         user: ${generated-user}
         password: ${generated-password}

Triggering a dynamic deployment by a HTTP POST request

The dynamic deployer is a http server started for a specific db module. When the module is pushed to XSA or CF, the dynamic deployer starts listening for requests and eventually starts the (non-dynamic) deployer to deploy the content of the db module to a given container.

To trigger the deployment one has to send a HTTP POST request with basic authentication and content type application/json to the dynamic deployer. The api offers three urls for deployment, http(s)://<hostname>:<port>/v1/deploy, http(s)://<hostname>:<port>/v1/deploy/to/instance and http(s)://<hostname>:<port>/v1/deploy/to/instance/async.

Synchronous deployment

Deployment via http(s)://<hostname>:<port>/v1/deploy (VCAP_SERVICES style)

The first way to trigger a deployment is to send a HTTP POST request to the url http(s)://<hostname>:<port>/v1/deploy. The body simply consists of a JSON object containing replacements for several of the HDI deployer's environment variables. Supported are replacements for:

  • HDI_DEPLOY_OPTIONS
  • DEPLOY_ID
  • TARGET_CONTAINER
  • SERVICE_REPLACEMENTS
  • VCAP_SERVICES

In addition to providing VCAP_SERVICES for replacing the corresponding environment variable it is also possible to provide ADDITIONAL_VCAP_SERVICES. The deployer is then called with service bindings created from the VCAP_SERVICES of the dynamic deployer by adding the service definitions given by ADDITIONAL_VCAP_SERVICES. The ADDITIONAL_VCAP_SERVICES object has the same structure as the original VCAP_SERVICES, i.e. it contains lists of service bindings. If the request contains ADDITIONAL_VCAP_SERVICES, the server scans through all of its services and either adds the list of bindings to the VCAP_SERVICES environment variable or merges the two lists in case bindings for the given service already exist. Existing bindings with the same name are replaced with the bindings from ADDITIONAL_VCAP_SERVICES.

Example:

{
    "TARGET_CONTAINER": "hdi_container_service_name",
    "ADDITIONAL_VCAP_SERVICES": {
        "hana" : [ {
            "name" : "hdi_container_service_name",
            "label" : "hana",
            "tags" : [ "hana", "database", "relational" ],
            "plan" : "hdi-shared",
            "credentials" : {
                "schema" : "DB_EXAMPLE",
                "hdi_password" : "hdi_password",
                "password" : "password",
                "driver" : "com.sap.db.jdbc.Driver",
                "port" : "30015",
                "host" : "srv1234567.host.name",
                "db_hosts" : [ {
                 "port" : 30015,
                 "host" : "srv7654321.host.name"
                } ],
                "hdi_user" : "hdi_user",
                "user" : "user",
                "url" : "jdbc:sap://srv1234567.host.name:30015/?currentschema=DB_EXAMPLE"
            }
        } ]
    }
}

Deployment via http(s)://<hostname>:<port>/v1/deploy/to/instance (Instance Manager style)

Since version 1.2.0 of the dynamic deployer there is a second way to trigger a deployment by sending a HTTP POST request to the url http(s)://<hostname>:<port>/v1/deploy/to/instance. The request body is simply a managed service instance as retrieved from the Instance Manager with a HTTP GET.

Since version 2.3.2 of the dynamic deployer the request body can be a managed service instance as retrieved from the Instance Manager or Service Manager with a HTTP GET.

Example: Instance Manager

{
  "tenant_id": "1",
  "id": "da7ff475-fd3f-4a86-a3e7-cd3e41e3653d",
  "binding_id": "3bb96cab-0bec-4088-9991-244b750e53b3",
  "instance_id": "d9cc0aef-16f7-40d2-8e10-1816b9214f2e",
  "managed_service_id": "79d9e11a-95c2-4771-ae2d-8ba703bd8fda",
  "managed_plan_id": "bebaad3e-352b-4928-bc6b-8783d754ac3b",
  "managed_instance_id": "0a4d365a-eec5-4083-85ae-677f77bb6f5d",
  "managed_binding_id": "a5cba4c8-95a3-42c8-982d-e075d0a6b941",
  "status": "CREATION_SUCCEEDED",
  "updated_on": 1494322225942,
  "credentials": {
    "host": "srv1234567.host.name",
    "port": "30015",
    "driver": "com.sap.db.jdbc.Driver",
    "url": "jdbc:sap://srv1234567.host.name:30015/?currentschema=55D392C7232649E8A2F08993645B28B5",
    "schema": "55D392C7232649E8A2F08993645B28B5",
    "hdi_user": "SBSS_78283957013891283645150604040575244555286482237534978212872169092",
    "hdi_password": "password",
    "user": "SBSS_92380540949443696814788249184554628165227387555319796659663474608",
    "password": "password"
  }
}

Example: Service Manager

{
  "id": "da7ff475-fd3f-4a86-a3e7-cd3e41e3653d",
  "ready": true,
  "service_instance_id": "790e19ae-b9f7-4d80-8e4d-d368d96f79bd",
  "last_operation": {
        "id": "e9061cfe-b70a-405b-a402-084b3ec74711",
        "ready": true,
        "type": "create",
        "state": "succeeded",
        "resource_id": "bf24e49c-fd62-4ad4-83fc-6c09132f8cf7",
        "resource_type": "/v1/service_bindings",
        "platform_id": "service-manager",
        "correlation_id": "ec849486-34b3-40ac-71e6-baf91c91cd0b",
        "reschedule": false,
        "reschedule_timestamp": "0001-01-01T00:00:00Z",
        "deletion_scheduled": "0001-01-01T00:00:00Z",
        "created_at": "2021-11-02T09:22:47.667677Z",
        "updated_at": "2021-11-02T09:22:49.288825Z"
    },
  "name": "91ce8c49-a1c8-4557-8b25-0e2c4034a4cf",
  "credentials": {
    "host": "srv1234567.host.name",
    "port": "30015",
    "driver": "com.sap.db.jdbc.Driver",
    "url": "jdbc:sap://srv1234567.host.name:30015/?currentschema=55D392C7232649E8A2F08993645B28B5",
    "schema": "55D392C7232649E8A2F08993645B28B5",
    "hdi_user": "SBSS_78283957013891283645150604040575244555286482237534978212872169092",
    "hdi_password": "password",
    "user": "SBSS_92380540949443696814788249184554628165227387555319796659663474608",
    "password": "password"
  }
}

The response from the dynamic deployer

If there was no problem with the basic authentication and the request reaches the dynamic deployer, it usually responds with status code 200 and a json body containing the result of the deployment. The response body has the following form:

{
  messages: [<list of result messages from the di server>],
  exitCode: <exit code of the call to the deployer app>
}

IMPORTANT: A status code of 200 does not mean that the deployment was successful. It just means that the dynamic deployer was able to call the (non-dynamic) deployer. If the deployer finished with no errors the exitCode attribute of the response is 0, otherwise it is 1. More detailed information about the deployment can be retrieved from the messages attribute of the response.

Asynchronous deployment

Since version 1.7.0 of the dynamic deployer there is a third way to trigger a deployment by sending a HTTP POST request to the url http(s)://<hostname>:<port>/v1/deploy/to/instance/async. The request body is simply a managed service instance as retrieved from the Instance Manager with a HTTP GET, i.e. the same as for http(s)://<hostname>:<port>/v1/deploy/to/instance. But instead of waiting until the deployment is done and then returning the results, a GUID is returned.

Since version 2.3.2 of the dynamic deployer the request body can be a managed service instance as retrieved from the Instance Manager or Service Manager with a HTTP GET.

This GUID can be used to query the status of the deployment by sending a GET request to http(s)://<hostname>:<port>/v1/status/:guid - if the deployment is still running, the response just contains a status property. If the deployment is finished, the usual response is returned - in conjunction with the status property.

Since version 2.5.0 of the dynamic deployer there is a forth way to trigger a deployment by sending a HTTP POST request to the url http(s)://<hostname>:<port>/v1/deploy/async. The request body is same as for http(s)://<hostname>:<port>/v1/deploy/. But instead of waiting until the deployment is done and then returning the results, a GUID is returned.

How to use it in a multi-target application

A multi-target application (MTA) for multi-tenancy scenarios with the instance manager typically includes multiple db modules:

  • N static db modules (type: com.sap.xs.hdi), e.g. for configuration or shared data. A static module depends on @sap/hdi-deploy; it does not depend on @sap/hdi-dynamic-deploy.
  • M dynamic db modules (type: com.sap.xs.hdi-dynamic) where the business data for a certain type of tenant is contained. A dynamic module depends on @sap/hdi-dynamic-deploy, which internally depends on @sap/hdi-deploy for deployment to the correct tenant.

Example:

modules:
  - name: db-static-1
    type: com.sap.xs.hdi
    path: db-static-1
    
  - name: db-static-2
    type: com.sap.xs.hdi
    path: db-static-2
    
  - name: db-dynamic-1
    type: com.sap.xs.hdi-dynamic
    path: db-dynamic-1
    
  - name: db-dynamic-2
    type: com.sap.xs.hdi-dynamic
    path: db-dynamic-2
    
  - name: db-dynamic-3
    type: com.sap.xs.hdi-dynamic
    path: db-dynamic-3

Accessing the underlying HTTP server

By requiring the dynamic deploy package, you can access the internal HTTP server. This way, you can decide when to start/stop the server.

The exported object offers two methods:

    /**
     * Start the HTTP server on this.port.
     *
     * @param {any} cb Callback function (error, result).
     * @returns {undefined}
     */
    function start(cb){
      <..>
    };

    /**
     * Stop the HTTP server.
     *
     * @param {any} cb Callback function (error, result).
     * @returns {undefined}
     */
    function stop(cb){
      <..>
    };

Furthermore, the object has the property port. This has to be set to the port that you want the server to listen on.

Example:

'use strict';

const {server} = require('@sap/hdi-dynamic-deploy/index');

server.port = process.env.PORT;
server.start(function(){
  console.log(`@sap/hdi-dynamic-deploy HTTP server up and running, listening on port ${  server.port}`);
});

Accessing the router functions

By requiring the dynamic deploy package, you can access the functions used for the API endpoints and can use them in your own router. Both functions expect two parameters:

  • A HTTP request object
  • A HTTP response object

Example:

'use strict';

const {deploy_to_instance, deploy} = require('@sap/hdi-dynamic-deploy/index');

/** 
 * Now the functions can be added to a router.
 * 
 * deploy_to_instance is the function used for the /v1/deploy/to/instance route
 * deploy is the function used for the /v1/deploy route
 * 
 */

Readme

Keywords

none

Package Sidebar

Install

npm i @sap/hdi-dynamic-deploy

Weekly Downloads

1,065

Version

2.7.4

License

See LICENSE file

Unpacked Size

122 kB

Total Files

13

Last publish

Collaborators

  • sap_extncrepos