Monday, 23 September 2019

Jenkins Build Process with SAPUI5 through HANA XSA

In this blog, we will build the NodesJS and SAPUI5 app in SAP HANA XSA that controls the Jenkins build process.

What’s Jenkins ?


Jenkins (jenkins.io) offers a simple way to set up a continuous integration or continuous delivery environment for almost any combination of languages and source code repositories using pipelines, as well as automating other routine development tasks.

Prerequisites


◈ SAP HANA XSA
◈ Jenkins (jenkins.io) Installed on local machine
◈ NodeJS Client for Jenkins (https://www.npmjs.com/package/jenkins)
◈ A simple Python program.

High Level Steps


◈ Configure the Jenkins job to execute the Windows batch command to run the Python program. For simplicity, Windows batch file and Python app reside in the same Jenkins server (this is may not practical in the Production mode).

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Install and configure NodeJS Client for Jenkins in SAP HANA XSA and build the NodeJS app to call the REST API to build, get and stop the Jenkins job.

◈ Lastly, we will build the SAPUI5 Front End in SAP HANA XSA to trigger job, check job status and cancel job.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

Configure Jenkins


◈ Go to Jenkins main dashboard and click on New Item.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Enter item name trigger and select Freestyle project. Click OK to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Under Build, add build step Execute Windows batch command.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Specify the command. Here, I would like to execute run.bat in folder C:\MyPython\.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Click Save to finish.
◈ Go back to main dashboard and you will see the item “trigger” the that we just created.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

SAP HANA XSA


Create Project in SAP Web IDE

◈ Logon to SAP Web IDE for SAP HANA XSA. Create a new project in SAP Web IDE for HANA XSA and select SAP Cloud Platform Business Application. Click Next to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Give the project name, for example zjenkinauto. Click Next to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ On this page, just click Next to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ We need the NodeJS module but we will add it later on and we don’t need the database module. Select Not Included for both Service and Database. Click Next to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Click Finish to complete the setup.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

Adding NodeJS Module


◈ Now on the project that we just created, do a right-click and select New > Node.js Module.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Give a module name srv. Click Next to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ On this page, just click Next to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Click Finish to complete.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ We need to update the code in server.js in srv folder.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

Update with the following code:

/*eslint no-console: 0, no-unused-vars: 0, no-undef:0, no-process-exit:0*/
/*eslint-env node, es6 */
"use strict";
const port = process.env.PORT || 3000;
const server = require("http").createServer();

const cds = require("@sap/cds");
//Initialize Express App for XSA UAA and HDBEXT Middleware
const xsenv = require("@sap/xsenv");
const passport = require("passport");
const xssec = require("@sap/xssec");
const xsHDBConn = require("@sap/hdbext");
const express = require("express");
global.__base = __dirname + "/";

//logging
var logging = require("@sap/logging");
var appContext = logging.createAppContext();

//Initialize Express App for XS UAA and HDBEXT Middleware
var app = express();

//Compression
app.use(require("compression")({
  threshold: "1b"
}));

//Helmet for Security Policy Headers
const helmet = require("helmet");
// ...
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    styleSrc: ["'self'", "sapui5.hana.ondemand.com"],
    scriptSrc: ["'self'", "sapui5.hana.ondemand.com"]
  }
}));
// Sets "Referrer-Policy: no-referrer".
app.use(helmet.referrerPolicy({ policy: "no-referrer" }));

passport.use("JWT", new xssec.JWTStrategy(xsenv.getServices({
uaa: {
tag: "xsuaa"
}
}).uaa));
app.use(logging.middleware({
appContext: appContext,
logNetwork: true
}));
app.use(passport.initialize());
app.use(
passport.authenticate("JWT", {
session: false
})
);

// Redirect any to service root
app.get("/", (req, res) => {
res.send("Jenkins with UI5");
});

//Setup Additonal Node.js Routes
require("./router")(app, server);

