From 8fa8056b9223fea6192d6a1b712b9f826bed5df0 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Mon, 22 Aug 2016 09:43:19 -0700 Subject: [PATCH 01/10] Default to empty string when getting TASK_TIMEOUT. The ValueError will catch it and use the default timeout. --- images/python/bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/python/bootstrap.py b/images/python/bootstrap.py index 1b7d95f..9ecf6e0 100644 --- a/images/python/bootstrap.py +++ b/images/python/bootstrap.py @@ -113,7 +113,7 @@ def getPAYLOAD_FILE(): def getTASK_TIMEOUT(): try: - return int(os.environ.get('TASK_TIMEOUT')) + return int(os.environ.get('TASK_TIMEOUT', '')) except ValueError: return 3600 From d2288f000406bed53ad821f75e6878704d3dd735 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Mon, 29 Aug 2016 18:54:32 -0700 Subject: [PATCH 02/10] Add note about image name to aws-import. --- docs/aws.md | 2 -- docs/import.md | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/aws.md b/docs/aws.md index 47c04bd..0da9f92 100644 --- a/docs/aws.md +++ b/docs/aws.md @@ -229,8 +229,6 @@ nodejs -handler sns.handler sns.js This will create a local docker image. The `publish-function` command will upload this to Docker Hub and register it with IronWorker. -FIXME(nikhil): AWS credentials bit. - To be able to use the AWS SDK, you'll also need to set two environment variables. The values must be your AWS credentials. diff --git a/docs/import.md b/docs/import.md index 3b567a7..2ed9caa 100644 --- a/docs/import.md +++ b/docs/import.md @@ -29,6 +29,11 @@ ironcli lambda aws-import my-function will import the function code to a directory called `./my-function`. It will then create a docker image called `my-function`. +Using Lambda with Docker Hub and Iron Worker requires that the Docker image be +named `/`. This is used to uniquely identify +images on Docker Hub. Please use the `-image /` flag to `aws-import` to create a correctly named image. + If you only want to download the code, pass the `-download-only` flag. The `-region` and `-profile` flags are available similar to the `aws` tool to help you tweak the settings on a command level. If you want to call the docker image From 7a1489eb4d9a32be1f470435667368205e87a25d Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Mon, 24 Oct 2016 12:24:11 -0500 Subject: [PATCH 03/10] Stdin support (#61) * stdin payload support for python. * nodejs read from stdin support. * Fix vendoring * Java support. * Java treats null and empty string as different things. * Fix python payload error handling. * Idiomatic Python short circuits --- .../main/java/io/iron/lambda/Launcher.java | 26 ++++-- images/node/bootstrap.js | 87 ++++++++++++------- images/python/bootstrap.py | 17 ++-- test-suite/glide.lock | 41 ++++++--- test-suite/glide.yaml | 8 ++ 5 files changed, 119 insertions(+), 60 deletions(-) diff --git a/images/java/src/main/java/io/iron/lambda/Launcher.java b/images/java/src/main/java/io/iron/lambda/Launcher.java index c4ea43b..499c5a1 100644 --- a/images/java/src/main/java/io/iron/lambda/Launcher.java +++ b/images/java/src/main/java/io/iron/lambda/Launcher.java @@ -15,13 +15,29 @@ public class Launcher { public static void main(String[] args) { String handler = args[0]; String payload = ""; - try { - String file = System.getenv("PAYLOAD_FILE"); - if (file != null) { + String file = System.getenv("PAYLOAD_FILE"); + if (file != null && !file.isEmpty()) { + try { payload = new String(Files.readAllBytes(Paths.get(file))); + } catch (IOException ioe) { + System.err.println("bootstrap: Could not read payload file " + ioe); + System.exit(1); + } + } else { + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int n; + while ((n = System.in.read(buffer)) != -1) { + os.write(buffer, 0, n); + } + os.close(); + + payload = os.toString("UTF-8"); + } catch (IOException ioe) { + System.err.println("bootstrap: Could not read stdin " + ioe); + System.exit(1); } - } catch (IOException ioe) { - // Should probably log this somewhere useful but not in the output. } try { diff --git a/images/node/bootstrap.js b/images/node/bootstrap.js index 0e41b6c..cf594b3 100644 --- a/images/node/bootstrap.js +++ b/images/node/bootstrap.js @@ -225,50 +225,71 @@ var makeCtx = function() { function run() { // FIXME(nikhil): Check for file existence and allow non-payload. - var payload = {}; var path = process.env["PAYLOAD_FILE"]; + var stream = process.stdin; if (path) { try { - var contents = fs.readFileSync(path, { encoding: 'utf8' }); - payload = JSON.parse(contents); + stream = fs.createReadStream(path); } catch(e) { - console.error("bootstrap: Error reading payload file", e) + console.error("bootstrap: Error opening payload file", e) + process.exit(1); } } - if (process.argv.length > 2) { - var handler = process.argv[2]; - var parts = handler.split('.'); - // FIXME(nikhil): Error checking. - var script = parts[0]; - var entry = parts[1]; - var started = false; - try { - var mod = require('./'+script); - var func = mod[entry]; - if (func === undefined) { - console.log("Handler '" + entry + "' missing on module '" + script + "'"); - return; - } + var input = ""; + stream.setEncoding('utf8'); + stream.on('data', function(chunk) { + input += chunk; + }); - if (typeof func !== 'function') { - throw "TypeError: " + (typeof func) + " is not a function"; - } - started = true; - mod[entry](payload, makeCtx()) + stream.on('error', function(err) { + console.error("bootstrap: Error reading payload stream", err); + process.exit(1); + }); + + stream.on('end', function() { + try { + var payload = JSON.parse(input); } catch(e) { - if (typeof e === 'string') { - console.log(e) - } else { - console.log(e.message) - } - if (!started) { - console.log("Process exited before completing request\n") + console.error("bootstrap: Error parsing JSON", e); + process.exit(1); + } + + if (process.argv.length > 2) { + var handler = process.argv[2]; + var parts = handler.split('.'); + // FIXME(nikhil): Error checking. + var script = parts[0]; + var entry = parts[1]; + var started = false; + try { + var mod = require('./'+script); + var func = mod[entry]; + if (func === undefined) { + console.log("Handler '" + entry + "' missing on module '" + script + "'"); + return; + } + + if (typeof func !== 'function') { + throw "TypeError: " + (typeof func) + " is not a function"; + } + started = true; + mod[entry](payload, makeCtx()) + } catch(e) { + if (typeof e === 'string') { + console.log(e) + } else { + console.log(e.message) + } + if (!started) { + console.log("Process exited before completing request\n") + } } + } else { + console.error("bootstrap: No script specified") + process.exit(1); } - } else { - console.error("bootstrap: No script specified") - } + }) } run() diff --git a/images/python/bootstrap.py b/images/python/bootstrap.py index 9ecf6e0..734f0c9 100644 --- a/images/python/bootstrap.py +++ b/images/python/bootstrap.py @@ -213,11 +213,6 @@ def filter(self, record): if handlerName is None: stopWithError("handlerName arg is not specified") -if payloadFileName is None: - stopWithError("PAYLOAD_FILE variable is not specified") - -if not os.path.isfile(payloadFileName): - stopWithError("No payload present") handlerParts = string.rsplit(handlerName, ".", 2) @@ -234,12 +229,16 @@ def filter(self, record): stopWithError("Function name is not defined") try: - with file(payloadFileName) as f: + if payloadFileName: + payloadFile = file(payloadFileName, 'r') + else: + payloadFile = sys.stdin + + with payloadFile as f: payload = f.read() -except: - stopWithError("Failed to read {payloadFileName}".format(payloadFileName=payloadFileName)) -debugging and print ('payload loaded') +except Exception, e: + stopWithError("Failed to read {payloadFileName}. err={err}".format(payloadFileName=(payloadFileName or ''), err=e)) try: payload = json.loads(payload) diff --git a/test-suite/glide.lock b/test-suite/glide.lock index 0aa23fd..f8d09ac 100644 --- a/test-suite/glide.lock +++ b/test-suite/glide.lock @@ -1,8 +1,8 @@ -hash: 53e196d6ce241dae228e214717004a4ed3ce39691e8ffee195e37e76f6e9e265 -updated: 2016-03-22T10:32:17.613767945-07:00 +hash: 144bd93c4247f3246e459717b36598951283499fa842ff3bed8ad23dfea2478d +updated: 2016-10-18T11:20:43.851037791-07:00 imports: - name: github.com/aws/aws-sdk-go - version: 4da0bec8953a0a540f391930a946917b12a95671 + version: 8f2725740345a0561fa09669f5f75f905404ef8b subpackages: - aws - aws/awserr @@ -26,39 +26,54 @@ imports: - private/protocol/json/jsonutil - private/protocol/rest - name: github.com/Azure/go-ansiterm - version: 70b2c90b260171e829f1ebd7c17f600c11858dbe + version: fa152c58bc15761d0200cb75fe958b89a9d4888e subpackages: - winterm - name: github.com/docker/docker - version: 0dcdd1c5b84c4a066882ea52dd81b5b3a624897a + version: 20f81dde9bd97c86b2d0e33bbbf1388018611929 subpackages: - pkg/jsonmessage - pkg/jsonlog - pkg/term - pkg/system - pkg/term/windows +- name: github.com/docker/engine-api + version: 4290f40c056686fcaa5c9caf02eac1dde9315adf + subpackages: + - types/filters + - types/swarm - name: github.com/docker/go-units - version: 5d2041e26a699eaca682e2ea41c8f891e1060444 + version: f2145db703495b2e525c59662db69a7344b00bb8 - name: github.com/go-ini/ini - version: 12f418cc7edc5a618a51407b7ac1f1f512139df3 + version: 6e4869b434bd001f6983749881c7ead3545887d8 +- name: github.com/hashicorp/go-cleanhttp + version: ad28ea4487f05916463e2423a55166280e8254b5 - name: github.com/iron-io/iron_go3 - version: d1197926301489b15e418c49a0b17a9c42817acd + version: cd9cc95ce2d2bb25d2e4e10cd62fff1d97ad1906 subpackages: - worker - api - config +- name: github.com/iron-io/lambda + version: "" + subpackages: + - lambda - name: github.com/jmespath/go-jmespath - version: 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74 + version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d +- name: github.com/opencontainers/runc + version: bf77e5976ad42b6167a167a9f3093748f7c154db + subpackages: + - libcontainer/user - name: github.com/satori/go.uuid - version: e673fdd4dea8a7334adbbe7f57b7e4b00bdc5502 + version: b061729afc07e77a8aa4fad0a2fd840958f1942a - name: github.com/sendgrid/sendgrid-go version: 0bf6332788d0230b7da84a1ae68d7531073200e1 - name: github.com/sendgrid/smtpapi-go - version: 08102729bcf72bfe74dde6c0da8e0423e94090ca + version: 072b6f477c501c30348f442559997853f12b364e - name: github.com/Sirupsen/logrus - version: 4b6ea7319e214d98c938f12692336f7ca9348d6b + version: 3ec0642a7fb6488f65b06f9040adc67e3990296a - name: golang.org/x/sys - version: 9d4e42a20653790449273b3c85e67d6d8bae6e2e + version: 002cbb5f952456d0c50e0d2aff17ea5eca716979 subpackages: - unix devImports: [] diff --git a/test-suite/glide.yaml b/test-suite/glide.yaml index 5b60b51..cfc5b3d 100644 --- a/test-suite/glide.yaml +++ b/test-suite/glide.yaml @@ -21,3 +21,11 @@ import: version: ~1.10.3 subpackages: - pkg/jsonmessage +- package: github.com/docker/engine-api + subpackages: + - types/filters + - types/swarm +- package: github.com/hashicorp/go-cleanhttp +- package: github.com/opencontainers/runc + subpackages: + - libcontainer/user From d883e4b5ef216c3fcda72cf6628d9d72dd53be49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Seif=20Lotfy=20=D8=B3=D9=8A=D9=81=20=D9=84=D8=B7=D9=81?= =?UTF-8?q?=D9=8A?= Date: Wed, 26 Oct 2016 18:23:27 +0100 Subject: [PATCH 04/10] Change node bootstrap print to stdout (#62) --- images/node/bootstrap.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/images/node/bootstrap.js b/images/node/bootstrap.js index cf594b3..4bd85b9 100644 --- a/images/node/bootstrap.js +++ b/images/node/bootstrap.js @@ -64,11 +64,10 @@ var Context = function() { result = null } - var str; var failed = false; try { - str = JSON.stringify(result) - // Succeed does not output to log, it only responds to the HTTP request. + // Output result to log + console.log(JSON.stringify(result)); } catch(e) { // Set X-Amz-Function-Error: Unhandled header console.log("Unable to stringify body as json: " + e); @@ -248,8 +247,11 @@ function run() { }); stream.on('end', function() { + var payload = {} try { - var payload = JSON.parse(input); + if (input.length > 0) { + payload = JSON.parse(input); + } } catch(e) { console.error("bootstrap: Error parsing JSON", e); process.exit(1); From 19c4a281bf92c2f23bfaccd137c406f643928aca Mon Sep 17 00:00:00 2001 From: Seif Lotfy Date: Wed, 2 Nov 2016 19:08:26 +0100 Subject: [PATCH 05/10] Add support for stdout and convert results to json --- images/python/bootstrap.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/images/python/bootstrap.py b/images/python/bootstrap.py index 734f0c9..cd64ebb 100644 --- a/images/python/bootstrap.py +++ b/images/python/bootstrap.py @@ -241,7 +241,8 @@ def filter(self, record): stopWithError("Failed to read {payloadFileName}. err={err}".format(payloadFileName=(payloadFileName or ''), err=e)) try: - payload = json.loads(payload) + if len(payload) > 0: + payload = json.loads(payload) except: debugging and print ('payload is ') and print (payload) stopWithError('Payload is not JSON') @@ -261,7 +262,7 @@ def filter(self, record): try: result = caller.call(payload, context) - #FIXME where to put result in async mode? + sys.stdout.write(json.dumps(result)) except Exception as e: stopWithError(e) From 70b6f2679496e03610fea4136a5b31a7d95f0d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Seif=20Lotfy=20=D8=B3=D9=8A=D9=81=20=D9=84=D8=B7=D9=81?= =?UTF-8?q?=D9=8A?= Date: Thu, 3 Nov 2016 17:44:53 +0100 Subject: [PATCH 06/10] Serialize output to JSON in Java (#64) * Serialize output to JSoN in Java * catch json serialization exception * print out a proper error message on error during serialization --- .../examples/java/src/main/java/example/Hello.java | 4 ++-- .../java/src/main/java/io/iron/lambda/Launcher.java | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/images/examples/java/src/main/java/example/Hello.java b/images/examples/java/src/main/java/example/Hello.java index 7d4054c..3b98d2f 100644 --- a/images/examples/java/src/main/java/example/Hello.java +++ b/images/examples/java/src/main/java/example/Hello.java @@ -49,7 +49,7 @@ public ArrayList myHandlerList(ArrayList arrayList, Context return arrayList; } - public static void myHandlerPOJO(RequestClass request, Context context){ - System.out.println(String.format("Hello %s %s" , request.firstName, request.lastName)); + public static String myHandlerPOJO(RequestClass request, Context context){ + return String.format("Hello %s %s" , request.firstName, request.lastName); } } diff --git a/images/java/src/main/java/io/iron/lambda/Launcher.java b/images/java/src/main/java/io/iron/lambda/Launcher.java index 499c5a1..f3c5c87 100644 --- a/images/java/src/main/java/io/iron/lambda/Launcher.java +++ b/images/java/src/main/java/io/iron/lambda/Launcher.java @@ -131,6 +131,15 @@ private void runMethod(Method lambdaMethod, String payload, Class cls, String ha if (!processed) { System.err.println(String.format("Handler %s with simple, POJO, or IO(input/output) types not found", handlerName)); } + + try { + String jsonInString = ClassTypeHelper.gson.toJson(result); + System.out.println(jsonInString); + } + catch (Exception e) { + System.out.println("{\"error\": \"" + ClassTypeHelper.gson.toJson(e.toString()) + "\"}"); + System.exit(1); + } } private void launchMethod(String[] packageHandler, String payload) throws Exception { @@ -195,10 +204,6 @@ private void launchMethod(String[] packageHandler, String payload) throws Except }); Class returnType = matchedArray[0].getReturnType(); - if (!returnType.getTypeName().equals("void")) { - System.err.println(String.format("Handler can only have 'void' return type. Found '%s'.", returnType.getTypeName())); - System.exit(1); - } runMethod(matchedArray[0], payload, cls, handlerName); } From b80d4fc6e18798822360265c083e7abf7f04fb0c Mon Sep 17 00:00:00 2001 From: Seif Lotfy Date: Wed, 9 Nov 2016 17:58:02 +0100 Subject: [PATCH 07/10] images/node: fix bootstrap.js to extract CONFIG_* variables into the environment --- images/node/bootstrap.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/images/node/bootstrap.js b/images/node/bootstrap.js index 4bd85b9..62b7aa5 100644 --- a/images/node/bootstrap.js +++ b/images/node/bootstrap.js @@ -222,7 +222,23 @@ var makeCtx = function() { return ctx; } +var setEnvFromHeader = function () { + var headerPrefix = "CONFIG_"; + var newEnvVars = {}; + for (var key in process.env) { + if (key.indexOf(headerPrefix) == 0) { + newEnvVars[key.slice(headerPrefix.length)] = process.env[key]; + } + } + + for (var key in newEnvVars) { + process.env[key] = newEnvVars[key]; + } +} + + function run() { + setEnvFromHeader(); // FIXME(nikhil): Check for file existence and allow non-payload. var path = process.env["PAYLOAD_FILE"]; var stream = process.stdin; From 56e0f8b05e7d1cb2a13dd6bcde424ffc430c4f62 Mon Sep 17 00:00:00 2001 From: Seif Lotfy Date: Wed, 9 Nov 2016 17:59:06 +0100 Subject: [PATCH 08/10] images/python: fix bootstrap.py to extract CONFIG_* variables into the environment --- images/python/bootstrap.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/images/python/bootstrap.py b/images/python/bootstrap.py index cd64ebb..1053196 100644 --- a/images/python/bootstrap.py +++ b/images/python/bootstrap.py @@ -192,6 +192,18 @@ def filter(self, record): } logging.config.dictConfig(loggingConfig) +def setEnvFromHeader(): + headerPrefix = "CONFIG_" + newEnvVars = {} + for key, value in os.environ.iteritems(): + if key.startswith(headerPrefix): + newEnvVars[key[len(headerPrefix):]] = value + + for key, value in newEnvVars.iteritems(): + os.environ[key] = value + + +setEnvFromHeader() plannedEnd = int(time.time()) + getTASK_TIMEOUT() debugging and print ('os.environ = ', os.environ) From 2101c100b865f09f062e0cf6ae154c74d1593d35 Mon Sep 17 00:00:00 2001 From: Seif Lotfy Date: Wed, 9 Nov 2016 21:18:21 +0100 Subject: [PATCH 09/10] images/java: fix LambdaLauncher.sh to extract CONFIG_* variables into the environment --- images/java/LambdaLauncher.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/images/java/LambdaLauncher.sh b/images/java/LambdaLauncher.sh index 870412c..fc50294 100755 --- a/images/java/LambdaLauncher.sh +++ b/images/java/LambdaLauncher.sh @@ -17,6 +17,13 @@ else exit 1 fi +# set env variables from CONFIG_* prefixed ones +for i in $(env); do + if [[ $i == CONFIG_* ]]; then + export ${i:7} + fi +done + java -jar lambda.jar $2 exit 0 From 4a046af3249dd5933a8d328f965e9717c97fef24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Seif=20Lotfy=20=D8=B3=D9=8A=D9=81=20=D9=84=D8=B7=D9=81?= =?UTF-8?q?=D9=8A?= Date: Fri, 11 Nov 2016 01:30:26 +0100 Subject: [PATCH 10/10] Redirect stdout for python, java and nodejs (#67) To avoid having print out in the results --- examples/s3/example.js | 4 ++-- .../src/main/java/io/iron/lambda/Launcher.java | 12 ++++++++++-- images/node/bootstrap.js | 17 ++++++++++------- images/python/bootstrap.py | 4 +++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/examples/s3/example.js b/examples/s3/example.js index 8b1e7c4..2f4c3de 100644 --- a/examples/s3/example.js +++ b/examples/s3/example.js @@ -59,10 +59,10 @@ exports.run = function(event, context) { ACL: 'public-read', }, function (err, data) { if (err) throw err; - context.done() + context.succeed("Image updated"); }); } else { - context.done(); + context.succeed("Image not updated"); } }); }); diff --git a/images/java/src/main/java/io/iron/lambda/Launcher.java b/images/java/src/main/java/io/iron/lambda/Launcher.java index f3c5c87..6d63ef7 100644 --- a/images/java/src/main/java/io/iron/lambda/Launcher.java +++ b/images/java/src/main/java/io/iron/lambda/Launcher.java @@ -12,6 +12,14 @@ import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner; public class Launcher { + + PrintStream oldout; + + public Launcher() { + oldout = System.out; + System.setOut(System.err); + } + public static void main(String[] args) { String handler = args[0]; String payload = ""; @@ -134,10 +142,10 @@ private void runMethod(Method lambdaMethod, String payload, Class cls, String ha try { String jsonInString = ClassTypeHelper.gson.toJson(result); - System.out.println(jsonInString); + oldout.println(jsonInString); } catch (Exception e) { - System.out.println("{\"error\": \"" + ClassTypeHelper.gson.toJson(e.toString()) + "\"}"); + oldout.println("{\"error\": \"" + ClassTypeHelper.gson.toJson(e.toString()) + "\"}"); System.exit(1); } } diff --git a/images/node/bootstrap.js b/images/node/bootstrap.js index 62b7aa5..a080ed6 100644 --- a/images/node/bootstrap.js +++ b/images/node/bootstrap.js @@ -2,6 +2,9 @@ var fs = require('fs'); +var oldlog = console.log +console.log = console.error + // Some notes on the semantics of the succeed(), fail() and done() methods. // Tests are the source of truth! // First call wins in terms of deciding the result of the function. BUT, @@ -67,7 +70,7 @@ var Context = function() { var failed = false; try { // Output result to log - console.log(JSON.stringify(result)); + oldlog(JSON.stringify(result)); } catch(e) { // Set X-Amz-Function-Error: Unhandled header console.log("Unable to stringify body as json: " + e); @@ -107,10 +110,10 @@ var Context = function() { } else { errstr = error.toString() } - console.log(JSON.stringify({"errorMessage": errstr })) + oldlog(JSON.stringify({"errorMessage": errstr })) } catch(e) { // Set X-Amz-Function-Error: Unhandled header - console.log(errstr) + oldlog(errstr) } } @@ -284,7 +287,7 @@ function run() { var mod = require('./'+script); var func = mod[entry]; if (func === undefined) { - console.log("Handler '" + entry + "' missing on module '" + script + "'"); + oldlog("Handler '" + entry + "' missing on module '" + script + "'"); return; } @@ -295,12 +298,12 @@ function run() { mod[entry](payload, makeCtx()) } catch(e) { if (typeof e === 'string') { - console.log(e) + oldlog(e) } else { - console.log(e.message) + oldlog(e.message) } if (!started) { - console.log("Process exited before completing request\n") + oldlog("Process exited before completing request\n") } } } else { diff --git a/images/python/bootstrap.py b/images/python/bootstrap.py index 1053196..b575caf 100644 --- a/images/python/bootstrap.py +++ b/images/python/bootstrap.py @@ -8,6 +8,8 @@ import time import uuid +oldstdout = sys.stdout +sys.stdout = sys.stderr debugging = False @@ -274,7 +276,7 @@ def setEnvFromHeader(): try: result = caller.call(payload, context) - sys.stdout.write(json.dumps(result)) + oldstdout.write(json.dumps(result)) except Exception as e: stopWithError(e)