This is an automated email from the ASF dual-hosted git repository.
msciabarra pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-nodejs.git
The following commit(s) were added to refs/heads/master by this push:
new 912ee40 Actionloop based runtime for Typescript (#160)
912ee40 is described below
commit 912ee400d7be7ca4b428049418a1b5172e60ad32
Author: Michele Sciabarra <[email protected]>
AuthorDate: Tue Feb 25 07:49:02 2020 +0100
Actionloop based runtime for Typescript (#160)
---
.gitignore | 3 +
core/typescript37Action/Dockerfile | 59 ++++++++
core/typescript37Action/Makefile | 32 +++++
core/typescript37Action/bin/compile | 153 +++++++++++++++++++++
.../typescript37Action/build.gradle | 27 +---
core/typescript37Action/lib/launcher.ts | 101 ++++++++++++++
settings.gradle | 2 +
tests/dat/docker/typescript37docker/Dockerfile | 20 +++
.../dat/docker/typescript37docker/build.gradle | 27 +---
.../NodeJsActionContainerTests.scala | 70 ++++++----
.../actionContainers/NodeJsConcurrentTests.scala | 55 ++++----
.../NodeJsNonConcurrentTests.scala | 60 ++++----
.../actionContainers/Typescript37BasicTests.scala | 93 +++++++++++++
.../actionContainers/Typescript37CommonTests.scala | 31 ++---
14 files changed, 579 insertions(+), 154 deletions(-)
diff --git a/.gitignore b/.gitignore
index d5aef40..0f904f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,9 @@ results
!/ansible/environments/local
!/ansible/environments/mac
+# actionloop action compiler script
+!core/typescript37Action/bin/compile
+
# Eclipse
bin/
**/.project
diff --git a/core/typescript37Action/Dockerfile
b/core/typescript37Action/Dockerfile
new file mode 100644
index 0000000..e7135b2
--- /dev/null
+++ b/core/typescript37Action/Dockerfile
@@ -0,0 +1,59 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+FROM golang:1.12 as builder
+RUN env CGO_ENABLED=0 go get github.com/apache/openwhisk-runtime-go/main \
+ && mv /go/bin/main /bin/proxy
+FROM node:12.1.0-stretch
+ENV TYPESCRIPT_VERSION=3.7.4
+COPY --from=builder /bin/proxy /bin/proxy
+RUN apt-get update && apt-get install -y \
+ imagemagick \
+ graphicsmagick \
+ unzip \
+ wget \
+ && rm -rf /var/lib/apt/lists/* &&\
+ mkdir -p /app/action
+RUN cd /app ;\
+ npm install -g yarn ;\
+ npm install -g typescript@${TYPESCRIPT_VERSION} ;\
+ echo '{"private":true}' >package.json ;\
+ npm install --save --no-package-lock --production \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ @google-cloud/[email protected] \
+ @google-cloud/[email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ [email protected] \
+ && npm cache clean --force
+WORKDIR /app
+EXPOSE 8080
+COPY bin/compile /bin/compile
+COPY lib/launcher.ts /lib/launcher.ts
+ENV OW_COMPILER=/bin/compile
+ENV OW_LOG_INIT_ERROR=1
+ENV OW_WAIT_FOR_ACK=1
+ENV OW_EXECUTION_ENV=openwhisk/typescript3.7
+ENTRYPOINT ["/bin/proxy"]
diff --git a/core/typescript37Action/Makefile b/core/typescript37Action/Makefile
new file mode 100644
index 0000000..373f831
--- /dev/null
+++ b/core/typescript37Action/Makefile
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+PREFIX=whisk
+IMG=action-typescript-v3.7
+
+start:
+ docker run -p 8080:8080 -ti -v $(PWD):/mnt $(IMG)
+
+debug:
+ docker run -p 8080:8080 -p 8081:8081 -ti --entrypoint=/bin/bash \
+ -v $(PWD):/mnt -e OW_COMPILER=/mnt/bin/compile $(IMG)
+
+build:
+ docker build . -t $(IMG)
+ docker tag $(IMG) whisk/$(IMG)
+ docker images | grep $(IMG)
+
+.PHONY: start debug build
diff --git a/core/typescript37Action/bin/compile
b/core/typescript37Action/bin/compile
new file mode 100755
index 0000000..cf62d05
--- /dev/null
+++ b/core/typescript37Action/bin/compile
@@ -0,0 +1,153 @@
+#!/usr/bin/env node
+/*
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+*/
+const path = require("path")
+const fs = require("fs")
+const execFileSync = require('child_process').execFileSync;
+
+// write a file creating intermediate directories
+function write_file(file, body, executable) {
+ fs.mkdirSync(path.dirname(file), {recursive: true})
+ fs.writeFileSync(file, body)
+ if(executable)
+ fs.chmodSync(file, 0755)
+}
+
+// copy a file eventually replacing a substring
+function copy_replace(src, dst, match, replacement) {
+ var body = fs.readFileSync(src, "utf-8")
+ if(match)
+ body = body.replace(match, replacement)
+ write_file(dst, body)
+}
+
+function deext(filename) {
+ var pos = filename.lastIndexOf(".")
+ filename = pos > -1 ? filename.substring(0, pos) : filename
+ return filename
+}
+
+// resolve dependencies from package.json - return the main file
+function dependencies(src_dir) {
+ var pkg_config = src_dir+"/package.json"
+ var node_modules = src_dir+"/node_modules"
+ if(fs.existsSync(pkg_config)) {
+ if(!fs.existsSync(node_modules))
+ execFileSync("yarn", [], {
+ "cwd": src_dir
+ })
+ var config = JSON.parse(fs.readFileSync(pkg_config, "utf-8"))
+ //console.log(config)
+ if("main" in config) {
+ return deext(config["main"])
+ }
+ }
+ return "index"
+}
+
+// assemble sources
+function sources(launcher, main_file, main_func, src_dir) {
+ // init config
+ src_config = src_dir+"/tsconfig.json"
+ var config = {}
+ if(fs.existsSync(src_config)) {
+ config = JSON.parse(fs.readFileSync(src_config, "utf-8"))
+ }
+
+ if(!("files" in config))
+ config["files"] = []
+ if(!("compilerOptions" in config))
+ config["compilerOptions"] = {}
+ config["compilerOptions"]["inlineSourceMap"] = true
+ if("sourceMap" in config["compilerOptions"]) {
+ delete config["compilerOptions"]["sourceMap"]
+ }
+ if(!("outDir" in config["compilerOptions"]))
+ config["compilerOptions"]["outDir"] = "."
+
+ // copy main src file if any (and use it as main)
+ var src_file = src_dir+"/exec"
+ var tgt_file = src_dir+"/"+main_file+".ts"
+ if(fs.existsSync(src_file) && !fs.existsSync(tgt_file)){
+ var re = RegExp('(?<!export\\s+)function\\s+'+main_func)
+ copy_replace(src_file, tgt_file, re, "export function "+main_func)
+ config["files"].push(main_file+".ts")
+ }
+
+ // copy launcher and replace main
+ copy_replace(launcher,
+ src_dir+"/exec__.ts",
+ 'require("./main__").main',
+ 'require("./'+main_file+'").'+main_func)
+
+ // complete tsconfig.json
+ config["files"].push("exec__.ts")
+ write_file(src_config, JSON.stringify(config))
+}
+
+function build(src_dir, bin_dir) {
+ try {
+ fs.rmdirSync(bin_dir)
+ fs.renameSync(src_dir, bin_dir)
+ execFileSync("tsc", [], {
+ "cwd": bin_dir
+ })
+ write_file(bin_dir+"/exec",
+ '#!/bin/bash\n'+
+ 'if [ "$(cat $0.env)" != "$__OW_EXECUTION_ENV" ]\n'+
+ 'then cd "$(dirname $0)"\n'+
+ ' echo "Execution Environment Mismatch"\n'+
+ ' echo "Expected: $(cat $0.env)"\n'+
+ ' echo "Actual: $__OW_EXECUTION_ENV"\n'+
+ ' exit 1\n'+
+ 'fi\n'+
+ 'cd "$(dirname $0)"\n'+
+ 'if [ -z "$__OW_DEBUG_PORT" ]\n' +
+ 'then node exec__.js\n'+
+ 'else node --inspect=":$__OW_DEBUG_PORT" exec__.js\n'+
+ 'fi\n', true)
+ write_file(bin_dir+"/exec.env", process.env["__OW_EXECUTION_ENV"])
+ } catch(err) {
+ console.log("syntax error:", err.message)
+ }
+}
+
+function compile() {
+ if(process.argv.length<4) {
+ console.log("usage: <main-function> <source-dir> <target-dir>")
+ process.exit(1)
+ }
+ var launcher =
path.dirname(path.dirname(process.argv[1]))+"/lib/launcher.ts"
+ var src_dir = path.resolve(process.argv[3])
+ var bin_dir = path.resolve(process.argv[4])
+ var main_func = process.argv[2]
+ var main_file = dependencies(src_dir)
+ var pieces = main_func.split(".")
+ if(pieces.length >1) {
+ main_file = pieces.shift()
+ main_func = pieces.join(".")
+ }
+ //console.log(main_file, main_func)
+ sources(launcher, main_file, main_func, src_dir)
+ build(src_dir, bin_dir)
+}
+
+if(require.main === module) {
+ compile()
+}
diff --git a/settings.gradle b/core/typescript37Action/build.gradle
similarity index 56%
copy from settings.gradle
copy to core/typescript37Action/build.gradle
index da79f78..0476518 100644
--- a/settings.gradle
+++ b/core/typescript37Action/build.gradle
@@ -15,28 +15,5 @@
* limitations under the License.
*/
-include 'tests'
-
-include 'core:nodejsActionBase'
-include 'core:nodejs8Action'
-include 'core:nodejs10Action'
-include 'core:nodejs12Action'
-include 'tests:dat:docker:nodejs8docker'
-include 'tests:dat:docker:nodejs10docker'
-include 'tests:dat:docker:nodejs12docker'
-
-rootProject.name = 'runtime-nodejs'
-
-gradle.ext.openwhisk = [
- version: '1.0.0-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
+ext.dockerImageName = 'action-typescript-v3.7'
+apply from: '../../gradle/docker.gradle'
diff --git a/core/typescript37Action/lib/launcher.ts
b/core/typescript37Action/lib/launcher.ts
new file mode 100644
index 0000000..a575952
--- /dev/null
+++ b/core/typescript37Action/lib/launcher.ts
@@ -0,0 +1,101 @@
+/*
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+*/
+try {
+const main = require("./main__").main
+const readline = require('readline');
+const fs = require("fs")
+const os = require("os")
+
+function vscodeDebug() {
+ let ifaces = os.networkInterfaces()
+ for(let iface of Object.keys(ifaces)) {
+ for(let ip of ifaces[iface]) {
+ if(!ip.internal) {
+ return {
+ "type": "node",
+ "request": "attach",
+ "name": process.env["__OW_ACTION_NAME"],
+ "address": ip.address,
+ "port": 8081,
+ "localRoot": "${workspaceFolder}",
+ "remoteRoot": __dirname
+ }
+ }
+ }
+ }
+ return {"error": "cannot find external interface"}
+}
+
+async function actionLoop() {
+ const out = fs.createWriteStream(null,
+ { fd: 3, encoding: "utf8" })
+ process.stdin.setEncoding('utf8');
+ const rl = readline.createInterface({
+ input: process.stdin
+ });
+ const debugging = "__OW_DEBUG_PORT" in process.env
+ out.write(JSON.stringify({"ok":true})+"\n");
+ for await (const line of rl) {
+ try {
+ let args = JSON.parse(line)
+ let value = args.value || {}
+ for (let key in args) {
+ if(key !== "value") {
+ let envar = "__OW_"+key.toUpperCase()
+ process.env[envar] = args[key]
+ }
+ }
+ let result = {}
+ if(debugging && "debugWith" in value) {
+ if(value["debugWith"]==="vscode")
+ result = vscodeDebug()
+ else
+ result = {"error": "requested unknown debugger"}
+ } else {
+ result = main(value)
+ if(typeof result === 'undefined') {
+ result = {}
+ }
+ if(Promise.resolve(result) == result)
+ try {
+ result = await result
+ } catch(error) {
+ if(typeof error === 'undefined') {
+ error = {}
+ }
+ result = {"error": error }
+ }
+ }
+ out.write(JSON.stringify(result)+"\n");
+ } catch(err) {
+ console.log(err);
+ let message = err.message || err.toString()
+ let error = {"error": message}
+ out.write(JSON.stringify(error)+"\n");
+ }
+ }
+}
+actionLoop()
+} catch(e) {
+ if(e.code == "MODULE_NOT_FOUND") {
+ console.log("zipped actions must contain either package.json or
index.js at the root.")
+ }
+ console.log(e)
+ process.exit(1)
+}
diff --git a/settings.gradle b/settings.gradle
index da79f78..d688219 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -21,9 +21,11 @@ include 'core:nodejsActionBase'
include 'core:nodejs8Action'
include 'core:nodejs10Action'
include 'core:nodejs12Action'
+include 'core:typescript37Action'
include 'tests:dat:docker:nodejs8docker'
include 'tests:dat:docker:nodejs10docker'
include 'tests:dat:docker:nodejs12docker'
+include 'tests:dat:docker:typescript37docker'
rootProject.name = 'runtime-nodejs'
diff --git a/tests/dat/docker/typescript37docker/Dockerfile
b/tests/dat/docker/typescript37docker/Dockerfile
new file mode 100644
index 0000000..b6b31ec
--- /dev/null
+++ b/tests/dat/docker/typescript37docker/Dockerfile
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+FROM action-typescript-v3.7
+RUN cd /app && \
+ npm add --save [email protected] && \
+ npm install --production
diff --git a/settings.gradle b/tests/dat/docker/typescript37docker/build.gradle
similarity index 56%
copy from settings.gradle
copy to tests/dat/docker/typescript37docker/build.gradle
index da79f78..0d894ee 100644
--- a/settings.gradle
+++ b/tests/dat/docker/typescript37docker/build.gradle
@@ -15,28 +15,5 @@
* limitations under the License.
*/
-include 'tests'
-
-include 'core:nodejsActionBase'
-include 'core:nodejs8Action'
-include 'core:nodejs10Action'
-include 'core:nodejs12Action'
-include 'tests:dat:docker:nodejs8docker'
-include 'tests:dat:docker:nodejs10docker'
-include 'tests:dat:docker:nodejs12docker'
-
-rootProject.name = 'runtime-nodejs'
-
-gradle.ext.openwhisk = [
- version: '1.0.0-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
+ext.dockerImageName = 'typescript37docker'
+apply from: '../../../../gradle/docker.gradle'
diff --git
a/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
b/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
index 32fdd6e..4122309 100644
---
a/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
+++
b/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
@@ -29,6 +29,7 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
val nodejsContainerImageName: String
val nodejsTestDockerImageName: String
+ val isTypeScript = false
override def withActionContainer(env: Map[String, String] = Map.empty)(code:
ActionContainer => Unit) = {
withContainer(nodejsContainerImageName, env)(code)
@@ -137,7 +138,7 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
| 20 GOTO 10
""".stripMargin
- val (initCode, _) = c.init(initPayload(code))
+ val (initCode, res) = c.init(initPayload(code))
initCode should not be (200)
}
@@ -176,7 +177,10 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
initCode should be(200)
val (runCode, runRes) = c.run(runPayload(JsObject()))
- runCode should not be (200)
+ // actionloop proxy does not return a different error code when there is
an error,
+ // because it communicates only through json
+ if (!isTypeScript)
+ runCode should not be (200)
runRes shouldBe defined
runRes.get.fields.get("error") shouldBe defined
@@ -293,18 +297,21 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
| }
""".stripMargin
- c.init(initPayload(code))._1 should be(200)
-
- val (runCode, result) = c.run(runPayload(JsObject("payload" ->
JsString("test"))))
- runCode should be(200)
- result should be(Some(JsObject("payload" -> JsString("hello, test!"))))
+ if (isTypeScript)
+ c.init(initPayload(code))._1 should be(502)
+ else {
+ c.init(initPayload(code))._1 should be(200)
+ val (runCode, result) = c.run(runPayload(JsObject("payload" ->
JsString("test"))))
+ runCode should be(200)
+ result should be(Some(JsObject("payload" -> JsString("hello, test!"))))
+ }
}
-
- checkStreams(out, err, {
- case (o, e) =>
- o shouldBe "hello, test!"
- e shouldBe empty
- })
+ if (!isTypeScript)
+ checkStreams(out, err, {
+ case (o, e) =>
+ o shouldBe "hello, test!"
+ e shouldBe empty
+ })
}
it should "support webpacked function" in {
@@ -317,18 +324,24 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
|global.main = foo
""".stripMargin
- c.init(initPayload(code))._1 should be(200)
+ if (isTypeScript) {
+ c.init(initPayload(code))._1 should be(502)
+ } else {
+ c.init(initPayload(code))._1 should be(200)
- val (runCode, result) = c.run(JsObject.empty)
- runCode should be(200)
- result should be(Some(JsObject("bar" -> JsTrue)))
+ val (runCode, result) = c.run(JsObject.empty)
+ runCode should be(200)
+ result should be(Some(JsObject("bar" -> JsTrue)))
+ }
}
- checkStreams(out, err, {
- case (o, e) =>
- o shouldBe empty
- e shouldBe empty
- })
+ if (!isTypeScript) {
+ checkStreams(out, err, {
+ case (o, e) =>
+ o shouldBe empty
+ e shouldBe empty
+ })
+ }
}
it should "error when requiring a non-existent package" in {
@@ -349,12 +362,16 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
val (runCode, out) = c.run(runPayload(JsObject()))
- runCode should not be (200)
+ if (isTypeScript)
+ out.get.fields should contain key ("error")
+ else
+ runCode should not be (200)
+
}
// Somewhere, the logs should mention an error occurred.
checkStreams(out, err, {
- case (o, e) => (o + e) should include("MODULE_NOT_FOUND")
+ case (o, e) => (o + e) should include regex ("Error|error")
})
}
@@ -365,6 +382,7 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
"""
| function main(args) {
| require('openwhisk');
+ | return { "result": true }
| }
""".stripMargin
@@ -672,7 +690,7 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
checkStreams(out, err, {
case (o, e) =>
(o + e).toLowerCase should include("error")
- (o + e).toLowerCase should include("uncompressing")
+ (o + e).toLowerCase should include regex ("syntax|uncompressing")
})
}
@@ -691,7 +709,7 @@ abstract class NodeJsActionContainerTests extends
BasicActionRunnerTests with Ws
checkStreams(out, err, {
case (o, e) =>
- (o + e).toLowerCase should include("error")
+ (o + e).toLowerCase should include regex ("error|exited")
(o + e).toLowerCase should include("zipped actions must contain either
package.json or index.js at the root.")
})
}
diff --git
a/tests/src/test/scala/runtime/actionContainers/NodeJsConcurrentTests.scala
b/tests/src/test/scala/runtime/actionContainers/NodeJsConcurrentTests.scala
index efc294e..47fc858 100644
--- a/tests/src/test/scala/runtime/actionContainers/NodeJsConcurrentTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJsConcurrentTests.scala
@@ -28,18 +28,19 @@ abstract class NodeJsConcurrentTests extends
NodeJsActionContainerTests {
override def withNodeJsContainer(code: ActionContainer => Unit) =
withActionContainer(Map("__OW_ALLOW_CONCURRENT" -> "true"))(code)
- it should "allow running activations concurrently" in {
+ if (!isTypeScript) {
+ it should "allow running activations concurrently" in {
- val requestCount =
actorSystem.settings.config.getInt("akka.http.host-connection-pool.max-connections")
- require(requestCount > 100, "test requires that max-connections be set >
100")
- println(s"running $requestCount requests")
+ val requestCount =
actorSystem.settings.config.getInt("akka.http.host-connection-pool.max-connections")
+ require(requestCount > 100, "test requires that max-connections be set >
100")
+ println(s"running $requestCount requests")
- val (out, err) = withNodeJsContainer { c =>
- //this action will create a log entry, and only complete once all
activations have arrived and emitted logg
- //this forces all of the in-action logs to appear in a single portion of
the stdout, and all of the sentinels to appear following that
+ val (out, err) = withNodeJsContainer { c =>
+ //this action will create a log entry, and only complete once all
activations have arrived and emitted logg
+ //this forces all of the in-action logs to appear in a single portion
of the stdout, and all of the sentinels to appear following that
- val code =
- s"""
+ val code =
+ s"""
|
| global.count = 0;
| let requestCount = $requestCount;
@@ -69,29 +70,29 @@ abstract class NodeJsConcurrentTests extends
NodeJsActionContainerTests {
| }
""".stripMargin
- c.init(initPayload(code))._1 should be(200)
+ c.init(initPayload(code))._1 should be(200)
- val payloads = (1 to requestCount).map({ i =>
- JsObject(s"arg$i" -> JsString(s"value$i"))
- })
+ val payloads = (1 to requestCount).map({ i =>
+ JsObject(s"arg$i" -> JsString(s"value$i"))
+ })
- val responses = c.runMultiple(payloads.map {
- runPayload(_)
- })
- payloads.foreach { a =>
- responses should contain(200, Some(JsObject("args" -> a)))
+ val responses = c.runMultiple(payloads.map {
+ runPayload(_)
+ })
+ payloads.foreach { a =>
+ responses should contain(200, Some(JsObject("args" -> a)))
+ }
}
- }
- checkStreams(out, err, {
- case (o, e) =>
- o.replaceAll("\n", "") shouldBe "interleave me" * requestCount
- e shouldBe empty
- }, requestCount)
+ checkStreams(out, err, {
+ case (o, e) =>
+ o.replaceAll("\n", "") shouldBe "interleave me" * requestCount
+ e shouldBe empty
+ }, requestCount)
- withClue("expected grouping of stdout sentinels") {
- out should include((ActionContainer.sentinel + "\n") * requestCount)
+ withClue("expected grouping of stdout sentinels") {
+ out should include((ActionContainer.sentinel + "\n") * requestCount)
+ }
}
}
-
}
diff --git
a/tests/src/test/scala/runtime/actionContainers/NodeJsNonConcurrentTests.scala
b/tests/src/test/scala/runtime/actionContainers/NodeJsNonConcurrentTests.scala
index 85a4645..a63a523 100644
---
a/tests/src/test/scala/runtime/actionContainers/NodeJsNonConcurrentTests.scala
+++
b/tests/src/test/scala/runtime/actionContainers/NodeJsNonConcurrentTests.scala
@@ -27,9 +27,10 @@ abstract class NodeJsNonConcurrentTests extends
NodeJsActionContainerTests {
withActionContainer()(code)
it should "NOT allow running activations concurrently (without proper env
setup)" in {
- val (out, err) = withNodeJsContainer { c =>
- //this action will create a log entry, and only complete after 1s, to
guarantee previous is still running
- val code = """
+ if (!isTypeScript) {
+ val (out, err) = withNodeJsContainer { c =>
+ //this action will create a log entry, and only complete after 1s, to
guarantee previous is still running
+ val code = """
| function main(args) {
| console.log("no concurrency");
| return new Promise(function(resolve, reject) {
@@ -40,38 +41,39 @@ abstract class NodeJsNonConcurrentTests extends
NodeJsActionContainerTests {
| }
""".stripMargin
- c.init(initPayload(code))._1 should be(200)
- val requestCount = 2
+ c.init(initPayload(code))._1 should be(200)
+ val requestCount = 2
- val payloads = (1 to requestCount).map({ i =>
- JsObject(s"arg$i" -> JsString(s"value$i"))
- })
+ val payloads = (1 to requestCount).map({ i =>
+ JsObject(s"arg$i" -> JsString(s"value$i"))
+ })
- //run payloads concurrently
- val responses = c.runMultiple(payloads.map {
- runPayload(_)
- })
+ //run payloads concurrently
+ val responses = c.runMultiple(payloads.map {
+ runPayload(_)
+ })
- //one will fail, one will succeed - currently there is no way to
guarantee which one succeeds, since both arrive "at the same time"
- responses.count {
- case (200, Some(JsObject(a))) if a.get("args").isDefined => true
- case _ => false
- } shouldBe 1
+ //one will fail, one will succeed - currently there is no way to
guarantee which one succeeds, since both arrive "at the same time"
+ responses.count {
+ case (200, Some(JsObject(a))) if a.get("args").isDefined => true
+ case _ => false
+ } shouldBe 1
- responses.count {
- case (403, Some(JsObject(e)))
- if e.getOrElse("error", JsString("")) == JsString("System not
ready, status is running.") =>
- true
- case _ => false
- } shouldBe 1
+ responses.count {
+ case (403, Some(JsObject(e)))
+ if e.getOrElse("error", JsString("")) == JsString("System not
ready, status is running.") =>
+ true
+ case _ => false
+ } shouldBe 1
- }
+ }
- checkStreams(out, err, {
- case (o, e) =>
- o.replaceAll("\n", "") shouldBe "no concurrency"
- e shouldBe "Internal system error: System not ready, status is
running."
- }, 1)
+ checkStreams(out, err, {
+ case (o, e) =>
+ o.replaceAll("\n", "") shouldBe "no concurrency"
+ e shouldBe "Internal system error: System not ready, status is
running."
+ }, 1)
+ }
}
}
diff --git
a/tests/src/test/scala/runtime/actionContainers/Typescript37BasicTests.scala
b/tests/src/test/scala/runtime/actionContainers/Typescript37BasicTests.scala
new file mode 100644
index 0000000..4006cde
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/Typescript37BasicTests.scala
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package runtime.actionContainers
+
+import actionContainers.ActionContainer.withContainer
+import actionContainers.{ActionContainer, BasicActionRunnerTests}
+import common.WskActorSystem
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class Typescript37BasicTests extends BasicActionRunnerTests with
WskActorSystem {
+
+ val image = "action-typescript-v3.7"
+
+ override def withActionContainer(env: Map[String, String] = Map.empty)(code:
ActionContainer => Unit) = {
+ withContainer(image, env)(code)
+ }
+
+ def withActionLoopContainer(code: ActionContainer => Unit) =
+ withContainer(image)(code)
+
+ behavior of image
+
+ override val testNoSourceOrExec = TestConfig("")
+
+ override val testNotReturningJson =
+ TestConfig("""
+ |export function main(args) {
+ | return "not a json object"
+ |}
+ """.stripMargin)
+
+ override val testEcho = TestConfig("""|export function main(args) {
+ | console.log("hello stdout")
+ | console.error("hello stderr")
+ | return args
+ |}
+ """.stripMargin)
+
+ override val testUnicode = TestConfig("""|export function main(args) {
+ | let delimiter = args['delimiter']
+ | let msg = delimiter+" ☃ "+delimiter
+ | console.log(msg)
+ | return { "winter": msg }
+ |}
+ """.stripMargin)
+
+ override val testEnv = TestConfig("""|export function main(args) {
+ | let env = process.env
+ | return {
+ | "api_host": env["__OW_API_HOST"],
+ | "api_key": env["__OW_API_KEY"],
+ | "namespace": env["__OW_NAMESPACE"],
+ | "activation_id": env["__OW_ACTIVATION_ID"],
+ | "action_name": env["__OW_ACTION_NAME"],
+ | "deadline": env["__OW_DEADLINE"],
+ | "action_version": env["__OW_ACTION_VERSION"]
+ | }
+ |}
+ """.stripMargin)
+
+ override val testInitCannotBeCalledMoreThanOnce = TestConfig(s"""|export
function main(args) {
+ | return args
+ |}
+ """.stripMargin)
+
+ override val testEntryPointOtherThanMain = TestConfig(
+ s"""|export function niam(args) {
+ | return args
+ |}
+ """.stripMargin,
+ main = "niam")
+
+ override val testLargeInput = TestConfig(s"""|export function main(args) {
+ | return args
+ |}
+ """.stripMargin)
+}
diff --git a/settings.gradle
b/tests/src/test/scala/runtime/actionContainers/Typescript37CommonTests.scala
similarity index 56%
copy from settings.gradle
copy to
tests/src/test/scala/runtime/actionContainers/Typescript37CommonTests.scala
index da79f78..e39e246 100644
--- a/settings.gradle
+++
b/tests/src/test/scala/runtime/actionContainers/Typescript37CommonTests.scala
@@ -15,28 +15,15 @@
* limitations under the License.
*/
-include 'tests'
+package runtime.actionContainers
-include 'core:nodejsActionBase'
-include 'core:nodejs8Action'
-include 'core:nodejs10Action'
-include 'core:nodejs12Action'
-include 'tests:dat:docker:nodejs8docker'
-include 'tests:dat:docker:nodejs10docker'
-include 'tests:dat:docker:nodejs12docker'
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
-rootProject.name = 'runtime-nodejs'
+@RunWith(classOf[JUnitRunner])
+class Typescript37CommonTests extends NodeJsNonConcurrentTests {
-gradle.ext.openwhisk = [
- version: '1.0.0-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
+ override lazy val nodejsContainerImageName = "action-typescript-v3.7"
+ override lazy val nodejsTestDockerImageName = "typescript37docker"
+ override val isTypeScript = true
+}