Amazon AWS Lambda & Tcl

Playing with the Idea of Tcl in Amazon AWS

This is meant to be a basic overview of how one would run Tcl for their Amazon AWS Lambda functions. It appears to work well with some edge cases at the moment that need to be smoothed out. The idea for this was taken from write ups on how to make the Go Language work within Amazon Lambda.

First we need to handle the tcl proc which we will be spawning using Node's child_process.spawn command:

const MAX_FAILS = 4;
var child_process = require('child_process'),
        tcl_proc = null,
        done = console.log.bind(console),
        fails = 0;


(function new_tcl_proc() {

        // pipe stdin/out, blind passthru stderr
        tcl_proc = child_process.spawn('./tclkit', ['_index.tcl'], { stdio: ['pipe', 'pipe', 'pipe'] });
        tcl_proc.on('error', function(err) {
                process.stderr.write("go_proc errored: "+JSON.stringify(err)+"\n");
                if (++fails > MAX_FAILS) {
                        process.exit(1); // force container restart after too many fails
                }
                new_tcl_proc();
                done(err);
        });

        tcl_proc.on('exit', function(code) {
                process.stderr.write("go_proc exited prematurely with code: "+code+"\n");
                if (++fails > MAX_FAILS) {
                        process.exit(1); // force container restart after too many fails
                }
                new_tcl_proc();
                done(new Error("Exited with code "+code));
        });

        tcl_proc.stdin.on('error', function(err) {
                process.stderr.write("go_proc stdin write error: "+JSON.stringify(err)+"\n");
                if (++fails > MAX_FAILS) {
                        process.exit(1); // force container restart after too many fails
                }
                new_tcl_proc();
                done(err);
        });

        var data = null;
        tcl_proc.stdout.on('data', function(chunk) {
                fails = 0; // reset fails
                if (data === null) {
                        data = new Buffer(chunk);
                } else {
                        data.write(chunk);
                }
                // check for newline ascii char
                if (data.length && data[data.length-1] == 10) {
                        var output = JSON.parse(data.toString('UTF-8'));
                        data = null;
                        done(null, output);
                };
        });
        
        var log = null
        tcl_proc.stderr.on('data', function(chunk) {
          fails = 0; // reset fails
                if (log === null) {
                        log = new Buffer(chunk);
                } else {
                        log.write(chunk);
                }
                // check for newline ascii char
                if (log.length && log[log.length-1] == 10) {
                        const logData = log.toString('UTF-8')
                        console.log(logData)
                        log = null;
                };
        })
})();

We do this at the top level so that Lambda will spawn the Tcl Interpreter on wake up but keep it open for subsequent calls made by your API.

Then we need to feed the data into our Tcl Process within the Lambda Handler Function:

exports.handle = function(event, context, callback) {
        console.log('Starting Function with Event: ', event)
  done = context.done.bind(context);
  tcl_proc.stdin.write(JSON.stringify({
                "event": event,
                "context": context
        })+"\n");
}

Now we need to include the appropriate TclKit in our directory which we will package up. I use the following which includes Yajl, UDP, Tcllib -- Tls is failing at the moment but I plan to have that included as well.

http://kitcreator.rkeene.org/kits/41d8a1636b31d9ce277e441c38ecfb274f63eb2a/tclkit

Now all we have to do is write our Tcl Script and retrieve the data from standard in that our Node Process handled and respond to the Node using puts on stdin.

gets stdin data
set data [::yajl::json2dict $data]

... do stuff

puts {{"myJson": "poorly formatted"}}

When combing this with the Apex ( http://apex.run/ ) or Serveless Framework you can yield powerful automation for your Lambda Functions.