Difficulty: beginner
Estimated Time: 10 minutes

Apache OpenWhisk on OpenShift

Actions created in Apache OpenWhisk can be exposed via HTTP and invoked just like any other REST resource. In this lesson, we will see how to take a Java Action and expose an HTTP endpoint so that it can be invoked using any HTTP tool at your disposal.

Additional Resources

Congratulations

You now know how to expose any of your Actions via HTTP and access them using standard tools.

Please take sometime to play with other related scenarios that are available to get yourself familiarized with various serverless concepts on OpenShift Cloud Functions. Good Luck!

Additional Resources

Don’t stop now! The next scenario will only take about 10 minutes to complete.

Web Accessible Actions on OCF

Step 1

Create the Java Action

1. Create the Java Action

First we need to create our Java Action using the Java Action Maven Archetype. In this scenario, we will create a simple echo Action that simply returns whatever we send it.

cd /root/projects

Create a Java Action project called my-echo

mvn -q archetype:generate \
    -DarchetypeGroupId=org.apache.openwhisk.java \
    -DarchetypeArtifactId=java-action-archetype \
    -DarchetypeVersion=1.0-SNAPSHOT \
    -DgroupId=com.example \
    -DartifactId=my-echo

Move to the project directory

cd my-echo

Let's open the Java source file src/main/java/com/example/FunctionApp.java to review its contents. Click the link below to open the source file in the editor:

my-echo/src/main/java/com/example/FunctionApp.java

All Java Action classes should have a main method with a signature that takes a com.google.gson.JsonObject as parameter and returns a com.google.gson.JsonObject. We need to update the generated Action with our desired behavior. Update the FunctionApp class with this code:

package com.example;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

/**
 * echo FunctionApp
 */
public class FunctionApp {
  public static JsonObject main(JsonObject args) {
    JsonObject response = new JsonObject();
    response.add("response", args);
    return response;
  }
}

With the main Action updated, now we need to update the tests.

my-echo/src/test/java/com/example/FunctionAppTest.java

Update the FunctionAppTest class with this code:

package com.example;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import org.junit.Test;

/**
 * echo FunctionAppTest
 */
public class FunctionAppTest {
  @Test
  public void testFunction() {
    JsonObject args = new JsonObject();
    args.addProperty("name", "test");
    JsonObject response = FunctionApp.main(args);
    assertNotNull(response);
    String actual = response.get("response").getAsJsonObject().get("name").getAsString();
    assertEquals("test", actual);
  }
}

Build the project

mvn -q package

NOTE: The Java Action maven archetype is not in maven central yet. If you plan to use it in your local OpenWhisk environment you then need to build and install from sources.

2. Deploy the Action

Let's now create a Action called my-echo in OpenWhisk:

wsk -i action create --web=true my-echo target/my-echo.jar --main com.example.FunctionApp

When we create Java Action the parameter --main is mandatory. It defines which Java class will be called during OpenWhisk Action invocation. The --web=true parameter indicates that this action should be exposed via an HTTP endpoint.

4. Verify the Action

Let's check if the Action is created correctly:

wsk -i action list | grep 'my-echo'

The output of the command should show something like:

/whisk.system/my-echo                             private java

Once that is done we can invoke our action and verify we get back the correct response:

WEB_URL=`wsk -i action get my-echo --url | awk 'FNR==2{print $1}'`
AUTH=`oc get secret whisk.auth -o yaml | grep "system:" | awk '{print $2}'`

Running the above commands will help simplify subsequent invocations of our action. Doing echo $WEB_URL should yield a URL that looks like this:

https://openwhisk-faas.2886795325-80-kitek02.environments.katacoda.com/api/v1/web/whisk.system/default/my-echo

Using this environment variable will remove the clutter of the full URL with each curl and let us do things like:

curl -k $WEB_URL.json

Executing the above command will return a JSON payload something like this:

{
  "response": {
    "__ow_method": "get",
    "__ow_headers": {
      "x-forwarded-port": "80",
      "accept": "*/*",
      "forwarded": "for=172.17.0.3;host=openwhisk-faas.2886795325-80-kitek02.environments.katacoda.com;proto=http",
      "user-agent": "curl/7.29.0",
      "x-forwarded-proto": "http",
      "host": "controller.faas.svc.cluster.local:8080",
      "x-scheme": "https://",
      "x-katacoda-host": "kitek02",
      "via": "1.1 google",
      "x-real-ip": "172.17.0.1",
      "x-cloud-trace-context": "c2dea7974f8aa974ca035ee128e79a6c/4529087608544220621",
      "accept-encoding": "gzip",
      "x-forwarded-host": "openwhisk-faas.2886795325-80-kitek02.environments.katacoda.com",
      "x-forwarded-for": "172.17.0.3"
    },
    "__ow_path": ""
  }
}

As you can see, we get returned to us the headers we send as part of the GET request. It's hard to tell from the above that's really doing what we want so let's add a little information to our request:

curl -k $WEB_URL.json?key=value

In this request, we're simply passing the key/value pair as part of our URL. This will give us a response like this:

{
  "response": {
    "__ow_method": "get",
    "__ow_headers": {
      "x-forwarded-port": "80",
      "accept": "*/*",
      "forwarded": "for=172.17.0.3;host=openwhisk-faas.2886795325-80-kitek02.environments.katacoda.com;proto=http",
      "user-agent": "curl/7.29.0",
      "x-forwarded-proto": "http",
      "host": "controller.faas.svc.cluster.local:8080",
      "x-scheme": "https://",
      "x-katacoda-host": "kitek02",
      "via": "1.1 google",
      "x-real-ip": "172.17.0.1",
      "x-cloud-trace-context": "a51436d85f3035d8b3a87e2e6f842922/11418910889981019075",
      "accept-encoding": "gzip",
      "x-forwarded-host": "openwhisk-faas.2886795325-80-kitek02.environments.katacoda.com",
      "x-forwarded-for": "172.17.0.3"
    },
    "__ow_path": "",
    "key": "value"
  }
}

Of course, we can also do a POST to this URL as well:

curl --insecure -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST $WEB_URL.json

This command will return to us the entire JSON document we POST here:

{
  "response": {
    "__ow_method": "post",
    "key1": "value1",
    "__ow_headers": {
      "x-forwarded-port": "80",
      "accept": "*/*",
      "forwarded": "for=172.17.0.3;host=openwhisk-faas.2886795325-80-kitek02.environments.katacoda.com;proto=http",
      "user-agent": "curl/7.29.0",
      "x-forwarded-proto": "http",
      "host": "controller.faas.svc.cluster.local:8080",
      "x-scheme": "https://",
      "x-katacoda-host": "kitek02",
      "content-type": "application/json",
      "via": "1.1 google",
      "x-real-ip": "172.17.0.1",
      "x-cloud-trace-context": "9e96faaeb03273c107e167e3bcbe989a/5519046671984655474",
      "accept-encoding": "gzip",
      "x-forwarded-host": "openwhisk-faas.2886795325-80-kitek02.environments.katacoda.com",
      "x-forwarded-for": "172.17.0.3"
    },
    "key2": "value2",
    "__ow_path": ""
  }
}