You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ra...@apache.org on 2019/11/01 14:06:55 UTC
[openwhisk] 02/02: Install and launch the Playground UI at startup.
This is an automated email from the ASF dual-hosted git repository.
rabbah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk.git
commit 7062549035eb45f376653ce2433fcbe70e6fd806
Author: Chetan Mehrotra <ch...@apache.org>
AuthorDate: Fri Oct 4 17:11:24 2019 +0530
Install and launch the Playground UI at startup.
- Launch playground by default. Use `--no-ui` to opt out
- Disable auto bootstrap of user and action when using external ArtifactStores
- Document the `--enable-bootstrap` flag when using external stores
- Add playground UI screenshot
- Disable playground for all tests by default
- Prepull default images before the UI is launched
- Do prepull for nightly image even if they are present
---
core/standalone/README.md | 69 +++++++----
.../resources/playground/ui/playgroundFunctions.js | 2 +-
.../openwhisk/core/ExecManifestSupport.scala | 30 +++++
.../openwhisk/standalone/PlaygroundLauncher.scala | 128 +++++++++++++++++++++
.../standalone/StandaloneDockerSupport.scala | 12 ++
.../openwhisk/standalone/StandaloneOpenWhisk.scala | 76 +++++++++---
.../org/apache/openwhisk/standalone/Wsk.scala | 81 +++++++++++++
docs/images/playground-ui.png | Bin 0 -> 62459 bytes
.../standalone/StandaloneServerFixture.scala | 4 +
9 files changed, 365 insertions(+), 37 deletions(-)
diff --git a/core/standalone/README.md b/core/standalone/README.md
index 7ec4178..d7d7456 100644
--- a/core/standalone/README.md
+++ b/core/standalone/README.md
@@ -26,8 +26,12 @@ executed as a normal java application from command line.
java -jar openwhisk-standalone.jar
```
-This should start the OpenWhisk server on port 3233 by default. Once the server is started then [configure the cli][1]
-and then try out the [samples][2].
+This should start the OpenWhisk server on port 3233 by default and launch a Playground UI at port 3232.
+
+![Playground UI](../../docs/images/playground-ui.png)
+
+The Playground UI can be used to try out simple actions. To make use of all OpenWhisk features [configure the cli][1] and
+then try out the [samples][2].
This server by default uses a memory based store and does not depend on any other external service like Kafka and CouchDB.
It only needs Docker and Java to for running.
@@ -75,24 +79,25 @@ $ java -jar openwhisk-standalone.jar -h
\ \ / \/ \___/| .__/ \___|_| |_|__/\__|_| |_|_|___/_|\_\
\___\/ tm |_|
- -m, --manifest <arg> Manifest json defining the supported runtimes
- -c, --config-file <arg> application.conf which overrides the default
- standalone.conf
- --api-gw Enable API Gateway support
- --couchdb Enable CouchDB support
- --user-events Enable User Events along with Prometheus and
- Grafana
- --kafka Enable embedded Kafka support
- --kafka-ui Enable Kafka UI
-
- --all Enables all the optional services supported
- by Standalone OpenWhisk like CouchDB, Kafka
- etc
- --api-gw-port <arg> API Gateway Port
- --clean Clean any existing state like database
- -d, --data-dir <arg> Directory used for storage
- --dev-kcf Enables KubernetesContainerFactory for local
- development
+ -m, --manifest <arg> Manifest JSON defining the supported
+ runtimes
+ -c, --config-file <arg> application.conf which overrides the
+ default standalone.conf
+ --api-gw Enable API Gateway support
+ --couchdb Enable CouchDB support
+ --user-events Enable User Events along with Prometheus
+ and Grafana
+ --kafka Enable embedded Kafka support
+ --kafka-ui Enable Kafka UI
+
+ --all Enables all the optional services
+ supported by Standalone OpenWhisk like
+ CouchDB, Kafka etc
+ --api-gw-port <arg> API Gateway Port
+ --clean Clean any existing state like database
+ -d, --data-dir <arg> Directory used for storage
+ --dev-kcf Enables KubernetesContainerFactory for
+ local development
--dev-mode Developer mode speeds up the startup by
disabling preflight checks and avoiding
explicit pulls.
@@ -102,6 +107,13 @@ $ java -jar openwhisk-standalone.jar -h
configuring Prometheus to connect to
existing running service instance
--disable-color-logging Disables colored logging
+ --enable-bootstrap Enable bootstrap of default users and
+ actions like those needed for Api Gateway
+ or Playground UI. By default bootstrap is
+ done by default when using Memory store or
+ default CouchDB support. When using other
+ stores enable this flag to get bootstrap
+ done
--kafka-docker-port <arg> Kafka port for use by docker based
services. If not specified then 9091 or
some random free port (if 9091 is busy)
@@ -109,14 +121,21 @@ $ java -jar openwhisk-standalone.jar -h
--kafka-port <arg> Kafka port. If not specified then 9092 or
some random free port (if 9092 is busy)
would be used
+ --no-ui Disable Playground UI
+ --ui-port <arg> Playground UI server port. If not specified
+ then 3232 or some random free port (if
+ org.apache.openwhisk.standalone.StandaloneOpenWhisk$@75a1cd57
+ is busy) would be used
-p, --port <arg> Server port
-v, --verbose
--zk-port <arg> Zookeeper port. If not specified then 2181
or some random free port (if 2181 is busy)
would be used
-h, --help Show help message
+ --version Show version of this program
OpenWhisk standalone server
+
```
Sections below would illustrate some of the supported options
@@ -224,11 +243,15 @@ whisk {
Then pass this config file via `-c` option.
-#### Using Api Gateway
+Note that Standalone OpenWhisk will not bootstrap users and actions (e.g., API Gateway and Playground UI)
+when using an external database unless explicitly requested with `--enable-bootstrap`. This is to ensure
+that default users and actions are not added to your external artifact store.
+
+#### Using API Gateway
-Api Gateway mode can be enabled via `--api-gw` flag. In this mode upon launch a separate container for [OpenWhisk Api gateway][3]
+API Gateway mode can be enabled via `--api-gw` flag. In this mode upon launch a separate container for [OpenWhisk API gateway][3]
would be launched on port `3234` (can be changed with `--api-gw-port`). In this mode you can make use of the
-[api gateway][4] support.
+[API Gateway][4] support.
#### Using Kafka
diff --git a/core/standalone/src/main/resources/playground/ui/playgroundFunctions.js b/core/standalone/src/main/resources/playground/ui/playgroundFunctions.js
index 35c33ab..10f789b 100644
--- a/core/standalone/src/main/resources/playground/ui/playgroundFunctions.js
+++ b/core/standalone/src/main/resources/playground/ui/playgroundFunctions.js
@@ -18,7 +18,7 @@
$(document).ready(function(){
// This is the location of the supporting API
// The host value may get replaced in PlaygroundLauncher to a specific host
- window.APIHOST=window.location ? window.location.origin : ''
+ window.APIHOST='http://localhost:3233'
// To install in a different namespace, change this value
window.PLAYGROUND='whisk.system'
diff --git a/core/standalone/src/main/scala/org/apache/openwhisk/core/ExecManifestSupport.scala b/core/standalone/src/main/scala/org/apache/openwhisk/core/ExecManifestSupport.scala
new file mode 100644
index 0000000..2a2fd9b
--- /dev/null
+++ b/core/standalone/src/main/scala/org/apache/openwhisk/core/ExecManifestSupport.scala
@@ -0,0 +1,30 @@
+/*
+ * 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.core
+
+import org.apache.openwhisk.core.entity.ExecManifest
+
+/**
+ * Helper utility class to enable access to core scoped ExecManifest and related classes
+ */
+object ExecManifestSupport {
+ def getDefaultImage(familyName: String): Option[String] = {
+ val runtimes = ExecManifest.runtimesManifest
+ runtimes.resolveDefaultRuntime(s"$familyName:default").map(_.image.resolveImageName())
+ }
+}
diff --git a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/PlaygroundLauncher.scala b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/PlaygroundLauncher.scala
new file mode 100644
index 0000000..b588c44
--- /dev/null
+++ b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/PlaygroundLauncher.scala
@@ -0,0 +1,128 @@
+/*
+ * 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.nio.charset.StandardCharsets.UTF_8
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.model.{ContentType, HttpCharsets, HttpEntity, MediaTypes, StatusCodes}
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.server.directives.FileAndResourceDirectives.ResourceFile
+import akka.stream.ActorMaterializer
+import akka.stream.scaladsl.{Sink, Source}
+import org.apache.commons.io.IOUtils
+import org.apache.commons.lang3.SystemUtils
+import org.apache.openwhisk.common.{Logging, TransactionId}
+import org.apache.openwhisk.core.ExecManifestSupport
+import org.apache.openwhisk.http.BasicHttpService
+import pureconfig.loadConfigOrThrow
+
+import scala.concurrent.duration._
+import scala.concurrent.{Await, ExecutionContext}
+import scala.util.{Failure, Success, Try}
+import scala.sys.process._
+
+class PlaygroundLauncher(host: String, controllerPort: Int, pgPort: Int, authKey: String, devMode: Boolean)(
+ implicit logging: Logging,
+ ec: ExecutionContext,
+ actorSystem: ActorSystem,
+ materializer: ActorMaterializer,
+ tid: TransactionId) {
+ private val interface = loadConfigOrThrow[String]("whisk.controller.interface")
+ private val jsFileName = "playgroundFunctions.js"
+ private val jsContentType = ContentType(MediaTypes.`application/javascript`, HttpCharsets.`UTF-8`)
+
+ private val uiPath = {
+ //Depending on fact the run is done from within IDE or from terminal the classpath prefix needs to be adapted
+ val res = getClass.getResource(s"/playground/ui/$jsFileName")
+ Try(ResourceFile(res)) match {
+ case Success(_) => "playground/ui"
+ case Failure(_) => "BOOT-INF/classes/playground/ui"
+ }
+ }
+
+ private val jsFileContent = {
+ val js = resourceToString(jsFileName, "ui")
+ val content = js.replace("window.APIHOST='http://localhost:3233'", s"window.APIHOST='http://$host:$controllerPort'")
+ content.getBytes(UTF_8)
+ }
+
+ private val pg = "playground"
+ private val pgUrl = s"http://${StandaloneDockerSupport.getLocalHostName()}:$pgPort/$pg"
+
+ private val wsk = new Wsk(host, controllerPort, authKey)
+
+ def run(): ServiceContainer = {
+ BasicHttpService.startHttpService(PlaygroundService.route, pgPort, None, interface)(actorSystem, materializer)
+ ServiceContainer(pgPort, pgUrl, "Playground")
+ }
+
+ def install(): Unit = {
+ val actions = List("delete", "fetch", "run", "userpackage")
+ val f = Source(actions)
+ .mapAsync(1) { name =>
+ val actionName = s"playground-$name"
+ val js = resourceToString(s"playground-$name.js", "actions")
+ val r = wsk.updatePgAction(actionName, js)
+ r.foreach(_ => logging.info(this, s"Installed action $actionName"))
+ r
+ }
+ .runWith(Sink.ignore)
+ Await.result(f, 5.minutes)
+ Try {
+ if (!devMode) {
+ prePullDefaultImages()
+ }
+ launchBrowser(pgUrl)
+ logging.info(this, s"Launched browser $pgUrl")
+ }.failed.foreach(t => logging.warn(this, "Failed to launch browser " + t))
+ }
+
+ private def launchBrowser(url: String): Unit = {
+ if (SystemUtils.IS_OS_MAC) {
+ s"open $url".!!
+ } else if (SystemUtils.IS_OS_WINDOWS) {
+ s"""start "$url" """.!!
+ } else if (SystemUtils.IS_OS_LINUX) {
+ s"xdg-open $url".!!
+ }
+ }
+
+ private def prePullDefaultImages(): Unit = {
+ ExecManifestSupport.getDefaultImage("nodejs").foreach { imageName =>
+ StandaloneDockerSupport.prePullImage(imageName)
+ }
+ }
+
+ object PlaygroundService extends BasicHttpService {
+ override def routes(implicit transid: TransactionId): Route =
+ path(PathEnd | Slash | pg) { redirect(s"/$pg/ui/index.html", StatusCodes.Found) } ~
+ pathPrefix(pg / "ui" / Segment) { fileName =>
+ get {
+ if (fileName == jsFileName) {
+ complete(HttpEntity(jsContentType, jsFileContent))
+ } else {
+ getFromResource(s"$uiPath/$fileName")
+ }
+ }
+ }
+ }
+
+ private def resourceToString(name: String, resType: String) =
+ IOUtils.resourceToString(s"/playground/$resType/$name", UTF_8)
+}
diff --git a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneDockerSupport.scala b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneDockerSupport.scala
index 7181efc..d8ca898 100644
--- a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneDockerSupport.scala
+++ b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneDockerSupport.scala
@@ -129,6 +129,18 @@ object StandaloneDockerSupport {
else hostIpLinux
}
+ def prePullImage(imageName: String)(implicit logging: Logging): Unit = {
+ //docker images openwhisk/action-nodejs-v10:nightly
+ //REPOSITORY TAG IMAGE ID CREATED SIZE
+ //openwhisk/action-nodejs-v10 nightly dbb0f8e1a050 5 days ago 967MB
+ val imageResult = s"$dockerCmd images $imageName".!!
+ val imageExist = imageResult.linesIterator.toList.size > 1
+ if (!imageExist || imageName.contains(":nightly")) {
+ logging.info(this, s"Docker Pre pulling $imageName")
+ s"$dockerCmd pull $imageName".!!
+ }
+ }
+
private lazy val hostIpLinux: String = {
//Gets the hostIp for linux https://github.com/docker/for-linux/issues/264#issuecomment-387525409
// Typical output would be like and we need line with default
diff --git a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala
index d1b0c14..9108d7f 100644
--- a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala
+++ b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/StandaloneOpenWhisk.scala
@@ -44,6 +44,7 @@ import scala.util.{Failure, Success, Try}
import KafkaLauncher._
class Conf(arguments: Seq[String]) extends ScallopConf(Conf.expandAllMode(arguments)) {
+ import StandaloneOpenWhisk.preferredPgPort
banner(StandaloneOpenWhisk.banner)
footer("\nOpenWhisk standalone server")
StandaloneOpenWhisk.gitInfo.foreach(g => version(s"Git Commit - ${g.commitId}"))
@@ -101,10 +102,23 @@ class Conf(arguments: Seq[String]) extends ScallopConf(Conf.expandAllMode(argume
val devKcf = opt[Boolean](descr = "Enables KubernetesContainerFactory for local development")
+ val noUi = opt[Boolean](descr = "Disable Playground UI", noshort = true)
+ val uiPort = opt[Int](
+ descr = s"Playground UI server port. If not specified then $preferredPgPort or some random free port " +
+ s"(if $StandaloneOpenWhisk is busy) would be used",
+ noshort = true)
+
val devUserEventsPort = opt[Int](
descr = "Specify the port for the user-event service. This mode can be used for local " +
"development of user-event service by configuring Prometheus to connect to existing running service instance")
+ val enableBootstrap = opt[Boolean](
+ descr =
+ "Enable bootstrap of default users and actions like those needed for Api Gateway or Playground UI. " +
+ "By default bootstrap is done by default when using Memory store or default CouchDB support. " +
+ "When using other stores enable this flag to get bootstrap done",
+ noshort = true)
+
mainOptions = Seq(manifest, configFile, apiGw, couchdb, userEvents, kafka, kafkaUi)
verify()
@@ -188,6 +202,10 @@ object StandaloneOpenWhisk extends SLF4JLogging {
val wskPath = System.getProperty("whisk.standalone.wsk", "wsk")
+ val preferredPgPort = 3232
+
+ private val systemUser = "whisk.system"
+
def main(args: Array[String]): Unit = {
val conf = new Conf(args)
@@ -204,14 +222,12 @@ object StandaloneOpenWhisk extends SLF4JLogging {
implicit val logger: Logging = createLogging(actorSystem, conf)
implicit val ec: ExecutionContext = actorSystem.dispatcher
+ val owPort = conf.port()
val (dataDir, workDir) = initializeDirs(conf)
val (dockerClient, dockerSupport) = prepareDocker(conf)
val defaultSvcs = Seq(
- ServiceContainer(
- conf.port(),
- s"http://${StandaloneDockerSupport.getLocalHostName()}:${conf.port()}",
- "Controller"))
+ ServiceContainer(owPort, s"http://${StandaloneDockerSupport.getLocalHostName()}:$owPort", "Controller"))
val (apiGwApiPort, apiGwSvcs) = if (conf.apiGw()) {
startApiGateway(conf, dockerClient, dockerSupport)
@@ -227,14 +243,20 @@ object StandaloneOpenWhisk extends SLF4JLogging {
startUserEvents(conf.port(), kafkaDockerPort, conf.devUserEventsPort.toOption, workDir, dataDir, dockerClient)
else Seq.empty
- val svcs = Seq(defaultSvcs, apiGwSvcs, couchSvcs.toList, kafkaSvcs, userEventSvcs).flatten
+ val pgLauncher = if (conf.noUi()) None else Some(createPgLauncher(owPort, conf))
+ val pgSvc = pgLauncher.map(pg => Seq(pg.run())).getOrElse(Seq.empty)
+
+ val svcs = Seq(defaultSvcs, apiGwSvcs, couchSvcs.toList, kafkaSvcs, userEventSvcs, pgSvc).flatten
new ServiceInfoLogger(conf, svcs, dataDir).run()
startServer(conf)
new ServerStartupCheck(conf.serverUrl, "OpenWhisk").waitForServerToStart()
- if (conf.apiGw()) {
- installRouteMgmt(conf, workDir, apiGwApiPort)
+ if (canInstallUserAndActions(conf)) {
+ if (conf.apiGw()) {
+ installRouteMgmt(conf, workDir, apiGwApiPort)
+ }
+ pgLauncher.foreach(_.install())
}
}
@@ -253,7 +275,9 @@ object StandaloneOpenWhisk extends SLF4JLogging {
def startServer(
conf: Conf)(implicit actorSystem: ActorSystem, materializer: ActorMaterializer, logging: Logging): Unit = {
- bootstrapUsers()
+ if (canInstallUserAndActions(conf)) {
+ bootstrapUsers()
+ }
startController()
}
@@ -423,12 +447,9 @@ object StandaloneOpenWhisk extends SLF4JLogging {
}
private def installRouteMgmt(conf: Conf, workDir: File, apiGwApiPort: Int)(implicit logging: Logging): Unit = {
- val user = "whisk.system"
val apiGwHostv2 = s"http://${StandaloneDockerSupport.getLocalHostIp()}:$apiGwApiPort/v2"
- val authKey = getUsers().getOrElse(
- user,
- throw new Exception(s"Did not found auth key for $user which is needed to install the api management package"))
- val installer = InstallRouteMgmt(workDir, authKey, conf.serverUrl, "/" + user, Uri(apiGwHostv2), wskPath)
+ val authKey = systemAuthKey
+ val installer = InstallRouteMgmt(workDir, authKey, conf.serverUrl, "/" + systemUser, Uri(apiGwHostv2), wskPath)
installer.run()
}
@@ -536,4 +557,33 @@ object StandaloneOpenWhisk extends SLF4JLogging {
private def configureDevMode(): Unit = {
setSysProp("whisk.docker.standalone.container-factory.pull-standard-images", "false")
}
+
+ private def createPgLauncher(
+ owPort: Int,
+ conf: Conf)(implicit logging: Logging, as: ActorSystem, ec: ExecutionContext, materializer: ActorMaterializer) = {
+ implicit val tid: TransactionId = TransactionId(systemPrefix + "playground")
+ val pgPort = getPort(conf.uiPort.toOption, preferredPgPort)
+ new PlaygroundLauncher(StandaloneDockerSupport.getLocalHostName(), owPort, pgPort, systemAuthKey, conf.devMode())
+ }
+
+ private def systemAuthKey: String = {
+ getUsers().getOrElse(systemUser, throw new Exception(s"Did not found auth key for $systemUser"))
+ }
+
+ private def canInstallUserAndActions(conf: Conf)(implicit logging: Logging, actorSystem: ActorSystem): Boolean = {
+ val config = actorSystem.settings.config
+ val artifactStore = config.getString("whisk.spi.ArtifactStoreProvider")
+ if (conf.couchdb() || artifactStore == "org.apache.openwhisk.core.database.memory.MemoryArtifactStoreProvider") {
+ true
+ } else if (conf.enableBootstrap()) {
+ logging.info(this, "Bootstrap is enabled for external ArtifactStore")
+ true
+ } else {
+ logging.info(
+ this,
+ s"Bootstrap is not enabled as connecting to external ArtifactStore. " +
+ s"Start with ${conf.enableBootstrap.name} to bootstrap default users and action")
+ false
+ }
+ }
}
diff --git a/core/standalone/src/main/scala/org/apache/openwhisk/standalone/Wsk.scala b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/Wsk.scala
new file mode 100644
index 0000000..b663d29
--- /dev/null
+++ b/core/standalone/src/main/scala/org/apache/openwhisk/standalone/Wsk.scala
@@ -0,0 +1,81 @@
+/*
+ * 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 akka.Done
+import akka.actor.ActorSystem
+import akka.http.scaladsl.model.Uri.Query
+import akka.http.scaladsl.model.headers.{Accept, Authorization, BasicHttpCredentials}
+import akka.http.scaladsl.model.{HttpHeader, HttpMethods, MediaTypes, Uri}
+import org.apache.openwhisk.core.database.PutException
+import org.apache.openwhisk.http.PoolingRestClient
+import spray.json._
+
+import scala.concurrent.{ExecutionContext, Future}
+
+class Wsk(host: String, port: Int, authKey: String)(implicit system: ActorSystem) extends DefaultJsonProtocol {
+ import PoolingRestClient._
+ private implicit val ec: ExecutionContext = system.dispatcher
+ private val client = new PoolingRestClient("http", host, port, 10)
+ private val baseHeaders: List[HttpHeader] = {
+ val Array(username, password) = authKey.split(':')
+ List(Authorization(BasicHttpCredentials(username, password)), Accept(MediaTypes.`application/json`))
+ }
+
+ def updatePgAction(name: String, content: String): Future[Done] = {
+ val js = actionJson(name, content)
+ val params = Map("overwrite" -> "true")
+ val uri = Uri(s"/api/v1/namespaces/_/actions/$name").withQuery(Query(params))
+ client.requestJson[JsObject](mkJsonRequest(HttpMethods.PUT, uri, js, baseHeaders)).map {
+ case Right(_) => Done
+ case Left(status) => throw PutException(s"Error creating action $name " + status)
+ }
+ }
+
+ private def actionJson(name: String, code: String) = {
+ s"""{
+ | "namespace": "_",
+ | "name": "$name",
+ | "exec": {
+ | "kind": "nodejs:default",
+ | "code": ${quote(code)}
+ | },
+ | "annotations": [{
+ | "key": "provide-api-key",
+ | "value": true
+ | }, {
+ | "key": "web-export",
+ | "value": true
+ | }, {
+ | "key": "raw-http",
+ | "value": false
+ | }, {
+ | "key": "final",
+ | "value": true
+ | }],
+ | "parameters": [{
+ | "key": "__ignore_certs",
+ | "value": true
+ | }]
+ |}""".stripMargin.parseJson.asJsObject
+ }
+
+ private def quote(code: String) = {
+ JsString(code).compactPrint
+ }
+}
diff --git a/docs/images/playground-ui.png b/docs/images/playground-ui.png
new file mode 100644
index 0000000..71a6ebf
Binary files /dev/null and b/docs/images/playground-ui.png differ
diff --git a/tests/src/test/scala/org/apache/openwhisk/standalone/StandaloneServerFixture.scala b/tests/src/test/scala/org/apache/openwhisk/standalone/StandaloneServerFixture.scala
index 71a842f..e8d97f0 100644
--- a/tests/src/test/scala/org/apache/openwhisk/standalone/StandaloneServerFixture.scala
+++ b/tests/src/test/scala/org/apache/openwhisk/standalone/StandaloneServerFixture.scala
@@ -58,6 +58,8 @@ trait StandaloneServerFixture extends TestSuite with BeforeAndAfterAll with Stre
protected def dumpStartupLogs: Boolean = false
+ protected def disablePlayGround: Boolean = true
+
protected val dataDirPath: String = FilenameUtils.concat(FileUtils.getTempDirectoryPath, "standalone")
override def beforeAll(): Unit = {
@@ -70,6 +72,7 @@ trait StandaloneServerFixture extends TestSuite with BeforeAndAfterAll with Stre
System.setProperty(WHISK_SERVER, serverUrl)
super.beforeAll()
println(s"Running standalone server from ${standaloneServerJar.getAbsolutePath}")
+ val pgArgs = if (disablePlayGround) Seq("--no-ui") else Seq.empty
val args = Seq(
Seq(
"java",
@@ -81,6 +84,7 @@ trait StandaloneServerFixture extends TestSuite with BeforeAndAfterAll with Stre
++ Seq("-jar", standaloneServerJar.getAbsolutePath, "--disable-color-logging", "--data-dir", dataDirPath)
++ configFileOpts
++ manifestFileOpts
+ ++ pgArgs
++ extraArgs,
Seq("-p", serverPort.toString)).flatten