[OpenWhisk] Triggered! (part 4)

Our chain of actions is now ready to receive inputs and works. We need to read the feed periodically and report changes. In that sentence, two things are very important to make the whole thing work:

  • periodic (we’ll need a cron of some description)
  • fires only when there are changes

We could obviously use an actual cron that will call wsk action invoke ... but we won’t. Instead, we’ll explore the system of rules and triggers in OpenWhisk.

In an actual production deployment, I would probably implement my own feed (that provides triggers) to make it more compact and probably a tiny teeny bit more efficient following the docs here

As it is, I will use an alarm from the conveniently named alarms package, to trigger a trigger if and only if things have changed.

If you need to install the alarms package, the quickest way for me was to use @abaruni‘s excellent ansible playbooks : https://github.com/apache/incubator-openwhisk/pull/2622

The Periodic Trigger

There is almost nothing to it: we just want to fire a trigger that will start the whole thing every so often. I’ve decided to go for every hour, but that’s me.

What we do need is for it to have some sort of payload that will allow us to monitor several sites. Thankfully, alarms allow for that:

wsk trigger create webcheck \
  --feed /whisk.system/alarms/alarm \
  --param cron "10 * * * *" \
  --param trigger_payload "{\"url\":\"https://blog.krugazor.eu/feed/json\"}" \
  --param startDate "2018-01-01T00:00:00.000Z" \
  --param stopDate "2018-01-31T23:59:00.000Z"

startDate and stopDate are mandatory, and it makes sense as having something in the system that takes resources indefinitely is kind of antithetical to the philosophy of OpenWhisk. You have to be explicit about these things. The payload is what will be passed along as parameters to whatever action responds that that trigger. In that particular instance, the action will read that URL and see if it has changed, triggering its own series of actions if it did. Again, not the best practices, just a way to highlight a different way of doing things.

The Change Check

We will be woken up periodically to check if a certain URL has changed. While checksumming the page for changes is fairly easy (I just showcase a cool way to implement 128 bits value), storing the value in a somewhat permanent fashion isn’t, unless I want to introduce a database connection of some description: the local storage is by essence volatile within the action. Because I didn’t really want to have yet another process in this demo, I decided to use the redis instance that we already have as part of the installation.

I could have gone the same route as previously and gone with a custom docker, but honestly, there is some laziness involved and the Swift redis package is actually dependent on packages that are already included in the default action, so I just copied and pasted the whole thing in my swift file. If your OpenWhisk instance runs on a slow server, the action might timeout during build time, in which case you should probably build a docker for it.

I then persist the checksum in the redis db, and it stays available for all calls that that action.

The missing piece of the puzzle is triggering, and it can be done by posting a json to a specific URL. Thankfully the environment of the action contains what we need : activation key, host, and namespace.

func buildTriggerURL(triggerName: String?, triggerNamespace: String?) -> URL {
    let env = ProcessInfo.processInfo.environment
 
    let auth = env["__OW_API_KEY"] ?? "BOGUSKEY"
    let host = env["__OW_API_HOST"] ?? "LOCALHOST"
    let namespace = triggerNamespace ?? (env["__OW_NAMESPACE"] ?? "CURRENTNAMESPACE")
    let eNamespace = namespace.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
    let trigger = triggerName ?? (env["__OW_ACTION_NAME"] ?? "CURRENTACTION")
    let eTrigger = trigger.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
 
    let triggerURLString = "https://\(auth)@\(host)/api/v1/namespaces/\(eNamespace)/triggers/\(eTrigger)"
 
    return URL(string: triggerURLString)!
}

All we need to do now is read the json feed that is passed along with the trigger, check if there are changes, and if there are, trigger the sequence we put in place previously with the correct parameters.

        let triggerURL = buildTriggerURL(triggerName: args["triggerName"] as? String, triggerNamespace: args["namespace"] as? String)
        // ...
                        var request = URLRequest(url: triggerURL)
                        request.httpMethod = "POST"
                        request.httpBody = "{ \"url\" : \"\(checkingURL)\" }".data(using: .utf8)
       // ...

The architecture

So, we have a trigger every hour that says “check this url”. We have the code for an action that checks a URL, and triggers something if it has changed. We now need to link the two as well as inform OpenWhisk that a certain trigger now exists.

wsk trigger create checklinks
wsk action create CheckChanges.swift --param triggerName checklinks
wsk rule create checkupdates webcheck CheckChanges
wsk rule create checklinkschain checklinks checkChain

Let’s take that one line at a time:

  • we create the trigger that will be called every time CheckChanges wants to signal the feed has changed its contents
  • we create the action CheckChanges that will be triggered periodically, and we set the name of the trigger it fires to the right value. The action now takes one parameter: url, which is included in the webcheck trigger
  • we create a rule (link between a trigger and an action) that says that webcheck will launch the CheckChanges action with the correct parameters
  • we create a rule that says that whenever checklinks happens (triggered by CheckChanges), we should launch the sequence checkChain created in the previous post

Now, what we have is a cascade of events that kind of looks like this:

Every hour, at 10 past, webcheck tells CheckChanges to check url, which generates as many checklinks as there are articles in the feed if the feed has changed, and for each of these triggers, we launch the whole identify links/check link status/send a summary mail chain of actions.

Again, this is far from ideal in a production project, it’s just a demo of the various ways you can do things with OpenWhisk. It could probably be done with one action and an alarm trigger. But that way, we can see the whole thing unfold and do something cool.

The result

The side effect (good side effect) is that we have no result of the various invocations. We will need to actually look for results post-activation.

If you want to see them live as they happen, you can use the wsk activation poll command. You can also use wsk activation list <action> to see all the activations of a specific action.

$ wsk activation list
58bb122ddd914d71bb122ddd91ad714a linkssummary        
b7a4b02a71124382a4b02a7112d3829c LinkStatus          
4d54ca5943e6422e94ca5943e6522e78 identifylinks     
542cb696d28845ceacb696d28875ce4f checkChain
80e722003ee4468ca722003ee4e68c35 checklinkschain  
# ... (multiple ones, as there are multiple items in the field
af09ab15bc034e2a89ab15bc038e2a75 checklinks
2f2646bd7b214ab0a646bd7b217ab0ee CheckChanges        
5df81ae46c6f4c52b81ae46c6f5c529f checkupdates        
bd02cae174d348e082cae174d368e0ea webcheck
  

Leave a Reply