Skip to Content
Technical Articles

Deploy a Function Written in Web IDE Full-stack to SAP CP Functions

Objective

In this blog, I’ll show you two things:

  1. How to connect your instance of Web IDE Full-stack (running in Neo) to an instance of the new Functions Beta service (aka “Functions as a Service” or FaaS) running in Cloud Foundry
  2. How to write a simple function in Web IDE Full-stack and then deploy it to your instance of FaaS running SAP CP Cloud Foundry

Essentially, what we need to do here is to connect a service running in Neo (Web IDE Full-stack) with a service running in Cloud Foundry (your FaaS instance).

The instance of FaaS running in Cloud Foundry is accessed through a tool known as the Functions Dashboard

Assumptions

This blog makes the following assumptions:

  1. You have administrative access to an SAP Cloud Platform Enterprise account, not a Trial account
  2. Within your Enterprise account, there are at least two subaccounts:
    • A Neo Subaccount in which you have access to Web IDE Full-Stack
    • A Cloud Foundry subaccount that has been created with the “Enable beta features” flag switched on.  If this is not the case, a new subaccount must be created because the “Enable beta features” flag cannot be set retrospectively.
  3. The “Functions” service has been added as an entitlement to your CF subaccount

As per Liat Borenshtein‘s comment below, this scenario cannot yet be implemented using a SAP CP Trial account.

Configuration Steps

1) In Your Cloud Foundry Subaccount, Create a FaaS Instance

  1. In the SAP Cloud Platform Cockpit, connect to your Cloud Foundry (CF) space and from the side menu on the left, select Service Marketplace
  2. Select the “Functions” tile

    If the “Functions” tile does not appear, then you are using a subaccount in which the “Enable beta features” flag has not been enabled.  Unfortunately, no further progress is possible until you have access to a correctly configured subaccount
  3. From the side menu on the left, select “Instances”, then check that a Functions instance exists.
  4. If no (suitable) instance exists, then create a new one.In the “Create Instance” pop-up, unless you are creating a highly customised instance, you do not need to enter any values on the first three screens. Repeatedly press “Next” until you reach the fourth screen on which you are asked for the instance name. Enter some suitable name and press “Finish”
  5. Select the Functions service instance you have just created and from the side menu on the left, select “Service Keys”
  6. If no (suitable) service key exists, create a new one.

So Why Do I Need a Service Key?

The service key contains specific information about the service instance you have just created.

Here’s a formatted (and redacted) example:

{
  "vendor": "SAP"
, "url"   : "https://functions-service.cfapps.eu10.hana.ondemand.com"
, "uaa": {
    "uaadomain"      : "authentication.eu10.hana.ondemand.com"
  , "tenantmode"     : "dedicated"
  , "sburl"          : "https://internal-xsuaa.authentication.eu10.hana.ondemand.com"
  , "clientid"       : "sb-75cae42f-a2fe-443b-914e-a813051a4213!b8187|faas-broker!b2495"
  , "verificationkey": "-----BEGIN PUBLIC KEY----- <snip> -----END PUBLIC KEY-----"
  , "xsappname"      : "75cae42f-a2fe-443b-914e-a813051a4213!b8187|faas-broker!b2495"
  , "identityzone"   : "<subaccount_name>"
  , "identityzoneid" : "10673374-a27d-4135-c81d-8d55be7bd62a"
  , "clientsecret"   : "Y8PIUAmUm+sRQWqeUeQzQPGPIdf="
  , "tenantid"       : "10673374-a27d-4135-c81d-8d55be7bd62a"
  , "url"            : "https://dbs-dev.authentication.eu10.hana.ondemand.com"
  }
}

Here is where we will find the important values needed to connect Web IDE (running in Neo) to your instance of the Functions Dashboard you have just created here in Cloud Foundry.

When setting up the Neo destination, you will need to reference the following Service Key fields:

Property Description
url

The URL of the service instance you have just created. This is known as the protected resource.

In our specific case, this is the URL for the Functions Dashboard running in the EU10 data centre.

In order to access this protected resource, the user must know this URL together with the values of the following three fields.

uaa.clientid The unique ID of the service broker for this particular service instance.
The value in this field has been populated for you, and starts with sb- (for service broker), followed by the value of the Service Key field uaa.xsappname
uaa.clientsecret The password required for access to the protected resource
uaa.url The URL of the Cloud Foundry authentication server that will generate the required OAuth token.