//Start the Server 
server.on("request", app);
server.listen(port, function () {
console.info(`HTTP Server: ${server.address().port}`);
});​

◈ Also open package.json in srv folder.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

And update with the following code:

{
    "name": "serve",
    "description": "Generated from ../package.json, do not change!",
    "version": "1.0.0",
    "dependencies": {
        "@sap/cds": "^3.10.0",
        "express": "^4.17.1",
        "@sap/xssec": "^2.1.17",
"@sap/xsenv": "^2.0.0",
        "hdb": "^0.17.0",
        "@sap/hdbext": "^6.0.0",
"@sap/hana-client": "^2.4.139",
"@sap/textbundle": "latest",
"@sap/logging": "^5.0.1",
"@sap/audit-logging": "^3.0.0",
"nodemailer": "^6.2.1",
"passport": "~0.4.0",
"async": "^3.0.1",
"ws": "^7.0.0",
"accept-language-parser": "latest",
"node-xlsx": "^0.15.0",
"node-zip": "~1.1.1",
"xmldoc": "~1.1.2",
"winston": "^3.2.1",
"body-parser": "^1.19.0",
"elementtree": "latest",
"then-request": "latest",
"compression": "~1.7",
"helmet": "^3.18.0",
"jenkins": "^0.27.0"
    },
    "engines": {
        "node": "^8.9",
        "npm": "^6"
    },
    "devDependencies": {},
    "scripts": {
    "postinstall": "cds build/all --project .. --clean",
"start": "node server.js"
    },
    "i18n": {
"folders": [
"_i18n"
]
},
"cds": {
"data": {
"driver": "hana"
}
}
}​

We added the NodeJS client for Jenkins from https://www.npmjs.com/package/jenkins in dependencies section: “jenkins”: “^0.27.0”.

◈ Create a folder in srv folder called router.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Create index.js in folder router.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

Insert the following code:

/*eslint-env node, es6 */
"use strict";

module.exports = (app, server) => {
app.use("/node", require("./routes/myNode")());
};​

◈ Create another folder routes inside router folder.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ We will create the NodeJS app to trigger build, get job information and stop build.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Create myNode.js inside routes folder.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

Insert the following code:

/*eslint no-console: 0, no-unused-vars: 0, no-shadow: 0, newcap:0*/
/*eslint-env node, es6 */
"use strict";
var express = require("express");
var async = require("async");
var jenkinsurl = "**UPDATE WITH YOUR JENKINS URL";

module.exports = function () {
var app = express.Router();

app.get("/triggerJob", (req, res) => {
var jenkins = require("jenkins")({
baseUrl: jenkinsurl,
crumbIssuer: true
});

jenkins.job.build("trigger", function (err, data) {
if (err) {
res.type("application/json").status(200).send("Error");
} else {

console.log("queue item number:" + data);
res.type("application/json").status(200).send('{"queue":' + data + '}');
}
});
});

app.get("/statusJob", (req, res) => {
var jenkins = require("jenkins")({
baseUrl: jenkinsurl,
crumbIssuer: true
});

jenkins.job.get("trigger", function (err, data) {
if (err) {
res.type("application/json").status(200).send("Error");
} else {

console.log("build:" + data);
res.type("application/json").status(200).send(data);
}
});
});
app.get("/cancelJob", (req, res) => {
var jobq = req.query.q;
console.log(jobq);

var jenkins = require("jenkins")({
baseUrl: jenkinsurl,
crumbIssuer: true
});

jenkins.build.stop('trigger', jobq, function(err) {
if (err) {
res.type("application/json").status(200).send('{"status":error}');
} else {
res.type("application/json").status(200).send('{"status":ok}');
}
});
});

return app;
};​

◈ Update the variable jenkinsurl in the above code with your Jenkins server. For example:

var jenkinsurl = "http://userid:password@10.11.18.134:8080";​

Create Web User (SAPUI5) Interface


