You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by cs...@apache.org on 2018/03/28 02:41:07 UTC
[incubator-openwhisk-runtime-nodejs] branch master updated: update
nodejs to 8.10.0 and npm openwhisk to 3.14.0 (#31)
This is an automated email from the ASF dual-hosted git repository.
csantanapr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-runtime-nodejs.git
The following commit(s) were added to refs/heads/master by this push:
new fd15ee4 update nodejs to 8.10.0 and npm openwhisk to 3.14.0 (#31)
fd15ee4 is described below
commit fd15ee4e634e80707460d3f07d295dd552940adf
Author: Carlos Santana <cs...@gmail.com>
AuthorDate: Tue Mar 27 22:41:05 2018 -0400
update nodejs to 8.10.0 and npm openwhisk to 3.14.0 (#31)
---
core/nodejs6Action/CHANGELOG.md | 5 +
core/nodejs6Action/Dockerfile | 2 +-
core/nodejs8Action/CHANGELOG.md | 7 +
core/nodejs8Action/Dockerfile | 2 +-
core/nodejs8Action/package.json | 2 +-
.../scala/actionContainers/ActionContainer.scala | 174 -----------
.../ActionProxyContainerTests.scala | 348 ---------------------
.../scala/actionContainers/ResourceHelpers.scala | 190 -----------
.../NodeJs6ActionContainerTests.scala | 28 ++
.../NodeJs8ActionContainerTests.scala | 2 +-
.../NodeJsActionContainerTests.scala | 14 +-
11 files changed, 49 insertions(+), 725 deletions(-)
diff --git a/core/nodejs6Action/CHANGELOG.md b/core/nodejs6Action/CHANGELOG.md
index 079a4a2..16ceabb 100644
--- a/core/nodejs6Action/CHANGELOG.md
+++ b/core/nodejs6Action/CHANGELOG.md
@@ -1,5 +1,10 @@
# NodeJS 6 OpenWhisk Runtime Container
+## 1.7.0
+Change: Update npm openwhisk package
+
+- [openwhisk v3.14.0](https://www.npmjs.com/package/openwhisk) - JavaScript client library for the OpenWhisk platform. Provides a wrapper around the OpenWhisk APIs.
+
## 1.6.0
Change: Update npm openwhisk package
diff --git a/core/nodejs6Action/Dockerfile b/core/nodejs6Action/Dockerfile
index f8eef8c..9e6b834 100644
--- a/core/nodejs6Action/Dockerfile
+++ b/core/nodejs6Action/Dockerfile
@@ -46,7 +46,7 @@ nano@6.2.0 \
node-uuid@1.4.7 \
nodemailer@2.6.4 \
oauth2-server@2.4.1 \
-openwhisk@3.13.1 \
+openwhisk@3.14.0 \
pkgcloud@1.4.0 \
process@0.11.9 \
pug@">=2.0.0-beta6 <2.0.1" \
diff --git a/core/nodejs8Action/CHANGELOG.md b/core/nodejs8Action/CHANGELOG.md
index 2ad64e9..e1b2d11 100644
--- a/core/nodejs8Action/CHANGELOG.md
+++ b/core/nodejs8Action/CHANGELOG.md
@@ -1,5 +1,12 @@
# NodeJS 8 OpenWhisk Runtime Container
+## 1.4.0
+Change: Update nodejs and openwhisk npm package
+
+Node version = 8.10.0
+
+- [openwhisk v3.14.0](https://www.npmjs.com/package/openwhisk) - JavaScript client library for the OpenWhisk platform. Provides a wrapper around the OpenWhisk APIs.
+
## 1.3.0
Change: Update npm openwhisk package
diff --git a/core/nodejs8Action/Dockerfile b/core/nodejs8Action/Dockerfile
index 43eafae..31dea72 100644
--- a/core/nodejs8Action/Dockerfile
+++ b/core/nodejs8Action/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:8.9.3
+FROM node:8.10.0
RUN apt-get update && apt-get install -y \
imagemagick \
unzip \
diff --git a/core/nodejs8Action/package.json b/core/nodejs8Action/package.json
index 7eab06b..9eb2d84 100644
--- a/core/nodejs8Action/package.json
+++ b/core/nodejs8Action/package.json
@@ -8,7 +8,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "openwhisk": "3.13.1",
+ "openwhisk": "3.14.0",
"body-parser": "1.18.2",
"express": "4.16.2"
}
diff --git a/tests/src/test/scala/actionContainers/ActionContainer.scala b/tests/src/test/scala/actionContainers/ActionContainer.scala
deleted file mode 100644
index 56aa131..0000000
--- a/tests/src/test/scala/actionContainers/ActionContainer.scala
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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 java.io.ByteArrayOutputStream
-import java.io.File
-import java.io.PrintWriter
-
-import scala.concurrent.Await
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.concurrent.Future
-import scala.concurrent.blocking
-import scala.concurrent.duration.Duration
-import scala.concurrent.duration.DurationInt
-import scala.language.postfixOps
-import scala.sys.process.ProcessLogger
-import scala.sys.process.stringToProcess
-import scala.util.Random
-
-import org.apache.commons.lang3.StringUtils
-import org.scalatest.FlatSpec
-import org.scalatest.Matchers
-
-import akka.actor.ActorSystem
-import common.WhiskProperties
-import spray.json._
-import whisk.core.entity.Exec
-
-/**
- * For testing convenience, this interface abstracts away the REST calls to a
- * container as blocking method calls of this interface.
- */
-trait ActionContainer {
- def init(value: JsValue): (Int, Option[JsObject])
- def run(value: JsValue): (Int, Option[JsObject])
-}
-
-trait ActionProxyContainerTestUtils extends FlatSpec with Matchers {
- import ActionContainer.{filterSentinel, sentinel}
-
- def initPayload(code: String, main: String = "main") = {
- JsObject(
- "value" -> JsObject(
- "code" -> { if (code != null) JsString(code) else JsNull },
- "main" -> JsString(main),
- "binary" -> JsBoolean(Exec.isBinaryCode(code))))
- }
-
- def runPayload(args: JsValue, other: Option[JsObject] = None) = {
- JsObject(Map("value" -> args) ++ (other map { _.fields } getOrElse Map()))
- }
-
- def checkStreams(out: String, err: String, additionalCheck: (String, String) => Unit, sentinelCount: Int = 1) = {
- withClue("expected number of stdout sentinels") {
- sentinelCount shouldBe StringUtils.countMatches(out, sentinel)
- }
- withClue("expected number of stderr sentinels") {
- sentinelCount shouldBe StringUtils.countMatches(err, sentinel)
- }
-
- val (o, e) = (filterSentinel(out), filterSentinel(err))
- o should not include (sentinel)
- e should not include (sentinel)
- additionalCheck(o, e)
- }
-}
-
-object ActionContainer {
- private lazy val dockerBin: String = {
- List("/usr/bin/docker", "/usr/local/bin/docker")
- .find { bin =>
- new File(bin).isFile()
- }
- .getOrElse(???) // This fails if the docker binary couldn't be located.
- }
-
- private lazy val dockerCmd: String = {
- val version = WhiskProperties.getProperty("whisk.version.name")
- // Check if we are running on docker-machine env.
- val hostStr = if (version.toLowerCase().contains("mac")) {
- s" --host tcp://${WhiskProperties.getMainDockerEndpoint()} "
- } else {
- " "
- }
- s"$dockerBin $hostStr"
- }
-
- private def docker(command: String): String = s"$dockerCmd $command"
-
- // Runs a process asynchronously. Returns a future with (exitCode,stdout,stderr)
- private def proc(cmd: String): Future[(Int, String, String)] = Future {
- blocking {
- val out = new ByteArrayOutputStream
- val err = new ByteArrayOutputStream
- val outW = new PrintWriter(out)
- val errW = new PrintWriter(err)
- val v = cmd ! (ProcessLogger(outW.println, errW.println))
- outW.close()
- errW.close()
- (v, out.toString, err.toString)
- }
- }
-
- // Tying it all together, we have a method that runs docker, waits for
- // completion for some time then returns the exit code, the output stream
- // and the error stream.
- private def awaitDocker(cmd: String, t: Duration): (Int, String, String) = {
- Await.result(proc(docker(cmd)), t)
- }
-
- // Filters out the sentinel markers inserted by the container (see relevant private code in Invoker.scala)
- val sentinel = "XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX"
- def filterSentinel(str: String) = str.replaceAll(sentinel, "").trim
-
- def withContainer(imageName: String, environment: Map[String, String] = Map.empty)(code: ActionContainer => Unit)(
- implicit actorSystem: ActorSystem): (String, String) = {
- val rand = { val r = Random.nextInt; if (r < 0) -r else r }
- val name = imageName.toLowerCase.replaceAll("""[^a-z]""", "") + rand
- val envArgs = environment.toSeq.map {
- case (k, v) => s"-e ${k}=${v}"
- } mkString (" ")
-
- // We create the container...
- val runOut = awaitDocker(s"run --name $name $envArgs -d $imageName", 10 seconds)
- assert(runOut._1 == 0, "'docker run' did not exit with 0: " + runOut)
-
- // ...find out its IP address...
- val ipOut = awaitDocker(s"""inspect --format '{{.NetworkSettings.IPAddress}}' $name""", 10 seconds)
- assert(ipOut._1 == 0, "'docker inspect did not exit with 0")
- val ip = ipOut._2.replaceAll("""[^0-9.]""", "")
-
- // ...we create an instance of the mock container interface...
- val mock = new ActionContainer {
- def init(value: JsValue) = syncPost(ip, 8080, "/init", value)
- def run(value: JsValue) = syncPost(ip, 8080, "/run", value)
- }
-
- try {
- // ...and finally run the code with it.
- code(mock)
- // I'm told this is good for the logs.
- Thread.sleep(100)
- val (_, out, err) = awaitDocker(s"logs $name", 10 seconds)
- (out, err)
- } finally {
- awaitDocker(s"kill $name", 10 seconds)
- awaitDocker(s"rm $name", 10 seconds)
- }
- }
-
- private def syncPost(host: String, port: Int, endPoint: String, content: JsValue): (Int, Option[JsObject]) = {
- whisk.core.containerpool.HttpUtils.post(host, port, endPoint, content)
- }
-
- private class ActionContainerImpl() extends ActionContainer {
- override def init(value: JsValue) = ???
- override def run(value: JsValue) = ???
- }
-}
diff --git a/tests/src/test/scala/actionContainers/ActionProxyContainerTests.scala b/tests/src/test/scala/actionContainers/ActionProxyContainerTests.scala
deleted file mode 100644
index 1471d12..0000000
--- a/tests/src/test/scala/actionContainers/ActionProxyContainerTests.scala
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * 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 java.io.File
-import java.util.Base64
-
-import org.apache.commons.io.FileUtils
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-
-import ActionContainer.withContainer
-import common.TestUtils
-import common.WskActorSystem
-import spray.json.DefaultJsonProtocol._
-import spray.json._
-
-@RunWith(classOf[JUnitRunner])
-class ActionProxyContainerTests extends BasicActionRunnerTests with WskActorSystem {
-
- override def withActionContainer(env: Map[String, String] = Map.empty)(code: ActionContainer => Unit) = {
- withContainer("dockerskeleton", env)(code)
- }
-
- val codeNotReturningJson = """
- |#!/bin/sh
- |echo not a json object
- """.stripMargin.trim
-
- /** Standard code samples, should print 'hello' to stdout and echo the input args. */
- val stdCodeSamples = {
- val bash = """
- |#!/bin/bash
- |echo 'hello stdout'
- |echo 'hello stderr' 1>&2
- |if [[ -z $1 || $1 == '{}' ]]; then
- | echo '{ "msg": "Hello from bash script!" }'
- |else
- | echo $1 # echo the arguments back as the result
- |fi
- """.stripMargin.trim
-
- val python = """
- |#!/usr/bin/env python
- |from __future__ import print_function
- |import sys
- |print('hello stdout')
- |print('hello stderr', file=sys.stderr)
- |print(sys.argv[1])
- """.stripMargin.trim
-
- val perl = """
- |#!/usr/bin/env perl
- |print STDOUT "hello stdout\n";
- |print STDERR "hello stderr\n";
- |print $ARGV[0];
- """.stripMargin.trim
-
- // excluding perl as it not installed in alpine based image
- Seq(("bash", bash), ("python", python))
- }
-
- val stdUnicodeSamples = {
- // python 3 in base image
- val python = """
- |#!/usr/bin/env python
- |import json, sys
- |j = json.loads(sys.argv[1])
- |sep = j["delimiter"]
- |s = sep + " ☃ " + sep
- |print(s)
- |print(json.dumps({"winter": s}))
- """.stripMargin.trim
-
- Seq(("python", python))
- }
-
- /** Standard code samples, should print 'hello' to stdout and echo the input args. */
- val stdEnvSamples = {
- val bash = """
- |#!/bin/bash
- |echo "{ \
- |\"api_host\": \"$__OW_API_HOST\", \"api_key\": \"$__OW_API_KEY\", \
- |\"namespace\": \"$__OW_NAMESPACE\", \"action_name\": \"$__OW_ACTION_NAME\", \
- |\"activation_id\": \"$__OW_ACTIVATION_ID\", \"deadline\": \"$__OW_DEADLINE\" }"
- """.stripMargin.trim
-
- val python =
- """
- |#!/usr/bin/env python
- |import os
- |
- |print('{ "api_host": "%s", "api_key": "%s", "namespace": "%s", "action_name" : "%s", "activation_id": "%s", "deadline": "%s" }' % (
- | os.environ['__OW_API_HOST'], os.environ['__OW_API_KEY'],
- | os.environ['__OW_NAMESPACE'], os.environ['__OW_ACTION_NAME'],
- | os.environ['__OW_ACTIVATION_ID'], os.environ['__OW_DEADLINE']))
- """.stripMargin.trim
-
- val perl =
- """
- |#!/usr/bin/env perl
- |$a = $ENV{'__OW_API_HOST'};
- |$b = $ENV{'__OW_API_KEY'};
- |$c = $ENV{'__OW_NAMESPACE'};
- |$d = $ENV{'__OW_ACTION_NAME'};
- |$e = $ENV{'__OW_ACTIVATION_ID'};
- |$f = $ENV{'__OW_DEADLINE'};
- |print "{ \"api_host\": \"$a\", \"api_key\": \"$b\", \"namespace\": \"$c\", \"action_name\": \"$d\", \"activation_id\": \"$e\", \"deadline\": \"$f\" }";
- """.stripMargin.trim
-
- // excluding perl as it not installed in alpine based image
- Seq(("bash", bash), ("python", python))
- }
-
- behavior of "openwhisk/dockerskeleton"
-
- it should "run sample without init" in {
- val (out, err) = withActionContainer() { c =>
- val (runCode, out) = c.run(JsObject())
- runCode should be(200)
- out should be(Some(JsObject("error" -> JsString("This is a stub action. Replace it with custom logic."))))
- }
-
- checkStreams(out, err, {
- case (o, _) => o should include("This is a stub action")
- })
- }
-
- it should "run sample with 'null' init" in {
- val (out, err) = withActionContainer() { c =>
- val (initCode, _) = c.init(initPayload(null))
- initCode should be(200)
-
- val (runCode, out) = c.run(JsObject())
- runCode should be(200)
- out should be(Some(JsObject("error" -> JsString("This is a stub action. Replace it with custom logic."))))
- }
-
- checkStreams(out, err, {
- case (o, _) => o should include("This is a stub action")
- })
- }
-
- it should "run sample with init that does nothing" in {
- val (out, err) = withActionContainer() { c =>
- val (initCode, _) = c.init(JsObject())
- initCode should be(200)
- val (runCode, out) = c.run(JsObject())
- runCode should be(200)
- out should be(Some(JsObject("error" -> JsString("This is a stub action. Replace it with custom logic."))))
- }
-
- checkStreams(out, err, {
- case (o, _) => o should include("This is a stub action")
- })
- }
-
- it should "respond with 404 for bad run argument" in {
- val (out, err) = withActionContainer() { c =>
- val (runCode, out) = c.run(runPayload(JsString("A")))
- runCode should be(404)
- }
-
- checkStreams(out, err, {
- case (o, e) =>
- o shouldBe empty
- e shouldBe empty
- })
- }
-
- it should "fail to run a bad script" in {
- val (out, err) = withActionContainer() { c =>
- val (initCode, _) = c.init(initPayload(""))
- initCode should be(200)
- val (runCode, out) = c.run(JsNull)
- runCode should be(502)
- out should be(Some(JsObject("error" -> JsString("The action did not return a dictionary."))))
- }
-
- checkStreams(out, err, {
- case (o, _) => o should include("error")
- })
- }
-
- it should "extract and run a compatible zip exec" in {
- val zip = FileUtils.readFileToByteArray(new File(TestUtils.getTestActionFilename("blackbox.zip")))
- val contents = Base64.getEncoder.encodeToString(zip)
-
- val (out, err) = withActionContainer() { c =>
- val (initCode, err) =
- c.init(JsObject("value" -> JsObject("code" -> JsString(contents), "binary" -> JsBoolean(true))))
- initCode should be(200)
- val (runCode, out) = c.run(JsObject())
- runCode should be(200)
- out.get should be(JsObject("msg" -> JsString("hello zip")))
- }
-
- checkStreams(out, err, {
- case (o, e) =>
- o shouldBe "This is an example zip used with the docker skeleton action."
- e shouldBe empty
- })
- }
-
- testNotReturningJson(codeNotReturningJson, checkResultInLogs = true)
- testEcho(stdCodeSamples)
- testUnicode(stdUnicodeSamples)
- testEnv(stdEnvSamples)
-}
-
-trait BasicActionRunnerTests extends ActionProxyContainerTestUtils {
- def withActionContainer(env: Map[String, String] = Map.empty)(code: ActionContainer => Unit): (String, String)
-
- /**
- * Runs tests for actions which do not return a dictionary and confirms expected error messages.
- * @param codeNotReturningJson code to execute, should not return a JSON object
- * @param checkResultInLogs should be true iff the result of the action is expected to appear in stdout or stderr
- */
- def testNotReturningJson(codeNotReturningJson: String, checkResultInLogs: Boolean = true) = {
- it should "run and report an error for script not returning a json object" in {
- val (out, err) = withActionContainer() { c =>
- val (initCode, _) = c.init(initPayload(codeNotReturningJson))
- initCode should be(200)
- val (runCode, out) = c.run(JsObject())
- runCode should be(502)
- out should be(Some(JsObject("error" -> JsString("The action did not return a dictionary."))))
- }
-
- checkStreams(out, err, {
- case (o, e) =>
- if (checkResultInLogs) {
- (o + e) should include("not a json object")
- } else {
- o shouldBe empty
- e shouldBe empty
- }
- })
- }
- }
-
- /**
- * Runs tests for code samples which are expected to echo the input arguments
- * and print hello [stdout, stderr].
- */
- def testEcho(stdCodeSamples: Seq[(String, String)]) = {
- stdCodeSamples.foreach { s =>
- it should s"run a ${s._1} script" in {
- val argss = List(
- JsObject("string" -> JsString("hello")),
- JsObject("string" -> JsString("❄ ☃ ❄")),
- JsObject("numbers" -> JsArray(JsNumber(42), JsNumber(1))),
- // JsObject("boolean" -> JsBoolean(true)), // fails with swift3 returning boolean: 1
- JsObject("object" -> JsObject("a" -> JsString("A"))))
-
- val (out, err) = withActionContainer() { c =>
- val (initCode, _) = c.init(initPayload(s._2))
- initCode should be(200)
-
- for (args <- argss) {
- val (runCode, out) = c.run(runPayload(args))
- runCode should be(200)
- out should be(Some(args))
- }
- }
-
- checkStreams(out, err, {
- case (o, e) =>
- o should include("hello stdout")
- e should include("hello stderr")
- }, argss.length)
- }
- }
- }
-
- def testUnicode(stdUnicodeSamples: Seq[(String, String)]) = {
- stdUnicodeSamples.foreach { s =>
- it should s"run a ${s._1} action and handle unicode in source, input params, logs, and result" in {
- val (out, err) = withActionContainer() { c =>
- val (initCode, _) = c.init(initPayload(s._2))
- initCode should be(200)
-
- val (runCode, runRes) = c.run(runPayload(JsObject("delimiter" -> JsString("❄"))))
- runRes.get.fields.get("winter") shouldBe Some(JsString("❄ ☃ ❄"))
- }
-
- checkStreams(out, err, {
- case (o, _) =>
- o.toLowerCase should include("❄ ☃ ❄")
- })
- }
- }
- }
-
- /** Runs tests for code samples which are expected to return the expected standard environment {auth, edge}. */
- def testEnv(stdEnvSamples: Seq[(String, String)],
- enforceEmptyOutputStream: Boolean = true,
- enforceEmptyErrorStream: Boolean = true) = {
- stdEnvSamples.foreach { s =>
- it should s"run a ${s._1} script and confirm expected environment variables" in {
- val props = Seq(
- "api_host" -> "xyz",
- "api_key" -> "abc",
- "namespace" -> "zzz",
- "action_name" -> "xxx",
- "activation_id" -> "iii",
- "deadline" -> "123")
- val env = props.map { case (k, v) => s"__OW_${k.toUpperCase()}" -> v }
-
- val (out, err) = withActionContainer(env.take(1).toMap) { c =>
- val (initCode, _) = c.init(initPayload(s._2))
- initCode should be(200)
-
- val (runCode, out) = c.run(runPayload(JsObject(), Some(props.toMap.toJson.asJsObject)))
- runCode should be(200)
- out shouldBe defined
- props.map {
- case (k, v) =>
- withClue(k) {
- out.get.fields(k) shouldBe JsString(v)
- }
-
- }
- }
-
- checkStreams(out, err, {
- case (o, e) =>
- if (enforceEmptyOutputStream) o shouldBe empty
- if (enforceEmptyErrorStream) e shouldBe empty
- })
- }
- }
- }
-}
diff --git a/tests/src/test/scala/actionContainers/ResourceHelpers.scala b/tests/src/test/scala/actionContainers/ResourceHelpers.scala
deleted file mode 100644
index 08f46e4..0000000
--- a/tests/src/test/scala/actionContainers/ResourceHelpers.scala
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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 java.net.URI
-import java.net.URLClassLoader
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.Paths
-import java.nio.file.SimpleFileVisitor
-import java.nio.file.FileVisitResult
-import java.nio.file.FileSystems
-import java.nio.file.attribute.BasicFileAttributes
-import java.nio.charset.StandardCharsets
-import java.util.Base64
-
-import javax.tools.ToolProvider
-
-import collection.JavaConverters._
-
-/**
- * A collection of utility objects to create ephemeral action resources based
- * on file contents.
- */
-object ResourceHelpers {
-
- /** Creates a zip file based on the contents of a top-level directory. */
- object ZipBuilder {
- def mkBase64Zip(sources: Seq[(Seq[String], String)]): String = {
- val (tmpDir, _) = writeSourcesToTempDirectory(sources)
- val archive = makeZipFromDir(tmpDir)
- readAsBase64(archive)
- }
- }
-
- /**
- * A convenience object to compile and package Java sources into a JAR, and to
- * encode that JAR as a base 64 string. The compilation options include the
- * current classpath, which is why Google GSON is readily available (though not
- * packaged in the JAR).
- */
- object JarBuilder {
- def mkBase64Jar(sources: Seq[(Seq[String], String)]): String = {
- // Note that this pipeline doesn't delete any of the temporary files.
- val binDir = compile(sources)
- val jarPath = makeJarFromDir(binDir)
- val base64 = readAsBase64(jarPath)
- base64
- }
-
- def mkBase64Jar(source: (Seq[String], String)): String = {
- mkBase64Jar(Seq(source))
- }
-
- private def compile(sources: Seq[(Seq[String], String)]): Path = {
- require(!sources.isEmpty)
-
- // The absolute paths of the source file
- val (srcDir, srcAbsPaths) = writeSourcesToTempDirectory(sources)
-
- // A temporary directory for the destination files.
- val binDir = Files.createTempDirectory("bin").toAbsolutePath()
-
- // Preparing the compiler
- val compiler = ToolProvider.getSystemJavaCompiler()
- val fileManager = compiler.getStandardFileManager(null, null, StandardCharsets.UTF_8)
-
- // Collecting all files to be compiled
- val compUnit = fileManager.getJavaFileObjectsFromFiles(srcAbsPaths.map(_.toFile).asJava)
-
- // Setting the options
- val compOptions = Seq("-d", binDir.toAbsolutePath().toString(), "-classpath", buildClassPath())
- val compTask = compiler.getTask(null, fileManager, null, compOptions.asJava, null, compUnit)
-
- // ...and off we go.
- compTask.call()
-
- binDir
- }
-
- private def buildClassPath(): String = {
- val bcp = System.getProperty("java.class.path")
-
- val list = this.getClass().getClassLoader() match {
- case ucl: URLClassLoader =>
- bcp :: ucl.getURLs().map(_.getFile().toString()).toList
-
- case _ =>
- List(bcp)
- }
-
- list.mkString(System.getProperty("path.separator"))
- }
- }
-
- /**
- * Creates a temporary directory and reproduces the desired file structure
- * in it. Returns the path of the temporary directory and the path of each
- * file as represented in it.
- */
- private def writeSourcesToTempDirectory(sources: Seq[(Seq[String], String)]): (Path, Seq[Path]) = {
- // A temporary directory for the source files.
- val srcDir = Files.createTempDirectory("src").toAbsolutePath()
-
- val srcAbsPaths = for ((sourceName, sourceContent) <- sources) yield {
- // The relative path of the source file
- val srcRelPath = Paths.get(sourceName.head, sourceName.tail: _*)
- // The absolute path of the source file
- val srcAbsPath = srcDir.resolve(srcRelPath)
- // Create parent directories if needed.
- Files.createDirectories(srcAbsPath.getParent)
- // Writing contents
- Files.write(srcAbsPath, sourceContent.getBytes(StandardCharsets.UTF_8))
-
- srcAbsPath
- }
-
- (srcDir, srcAbsPaths)
- }
-
- private def makeZipFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".zip")
-
- private def makeJarFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".jar")
-
- /**
- * Compresses all files beyond a directory into a zip file.
- * Note that Jar files are just zip files.
- */
- private def makeArchiveFromDir(dir: Path, extension: String): Path = {
- // Any temporary file name for the archive.
- val arPath = Files.createTempFile("output", extension).toAbsolutePath()
-
- // We "mount" it as a filesystem, so we can just copy files into it.
- val dstUri = new URI("jar:" + arPath.toUri().getScheme(), arPath.toAbsolutePath().toString(), null)
- // OK, that's a hack. Doing this because newFileSystem wants to create that file.
- arPath.toFile().delete()
- val fs = FileSystems.newFileSystem(dstUri, Map(("create" -> "true")).asJava)
-
- // Traversing all files in the bin directory...
- Files.walkFileTree(
- dir,
- new SimpleFileVisitor[Path]() {
- override def visitFile(path: Path, attributes: BasicFileAttributes) = {
- // The path relative to the src dir
- val relPath = dir.relativize(path)
-
- // The corresponding path in the zip
- val arRelPath = fs.getPath(relPath.toString())
-
- // If this file is not top-level in the src dir...
- if (relPath.getParent() != null) {
- // ...create the directory structure if it doesn't exist.
- if (!Files.exists(arRelPath.getParent())) {
- Files.createDirectories(arRelPath.getParent())
- }
- }
-
- // Finally we can copy that file.
- Files.copy(path, arRelPath)
-
- FileVisitResult.CONTINUE
- }
- })
-
- fs.close()
-
- arPath
- }
-
- /** Reads the contents of a (possibly binary) file into a base64-encoded String */
- def readAsBase64(path: Path): String = {
- val encoder = Base64.getEncoder()
- new String(encoder.encode(Files.readAllBytes(path)), StandardCharsets.UTF_8)
- }
-}
diff --git a/tests/src/test/scala/runtime/actionContainers/NodeJs6ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJs6ActionContainerTests.scala
new file mode 100644
index 0000000..78f064e
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJs6ActionContainerTests.scala
@@ -0,0 +1,28 @@
+/*
+ * 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 org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class NodeJs6ActionContainerTests extends NodeJsActionContainerTests {
+
+ override lazy val nodejsContainerImageName = "nodejs6action"
+
+}
diff --git a/tests/src/test/scala/actionContainers/NodeJs8ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJs8ActionContainerTests.scala
similarity index 98%
rename from tests/src/test/scala/actionContainers/NodeJs8ActionContainerTests.scala
rename to tests/src/test/scala/runtime/actionContainers/NodeJs8ActionContainerTests.scala
index 8727a7f..ec57018 100644
--- a/tests/src/test/scala/actionContainers/NodeJs8ActionContainerTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJs8ActionContainerTests.scala
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package actionContainers
+package runtime.actionContainers
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
diff --git a/tests/src/test/scala/actionContainers/NodeJsActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
similarity index 98%
rename from tests/src/test/scala/actionContainers/NodeJsActionContainerTests.scala
rename to tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
index be45a52..8898e22 100644
--- a/tests/src/test/scala/actionContainers/NodeJsActionContainerTests.scala
+++ b/tests/src/test/scala/runtime/actionContainers/NodeJsActionContainerTests.scala
@@ -15,19 +15,15 @@
* limitations under the License.
*/
-package actionContainers
-
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-
-import ActionContainer.withContainer
-import ResourceHelpers.ZipBuilder
+package runtime.actionContainers
import common.WskActorSystem
+import actionContainers.{ActionContainer, BasicActionRunnerTests}
+import actionContainers.ActionContainer.withContainer
+import actionContainers.ResourceHelpers.ZipBuilder
import spray.json._
-@RunWith(classOf[JUnitRunner])
-class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSystem {
+abstract class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSystem {
lazy val nodejsContainerImageName = "nodejs6action"
--
To stop receiving notification emails like this one, please contact
csantanapr@apache.org.