Notice that the uaa.url field contains the URL of the authentication server within your own Cloud Foundry sub account. Using the Neo destination we are about to create, Web IDE will make a request to this authentication server to generate an OAuth token that will then allow it to publish your coding to the Functions Dashboard.

2) In Your Neo Subaccount, Create a Destination

  1. From the SAP Cloud Platform Cockpit, connect to your Neo sub account
  2. From the side menu on the right, select Connectivity -> Destinations
  3. Create a new destination. We now need to enter some of the Service Key values created above.
  4. The fields of the new destination contain the following information
    Field Description
    Name Give the destination some meaningful name
    Type What sort of connection are we going to use to communicate with the protected resource?
    Description Some meaningful description of why this destination exists. In our case, this destination allows Web IDE to gain access to FaaS
    URL The URL of our protected resource.
    This value is always found in the Service Key field url
    Proxy Type Where is our protected resource located? On an on-premise server (accessed via the SAP Cloud Connector) or on a server accessible via the public internet?
    Authentication

    Here we’re going to use OAuth2ClientCredentials.

    This describes the fact that authentication between Neo and CF will take place using an OAuth token generated by the server referenced in the “Token Server URL” field below

    Client ID The unique Id of the service broker managing our service instance.
    This value is found in the Service Key field uaa.clientid
    Client Secret This value is found in the Service Key field uaa.clientsecret and behaves as a password
    Token Service URL

    The URL of the server that will generate the required OAuth token. This value is found in the Service Key field uaa.url and needs /oauth/token appended to it.

    From the perspective of authentication, this is where the connection is made between a service running in the Neo environment (Web IDE) and a service running in the Cloud Foundry environment (the Functions Dashboard)

    Token Service User Leave blank
    Token Service Password Leave blank

    The Token Service User and Password fields are left blank because in this case, the Token Service server itself does not require a user id and password before it will generate an OAuth token; all it needs is a valid clientid and clientsecret.

    Using the values from our example Service Key shown above, the destination will now be configured like this:

    Field Value
    Name WebIDE_FaaS_Usage
    Type HTTP
    Description Connect Web IDE to CF Functions Dashboard
    URL https://functions-service.cfapps.eu10.hana.ondemand.com
    Proxy Type Internet
    Authentication OAuth2ClientCredentials
    Client ID sb-72cee12c-a8fd-463a-904d-a6180a124f13!b8718|faas-broker!b5249
    Client Secret Y8PIUAmUm+sRQWqeUeQzQPGPIdf=
    Token Service URL https://dbs-dev.authentication.eu10.hana.ondemand.com/oauth/token
    Token Service User
    Token Service Password

     

  5. Two additional properties must also be added to the destination.Without the first of these additional properties, this destination will always remain invisible to Web IDE, and without the second, this destination will not be recognised as pointing to a FaaS deployment target.
    Property Name Description
    WebIDEEnabled true

    At start up, Web IDE is passed a JSON object called listDestinationscontaining all the Neo destinations for which this flag has been set to true.

    All destinations in which this flag is either missing, or set to false will remain invisible to Web IDE

    WebIDEUsage faas_api

    The value of this field indicates the type of service to which Web IDE has access.

    In this case, Web IDE will now have access to the Functions Dashboard referenced by the URL field of this destination

     

  6. Once you have entered these values, press Save.
  7. If you now press “Check connection”, you will see the somewhat unexpected response of HTTP 404 Not Found

    Surprisingly, this is actually the correct response…

We have now created a FaaS instance in Cloud Foundry with a service key, and then used the information found in the service key to define a destination in Neo.

The Neo destination will then be used by Web IDE Full-stack (also running in Neo) to deploy functions to the Functions Dashboard instance running in Cloud Foundry.

Testing