◈ Do a right click on the project root folder zjenkinauto and select New > Basic HTML5 Module.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Give a module name web. Click Next to continue.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Click Finish to complete.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ You will see the web folder. Open xs-app.json in web folder.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

And update with the following code:

{
"welcomeFile": "index.html",
"authenticationMethod": "route",
"routes": [{
"source": "/node(.*)",
"destination": "srv_api",
"csrfProtection": true,
"authenticationType": "xsuaa"
}]
}​

◈ Populate the SAP UI5 files in resources folder. Copy from my Git: https://github.com/ferrygun/zjenkinauto

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ In Page.controller.js, we just call the NodeJS service using Ajax query to start, get status and cancel the job.

/*eslint no-console: 0, no-unused-vars: 0, no-use-before-define: 0, no-redeclare: 0, no-shadow:0*/
/*eslint-env es6 */
sap.ui.define(["sap/m/MessageToast", "sap/m/MessageBox", "sap/ui/core/mvc/Controller"],
function (MessageToast, MessageBox, Controller) {
"use strict";

function ongetJobStatus(myJSON) {
try {
var result = JSON.parse(myJSON);
console.log(result);

var lastBuild = result.lastBuild;
var lastCompletedBuild = result.lastCompletedBuild;
console.log(lastBuild + ":" + lastCompletedBuild);
//true = job is not running; false = job is running
if(lastBuild === null && lastCompletedBuild === null) {
return true;
} else if(lastBuild !== null && lastCompletedBuild === null) {
return false;
} else if(lastBuild !== null && lastCompletedBuild !== null) {
lastBuild = result.lastBuild.number;
lastCompletedBuild = result.lastCompletedBuild.number;

if (lastBuild === lastCompletedBuild) {
return true;
} else {
return false;
}
} else {
return true;
}

} catch (e) {
return "";
}
}

function getJobStatus() {
var aUrl = "/node/statusJob";

return ongetJobStatus(
jQuery.ajax({
url: aUrl,
method: "GET",
dataType: "json",
async: false
}).responseText);
}

function ontriggerJob(myJSON) {
try {
var result = JSON.parse(myJSON);
console.log(result);
return (result.queue);
} catch (e) {
return "";
}
}

function triggerJob() {
var aUrl = "/node/triggerJob";

return ontriggerJob(
jQuery.ajax({
url: aUrl,
method: "GET",
dataType: "json",
async: false
}).responseText);
}

function ongetcurrentJobqueue(myJSON) {
try {
var result = JSON.parse(myJSON);
console.log(result);

var lastBuild = result.lastBuild.number;
return lastBuild;

} catch (e) {
return "";
}
}

function getcurrentJobqueue() {
var aUrl = "/node/statusJob";

return ongetcurrentJobqueue(
jQuery.ajax({
url: aUrl,
method: "GET",
dataType: "json",
async: false
}).responseText);
}

function oncancelJob(myJSON) {
try {
var result = JSON.parse(myJSON);
console.log(result);

return (result.status);

} catch (e) {
return "";
}
}

function cancelJob(jobqueue) {
var aUrl = "/node/cancelJob?q=" + jobqueue;

return oncancelJob(
jQuery.ajax({
url: aUrl,
method: "GET",
dataType: "json",
async: false
}).responseText);
}

var PageController = Controller.extend("sap.m.sample.Button.Page", {

onPress_triggerjob: function (evt) {

let jobstatus = getJobStatus();
console.log(jobstatus);
if (jobstatus) {

let jobqueuenumber = triggerJob();
console.log(jobqueuenumber);
MessageBox.information("Job number:" + jobqueuenumber, {
onClose: function (oAction) {
if (oAction === sap.m.MessageBox.Action.OK) {
console.log("OK");
}
}
});
} else {
MessageBox.information("Please wait. Job is still running", {
onClose: function (oAction) {
if (oAction === sap.m.MessageBox.Action.OK) {
console.log("OK");
}
}
});
}
},

onPress_checkjob: function (evt) {

let jobstatus = getJobStatus();
console.log(jobstatus);
if (jobstatus) {
MessageBox.information("Job is not running", {
onClose: function (oAction) {
if (oAction === sap.m.MessageBox.Action.OK) {
console.log("OK");
}
}
});
} else {
MessageBox.information("Job is still running", {
onClose: function (oAction) {
if (oAction === sap.m.MessageBox.Action.OK) {
console.log("OK");
}
}
});
}
},

onPress_canceljob: function (evt) {
let jobqueue = getcurrentJobqueue();
let status = cancelJob(jobqueue);
console.log(status);

MessageBox.information("Job has been cancelled", {
onClose: function (oAction) {
if (oAction === sap.m.MessageBox.Action.OK) {
console.log("OK");
}
}
});
}
});

return PageController;

});​

