diff --git a/.travis.yml b/.travis.yml index ddff5a034fe9389dc7c8ea789711a82aa2ad9870..1a45230ed12f78717729a0c9a250475725212704 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ sudo: required group: deprecated-2017Q3 language: scala scala: -- 2.11.8 +- 2.11.11 services: - docker before_install: diff --git a/settings.gradle b/settings.gradle index 90c394073dfccb54d1f1c14bc08d6db8b397b0b6..c7e3ab75780b7cc81c2212dbdbf006acd250a57c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -27,7 +27,7 @@ gradle.ext.openwhisk = [ ] gradle.ext.scala = [ - version: '2.11.8', + version: '2.11.11', compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import'] ] diff --git a/tests/src/test/scala/actionContainers/ActionContainer.scala b/tests/src/test/scala/actionContainers/ActionContainer.scala deleted file mode 100644 index 56aa1314022ec66a970cc8cec88eaaf8607d2d41..0000000000000000000000000000000000000000 --- 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 1471d1218569b192ded3646e85d4b9b4b4e29dce..0000000000000000000000000000000000000000 --- 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 08f46e4f81e6e3160606e8f9242a3162829f0047..0000000000000000000000000000000000000000 --- 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/actionContainers/Python2ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Python2ActionContainerTests.scala similarity index 100% rename from tests/src/test/scala/actionContainers/Python2ActionContainerTests.scala rename to tests/src/test/scala/runtime/actionContainers/Python2ActionContainerTests.scala diff --git a/tests/src/test/scala/actionContainers/PythonActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/PythonActionContainerTests.scala similarity index 98% rename from tests/src/test/scala/actionContainers/PythonActionContainerTests.scala rename to tests/src/test/scala/runtime/actionContainers/PythonActionContainerTests.scala index 0ccce5b94a4e57e4588b632b6d50b85f4ed12d9c..711fdf35e010858f097bf45b42390331587d9105 100644 --- a/tests/src/test/scala/actionContainers/PythonActionContainerTests.scala +++ b/tests/src/test/scala/runtime/actionContainers/PythonActionContainerTests.scala @@ -19,11 +19,12 @@ package runtime.actionContainers import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner -import ActionContainer.withContainer -import ResourceHelpers.{readAsBase64, ZipBuilder} import spray.json.DefaultJsonProtocol._ import spray.json._ import common.WskActorSystem +import actionContainers.{ActionContainer, BasicActionRunnerTests} +import actionContainers.ActionContainer.withContainer +import actionContainers.ResourceHelpers.{readAsBase64, ZipBuilder} import common.TestUtils import java.nio.file.Paths diff --git a/tools/travis/build.sh b/tools/travis/build.sh index f82bbd1b9337a84b7604e79e0aa539390540d185..5b8d1fd91442b079321dfdcf0114c4315a8bd602 100755 --- a/tools/travis/build.sh +++ b/tools/travis/build.sh @@ -44,14 +44,8 @@ docker tag openwhisk/invoker ${IMAGE_PREFIX}/invoker docker pull openwhisk/nodejs6action docker tag openwhisk/nodejs6action ${IMAGE_PREFIX}/nodejs6action -TERM=dumb ./gradlew \ -:common:scala:install \ -:core:controller:install \ -:core:invoker:install \ -:tests:install +TERM=dumb ./gradlew install # Build runtime cd $ROOTDIR -TERM=dumb ./gradlew \ -:core:python2Action:distDocker :core:pythonAction:distDocker \ --PdockerImagePrefix=${IMAGE_PREFIX} +TERM=dumb ./gradlew distDocker -PdockerImagePrefix=${IMAGE_PREFIX}