In order to test the connection between Neo and CF, we will create a simple function and trigger in Web IDE Full-Stack, and then attempt to deploy them to our instance of the Functions Dashboard.

  1. Start SAP Web IDE Full-Stack from your Neo account.
  2. As mentioned at the end of configuration step 2.5, at startup time, Web IDE receives a JSON object called listDestinations that contains the details of all Neo destinations that have the WebIDEEnabled property set to true.Therefore, our instance of Web IDE will now know about our new WebIDE_FaaS_Usage destination
  3. In Web IDE, click on Preferences (the gear wheel icon on the side menu to the left) and select Features.
  4. Ensure that the SAP Cloud Platform Functions (Beta) feature is switched on.If this feature is not switched on, do so, then save your changes and restart Web IDE.
  5. In your Web IDE workspace, select File -> New -> Project from Template
  6. Change the Category drop down box to SAP Cloud Platform Functions (Beta) and press Next. What follows is a sequence of wizard screens on which you will be asked to enter various names. Use the following names for this test function:
    Property Value
    Project name test-fn-project
    Function name fn-http
    Trigger name http
  7. Agree to all the scary Terms and Conditions and press Finish
  8. Expand the folders test-fn-project -> functions -> fn-http and edit file index.js.This file is a basic NPM module and for the sake of simplicity, will export only one function.
  9. Replace the entire file contents with the following code:
    var utils = require('basic-formatting-utils')
    
    // Call the appropriate datetime_xxx function for your timezone:
    // datetime_pst     US Pacific Time
    // datetime_est     US Eastern Standard Time
    // datetime_gmt     Greenwich Mean Time
    // datetime_cet     Central European Standard Time
    // datetime_ist     India Standard Time
    // 
    // Or supply the numbers of minutes by which your timezone is offset from UTC.
    // E.G. If you are four hours behind UTC, then use:
    //
    // var date = utils.datetime_by_timezone(-240)(new Date())
    
    var date = utils.datetime_gmt(new Date())
    
    var format_response = (evt, ctx) =>
      utils.as_html([],
         utils.as_body([]
         , [ utils.as_h1([], "Function called at: " + date)
           , utils.create_content(
             [ {title: "Context Object", value: ctx}
             , {title: "Event Object",   value: evt}
             , {title: "NodeJS process", value: process}
             ])
           ].join("")
         )
      )
    
    var response_handler = (content, stream) => 
      new Promise((resolve, reject) => {
        stream.on('finish', resolve)
        stream.on('error', reject)
        stream.end(content)
      })
    
    // ******************************************************************************
    // PUBLIC API
    // ******************************************************************************
    /**
     * @param {FaasEvent} event
     * @param {FaasContext} context
     * 
     * @return {Promise|*}
     */
    module.exports = (event, context) => 
      response_handler(
        format_response(event, context)
      , event.getResponseStream('text/html; charset=utf8')
      )​

    Save this file.

  10. Edit package.json and replace the contents of this file with the following JSON object:
    {
      "dependencies": {
        "basic-formatting-utils": "*"
      }
    }​

    Save this file.

  11. The project we have now developed contains two distinct parts:
    • A function – The coding in file functions -> index.js
    • A trigger – The configuration in file triggers -> http.trg.json (this file was created for you by the project template wizard)

    The order in which the parts of our project are deployed is important!

    You cannot deploy a trigger if the function that trigger invokes has not first been deployed. Therefore, we must always deploy the function first, and the trigger second. (There is an option to deploy the entire function project, but at the moment, this option does not always work reliably)

    Right-click on the fn-http folder and select Deploy -> Deploy Function. A pop-up will appear showing all the Neo destinations for which the WebIDEUsage property equals faas_api

    Select the appropriate destination and press “Deploy”

  12. If you have the Web IDE console open, you will see one or more messages informing you that (Function Deployment) Deployment of function fn-http in progress...
  13. After a few seconds, you will see an alert in the top right corner of the Web IDE screen informing you that the function has been deployed.
  14. Next, the trigger must be deployed. Right-click on the trigger name http.trg.json and select Deploy -> Deploy Trigger. The same pop-up will be display again in which you can select the destination.
  15. Press “Deploy” and wait for the deployment process to complete. If you have the Web IDE console open, you will see one or more messages saying (Trigger Deployment) Deployment of trigger http has started
  16. When the deployment process has completed, you will see two things. In the top right of the Web IDE screen, you will see the following alert:
  17. In the Web IDE console, you will also see a message informing you of the trigger’s URL. Something like:(Trigger Deployment) Trigger http deployed with url https://72cee12c-af3d-431a-940d-a6180a124f13.ingress.live.faas-live.shoot.live.k8s-hana.ondemand.com/fn-http/
  18. Copy the trigger’s URL and paste it into the address line of a new browser tab.You should see a screen similar to the following:In the table display of the server-side objects, any object property of type ObjectArray or Map can be expanded in order to view its contents.

So there you have it!

The point here is really to demonstrate how, by means of a Neo destination, a Neo service such as Web IDE Full-stack can communicate with a service running in a seperate cloud environment; in this case, an instance of the Functions Dashboard running in Cloud Foundry.

Merry Christmas everyone!

Chris W

2 Comments
You must be Logged on to comment or reply to a post.