Adding xs-security.json and update mta.yaml

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ The last step we need to do is to add the xs-security.json file in the root folder.

{
"xsappname": "zjenkinauto",
"scopes": [{
"name": "$XSAPPNAME.Display",
"description": "display"
}, {
"name": "$XSAPPNAME.Create",
"description": "create"
}, {
"name": "$XSAPPNAME.Edit",
"description": "edit"
}, {
"name": "$XSAPPNAME.Delete",
"description": "delete"
}, {
"name": "$XSAPPNAME.DataGenerator",
"description": "data generator"
}, {
"name": "xs_authorization.read",
"description": "Read authorization information from UAA"
}, {
"name": "xs_authorization.write",
"description": "Write authorization information to UAA"
}, {
"name": "$XSAPPNAME.ODATASERVICEUSER",
"description": "Enter"
}, {
"name": "$XSAPPNAME.ODATASERVICEADMIN",
"description": "Enter"
}],
"attributes": [{
"name": "client",
"description": "Session Client",
"valueType": "int"
}, {
"name": "country",
"description": "country",
"valueType": "s"
}],
"role-templates": [{
"name": "Viewer",
"description": "View all records",
"scope-references": [
"$XSAPPNAME.Display"
],
"attribute-references": [
"client", "country"
]
}, {
"name": "Editor",
"description": "Edit and Delete records",
"scope-references": [
"$XSAPPNAME.Create",
"$XSAPPNAME.Edit",
"$XSAPPNAME.Delete",
"$XSAPPNAME.Display",
"$XSAPPNAME.DataGenerator",
"$XSAPPNAME.ODATASERVICEUSER",
"$XSAPPNAME.ODATASERVICEADMIN"
],
"attribute-references": [
"client"
]
}]
}​

◈ And also to modify the mta.yaml with this code:

ID: zjenkinauto
_schema-version: '2.1'
version: 0.0.1

modules:
 - name: srv
   type: nodejs
   path: srv
   parameters:
     memory: 512M
     disk-quota: 256M
   provides:
     - name: srv_api
       properties:
         url: '${default-url}'
   requires:
     - name: zjenkinauto-uaa

 - name: web
   type: html5
   path: web
   requires:
     - name: zearnpfe-uaa
     - name: srv_api
       group: destinations
       properties:
         name: srv_api
         url: '~{url}'
         forwardAuthToken: true
         
resources:
  - name: zjenkinauto-uaa
    type: com.sap.xs.uaa-space
    parameters:
      config-path: ./xs-security.json

Execution


We have completed the necessary setup and now it’s time for the testing and execution.

◈ Click on Trigger Job to submit job.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ If there is no error, you will see the job queue number.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Now go to Jenkins console and you will see the job is being executed.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Click Check Job to get the job status.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ If the job is complete or not running, you will see this message.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Click Cancel Job to cancel the job that has been submitted.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

◈ Check the job status in Jenkins console.

SAP HANA XSA, SAP HANA Tutorials and Materials, SAP HANA Guides, SAP HANA Certifications, SAP HANA Study Materials, SAP HANA Online Exam

No comments:

Post a Comment