diff --git a/LICENSE.txt b/LICENSE.txt index 933501710f8..98beadd8ea8 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -213,6 +213,13 @@ Spray Caching 1.3.4 (io.spray:spray-caching_2.11:1.3.4 - http://spray.io/documen License included at licenses/LICENSE-spray.txt, or https://github.com/spray/spray/blob/master/LICENSE Copyright (C) 2011-2015 the spray project -This product bundles the files gradlew and gradlew.bat from Gradle v5.5 +The following code is adapted from Akka Streams 2.5.6 +(com.typesafe.akka:akka-stream_2.12:2.5.6 - https://akka.io/): + OwSink.combine in common/scala/src/main/scala/org/apache/openwhisk/core/containerpool/logging/ + DockerToActivationFileLogStore.scala is adapted from Akka Streams' Sink.combine. + Copyright (C) 2009-2020 Lightbend Inc. + Licensed under the Apache License, Version 2.0. + +This product bundles the files gradlew and gradlew.bat from Gradle v6.9.1 which are distributed under the Apache License, Version 2.0. For details see ./gradlew and ./gradlew.bat. diff --git a/common/scala/src/main/scala/org/apache/openwhisk/core/containerpool/logging/DockerToActivationFileLogStore.scala b/common/scala/src/main/scala/org/apache/openwhisk/core/containerpool/logging/DockerToActivationFileLogStore.scala index 08663db42cd..a35ea7a19ea 100644 --- a/common/scala/src/main/scala/org/apache/openwhisk/core/containerpool/logging/DockerToActivationFileLogStore.scala +++ b/common/scala/src/main/scala/org/apache/openwhisk/core/containerpool/logging/DockerToActivationFileLogStore.scala @@ -170,7 +170,8 @@ object OwSink { /** * Combines two sinks into one sink using the given strategy. The materialized value is a Tuple2 of the materialized - * values of either sink. Code basically copied from {@code Sink.combine} + * values of either sink. Adapted from Akka Streams 2.5.6's {@code Sink.combine}. + * Copyright (C) 2009-2020 Lightbend Inc. */ def combine[T, U, M1, M2](first: Sink[U, M1], second: Sink[U, M2])( strategy: Int => Graph[UniformFanOutShape[T, U], NotUsed]): Sink[T, (M1, M2)] = { diff --git a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/Unzip.scala b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/Unzip.scala index ea5136d49e8..902e93fef60 100644 --- a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/Unzip.scala +++ b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/Unzip.scala @@ -17,31 +17,42 @@ package org.apache.openwhisk.standalone -import java.io.{File, FileOutputStream, InputStream} +import java.io.{File, InputStream} +import java.nio.file.Files import java.util.zip.ZipInputStream object Unzip { def apply(is: InputStream, dir: File): Unit = { - //Based on https://stackoverflow.com/a/40547896/1035417 - val zis = new ZipInputStream((is)) + val zis = new ZipInputStream(is) val dest = dir.toPath - Stream.continually(zis.getNextEntry).takeWhile(_ != null).foreach { zipEntry => - if (!zipEntry.isDirectory) { + val buffer = new Array[Byte](4096) + + try { + var zipEntry = zis.getNextEntry + while (zipEntry != null) { val outPath = dest.resolve(zipEntry.getName) - val outPathParent = outPath.getParent - if (!outPathParent.toFile.exists()) { - outPathParent.toFile.mkdirs() + if (zipEntry.isDirectory) { + Files.createDirectories(outPath) + } else { + Files.createDirectories(outPath.getParent) + val out = Files.newOutputStream(outPath) + try { + var bytesRead = zis.read(buffer) + while (bytesRead != -1) { + out.write(buffer, 0, bytesRead) + bytesRead = zis.read(buffer) + } + } finally { + out.close() + } } - - val outFile = outPath.toFile - val out = new FileOutputStream(outFile) - val buffer = new Array[Byte](4096) - Stream.continually(zis.read(buffer)).takeWhile(_ != -1).foreach(out.write(buffer, 0, _)) - out.close() + zis.closeEntry() + zipEntry = zis.getNextEntry } + } finally { + zis.close() } - zis.close() } } diff --git a/core/standalone/src/test/scala/org/apache/openwhisk/standalone/UnzipTests.scala b/core/standalone/src/test/scala/org/apache/openwhisk/standalone/UnzipTests.scala new file mode 100644 index 00000000000..07ccc1000a8 --- /dev/null +++ b/core/standalone/src/test/scala/org/apache/openwhisk/standalone/UnzipTests.scala @@ -0,0 +1,60 @@ +/* + * 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 org.apache.openwhisk.standalone + +import java.io.{ByteArrayInputStream, ByteArrayOutputStream} +import java.nio.charset.StandardCharsets.UTF_8 +import java.nio.file.Files +import java.util.zip.{ZipEntry, ZipOutputStream} + +import org.apache.commons.io.FileUtils +import org.junit.runner.RunWith +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class UnzipTests extends AnyFlatSpec with Matchers { + behavior of "Unzip" + + it should "extract nested files from a zip stream" in { + val zipBytes = new ByteArrayOutputStream() + val zip = new ZipOutputStream(zipBytes) + val entries = Seq("provisioning/datasources/datasource.yml" -> "apiVersion: 1\n", "dashboard.json" -> "{}\n") + + entries.foreach { + case (name, contents) => + zip.putNextEntry(new ZipEntry(name)) + zip.write(contents.getBytes(UTF_8)) + zip.closeEntry() + } + zip.close() + + val testRoot = Files.createDirectories(java.nio.file.Paths.get("build/tmp")) + val destination = Files.createTempDirectory(testRoot, "unzip-test-") + try { + Unzip(new ByteArrayInputStream(zipBytes.toByteArray), destination.toFile) + + new String(Files.readAllBytes(destination.resolve("provisioning/datasources/datasource.yml")), UTF_8) shouldBe + "apiVersion: 1\n" + new String(Files.readAllBytes(destination.resolve("dashboard.json")), UTF_8) shouldBe "{}\n" + } finally { + FileUtils.deleteDirectory(destination.toFile) + } + } +} diff --git a/tools/admin/src/main/scala/org/apache/openwhisk/core/cli/Main.scala b/tools/admin/src/main/scala/org/apache/openwhisk/core/cli/Main.scala index 4a1e3b94afd..586daba5857 100644 --- a/tools/admin/src/main/scala/org/apache/openwhisk/core/cli/Main.scala +++ b/tools/admin/src/main/scala/org/apache/openwhisk/core/cli/Main.scala @@ -153,13 +153,9 @@ object Main { } private def printErr(message: String): Unit = { - //Taken from ScallopConf - if (overrideColorOutput.value.getOrElse(System.console() != null)) { - Console.err.println("[\u001b[31m%s\u001b[0m] Error: %s" format (printedName, message)) - } else { - // no colors on output - Console.err.println("[%s] Error: %s" format (printedName, message)) - } + val useColor = overrideColorOutput.value.getOrElse(System.console() != null) + val name = if (useColor) s"\u001b[31m${Main.printedName}\u001b[0m" else Main.printedName + Console.err.println(s"[$name] Error: $message") } }