Wrapping a legacy with a function

In this section, we will demonstrate how to write a wrapper function for a legacy web-based system. To achieve this, we use the chromeless library (https://github.com/graphcool/chromeless) to connect to a headless Chrome instance. Then the chromeless script drives the Chrome browser to do the rest for us.

The following diagram shows the working mechanism of this part of the system:

Figure 8.11: Diagram of implementing an OpenFaaS function to wrap around a UI-based ERP

What does chromeless do? chromeless is a Node.js library that can be used to perform browser automation, similar to PhantomJS or Selenium. But it is really fast. Together with headless Chrome instances, chromeless yields a very fast performance. So it could be used as a serverless function.

We start by using FaaS CLI to create a project. We call this function hivectl, a program to control an ERP program built with the Moqui framework, HiveMind. We will talk about HiveMind shortly after setting up this function:

$ faas new hivectl --lang node
2018/03/04 22:28:49 No templates found in current directory.
2018/03/04 22:28:50 Attempting to expand templates from https://github.com/openfaas/templates.git
2018/03/04 22:28:55 Fetched 11 template(s) : [csharp dockerfile go go-armhf node node-arm64 node-armhf python python-armhf python3 ruby] from https://github.com/openfaas/templates.git
Folder: hivectl created.

$ cd hivectl

Here's the content of hivectl.yml, the OpenFaaS function descriptor for the hivectl function:

provider:
name: faas
gateway: http://localhost:8080

functions:
hivectl:
lang: node
handler: ./hivectl
image: chanwit/hivectl:0.4

Here's a sample configuration to make chromeless connect to headless Chrome running inside another container on the same network. The trick is to set launchChrome to false, and set cdp, Chrome DevTool Protocol, pointing to host:'chrome', port: 9222:

const chromeless = new Chromeless({
launchChrome: false,
cdp: { host: 'chrome', port: 9222, secure: false, closeTab: true }
})

Here is the main chromeless script to remotely control a headless Chrome instance. We will put the program into hivectl/handler.js:

const { Chromeless } = require('chromeless')
const url = 'http://hivemind/vapps/hmadmin/Accounting/FinancialAccount/FinancialAccountTrans?finAccountId='

module.exports = (content, callback) => {

async function run(accountId, amount) {

const chromeless = new Chromeless({
launchChrome: false,
cdp: { host: 'chrome', port: 9222, secure: false, closeTab: true }
})

const screenshot = await chromeless
.goto('http://hivemind/Login/logout')
.click('#TestLoginLink_button')
.wait('.btn-danger')
.goto(url + accountId)
.wait('#AdjustDialog-button')
.click('#AdjustDialog-button')
.type(amount, '#AdjustFinancialAccount_amount')
.mousedown('#select2-AdjustFinancialAccount_reasonEnumId-container')
.mouseup('#select2-AdjustFinancialAccount_reasonEnumId-container')
.press(40, 5)
.press(13)
.click('#AdjustFinancialAccount_submitButton')
.screenshot()
.catch(e => {
console.log('{"error":"' + e.message + '"}')
process.exit(1);
})

console.log('{"success": "ok", "screenshot":"' + screenshot + '"}')

await chromeless.end()
}

const opt = JSON.parse(content)
run(opt.accountId, opt.amount).catch(console.error.bind(console))

};

With OpenFaaS, we can simply build the function container with the following command:

$ faas build -f ./hivectl.yml
...
Successfully built 1f7cc398fc61
Successfully tagged chanwit/hivectl:0.4
Image: chanwit/hivectl:0.4 built.
[0] < Building hivectl done.
[0] worker done.

Next, we will define the function in OpenFaaS. On the OpenFaaS UI, define a new function and the dialog will allow us to attach the new function to a specific network, parse_net in this case:

  • Image: chanwit/hivectl:0.4
  • Name: hivectl
  • Network: parse_net

We start a headless Chrome instance, exposing it as chrome on the same network as the caller function. This Chrome headless will be listening on TCP port 9222:

docker run -d --network=parse_net 
--network-alias=chrome
--cap-add=SYS_ADMIN
justinribeiro/chrome-headless

We now start an ERP system. It is the HiveMind ERP built using the Moqui framework. We can download it from the Moqui repository on GitHub (https://github.com/moqui/moqui-framework). Fortunately, the Moqui team also prepares a Docker image for use. So just run it and attach it to the main parse_net. Port 10000 is published only for debugging purposes:

$ docker run -p 10000:80 
-d --network=parse_net
--network-alias=hivemind
moqui/hivemind

The following screenshot shows the financial account page that will be processed by the chromeless function:

Figure 8.12: The HiveMind financial account page

Going back to the back routing, here's the code inside the WebHook (running on Fn) calling to the hivectl function (running on OpenFaaS). The WebHook code creates an HTTP client, then sends two parameters, accountId and amount, to the hivectl function:

public boolean faasAdjust(String txId, 
String accountId,
Double amount) throws Exception {
val env = System.getenv("FAAS_GATEWAY_SERVICE");
val faasGatewayService = (env == null? "http://gateway:8080" : env);

val JSON = MediaType.parse("application/json; charset=utf-8");
val client = new OkHttpClient();
val json = new ObjectMapper().writeValueAsString(new HashMap<String,String>(){{
put("accountId", accountId);
put("amount", String.valueOf(amount));
}});
val body = RequestBody.create(JSON, json);
val request = new Request.Builder()
.url(faasGatewayService + "/function/hivectl")
.post(body)
.build();
val response = client.newCall(request).execute();
System.out.println(response);

if(response.code() == 200) {
val str = response.body().string();
return true;
}

throw new Exception(response.toString());
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset