diff --git a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/CorsSettings.scala b/common/scala/src/main/scala/org/apache/openwhisk/http/CorsSettings.scala similarity index 70% rename from core/controller/src/main/scala/org/apache/openwhisk/core/controller/CorsSettings.scala rename to common/scala/src/main/scala/org/apache/openwhisk/http/CorsSettings.scala index 959867486ab..0bc20f130ef 100644 --- a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/CorsSettings.scala +++ b/common/scala/src/main/scala/org/apache/openwhisk/http/CorsSettings.scala @@ -15,15 +15,20 @@ * limitations under the License. */ -package org.apache.openwhisk.core.controller +package org.apache.openwhisk.http -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.model.HttpMethods.{DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT} +import akka.http.scaladsl.model.HttpMethods._ +import akka.http.scaladsl.model.headers.{ + `Access-Control-Allow-Headers`, + `Access-Control-Allow-Methods`, + `Access-Control-Allow-Origin` +} +import akka.http.scaladsl.server.Directives /** * Defines the CORS settings for the REST APIs and Web Actions. */ -protected[controller] object CorsSettings { +object CorsSettings { trait RestAPIs { val allowOrigin = Defaults.allowOrigin @@ -38,6 +43,16 @@ protected[controller] object CorsSettings { val allowMethods = `Access-Control-Allow-Methods`(OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH) } + object ServerAPIs { + val allowOrigin = Defaults.allowOrigin + val allowHeaders = Defaults.allowHeaders + val allowMethods = `Access-Control-Allow-Methods`(OPTIONS, GET, POST) + } + + trait RespondWithServerCorsHeaders extends Directives { + val sendCorsHeaders = respondWithHeaders(ServerAPIs.allowOrigin, ServerAPIs.allowHeaders, ServerAPIs.allowMethods) + } + object Defaults { val allowOrigin = `Access-Control-Allow-Origin`.* diff --git a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Controller.scala b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Controller.scala index 2ff1ecb5641..62659fcea7e 100644 --- a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Controller.scala +++ b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Controller.scala @@ -37,6 +37,7 @@ import org.apache.openwhisk.core.entity.ActivationId.ActivationIdGenerator import org.apache.openwhisk.core.entity.ExecManifest.Runtimes import org.apache.openwhisk.core.entity._ import org.apache.openwhisk.core.loadBalancer.LoadBalancerProvider +import org.apache.openwhisk.http.CorsSettings.RespondWithServerCorsHeaders import org.apache.openwhisk.http.ErrorResponse.terminate import org.apache.openwhisk.http.{BasicHttpService, BasicRasService} import org.apache.openwhisk.spi.SpiLoader @@ -78,7 +79,8 @@ class Controller(val instance: ControllerInstanceId, implicit val whiskConfig: WhiskConfig, implicit val actorSystem: ActorSystem, implicit val logging: Logging) - extends BasicRasService { + extends BasicRasService + with RespondWithServerCorsHeaders { TransactionId.controller.mark( this, @@ -98,7 +100,7 @@ class Controller(val instance: ControllerInstanceId, (pathEndOrSingleSlash & get) { complete(info) } - } ~ apiV1.routes ~ swagger.swaggerRoutes ~ internalInvokerHealth ~ activationStatus ~ disable + } ~ apiV1.routes ~ swagger.swaggerRoutes ~ adminRoutes } // initialize datastores @@ -217,6 +219,14 @@ class Controller(val instance: ControllerInstanceId, } } } + + private def adminRoutes(implicit transid: TransactionId) = { + sendCorsHeaders { + options { + complete(OK) + } ~ internalInvokerHealth ~ activationStatus ~ disable + } + } } /** diff --git a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/RestAPIs.scala b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/RestAPIs.scala index 74ae5bdb352..18db6c7bf81 100644 --- a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/RestAPIs.scala +++ b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/RestAPIs.scala @@ -36,7 +36,7 @@ import org.apache.openwhisk.core.entity._ import org.apache.openwhisk.core.entity.types._ import org.apache.openwhisk.core.loadBalancer.LoadBalancer import org.apache.openwhisk.core.{ConfigKeys, WhiskConfig} -import org.apache.openwhisk.http.Messages +import org.apache.openwhisk.http.{CorsSettings, Messages} import org.apache.openwhisk.spi.{Spi, SpiLoader} import scala.concurrent.{ExecutionContext, Future} diff --git a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala index c4d7d443316..93e31ac3dd4 100644 --- a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala +++ b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala @@ -18,7 +18,6 @@ package org.apache.openwhisk.core.controller import java.util.Base64 - import scala.concurrent.Future import scala.util.{Failure, Success, Try} import akka.http.scaladsl.model.HttpEntity.Empty @@ -56,7 +55,7 @@ import org.apache.openwhisk.core.entity._ import org.apache.openwhisk.core.entity.types._ import org.apache.openwhisk.core.loadBalancer.LoadBalancerException import org.apache.openwhisk.http.ErrorResponse.terminate -import org.apache.openwhisk.http.Messages +import org.apache.openwhisk.http.{CorsSettings, Messages} import org.apache.openwhisk.http.LenientSprayJsonSupport._ import org.apache.openwhisk.spi.SpiLoader import org.apache.openwhisk.utils.JsHelpers._ diff --git a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/v2/FunctionPullingContainerPool.scala b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/v2/FunctionPullingContainerPool.scala index 2a89630a550..7f9fc9b3e4d 100644 --- a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/v2/FunctionPullingContainerPool.scala +++ b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/v2/FunctionPullingContainerPool.scala @@ -65,7 +65,6 @@ case class TotalContainerPoolState(totalContainers: Int, prewarmedPool: PrewarmedContainerPoolState, busyPool: WarmContainerPoolState, pausedPool: WarmContainerPoolState) { - def serialize(): String = TotalContainerPoolState.totalPoolSerdes.write(this).compactPrint } @@ -436,7 +435,6 @@ class FunctionPullingContainerPool( // Reset the prewarmCreateCount value when do expiration check and backfill prewarm if possible prewarmCreateFailedCount.set(0) adjustPrewarmedContainer(false, true) - case GetState => val totalContainers = busyPool.size + inProgressPool.size + warmedPool.size + prewarmedPool.size val prewarmedState = @@ -444,7 +442,6 @@ class FunctionPullingContainerPool( val busyState = WarmContainerPoolState(busyPool.size, busyPool.values.map(_.basicContainerInfo).toList) val pausedState = WarmContainerPoolState(warmedPool.size, warmedPool.values.map(_.basicContainerInfo).toList) sender() ! TotalContainerPoolState(totalContainers, inProgressPool.size, prewarmedState, busyState, pausedState) - } /** Install prewarm containers up to the configured requirements for each kind/memory combination or specified kind/memory */ diff --git a/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/FPCInvokerServer.scala b/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/FPCInvokerServer.scala index 88c8b40766c..99909120956 100644 --- a/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/FPCInvokerServer.scala +++ b/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/FPCInvokerServer.scala @@ -19,11 +19,13 @@ package org.apache.openwhisk.core.invoker import akka.actor.ActorSystem import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.StatusCodes.OK import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.server.Route import org.apache.openwhisk.common.{Logging, TransactionId} import org.apache.openwhisk.core.ConfigKeys import org.apache.openwhisk.http.BasicRasService +import org.apache.openwhisk.http.CorsSettings.RespondWithServerCorsHeaders import org.apache.openwhisk.http.ErrorResponse.terminate import pureconfig.loadConfigOrThrow import spray.json.PrettyPrinter @@ -39,42 +41,48 @@ class FPCInvokerServer(val invoker: InvokerCore, systemUsername: String, systemP implicit val ec: ExecutionContext, val actorSystem: ActorSystem, val logger: Logging) - extends BasicRasService { + extends BasicRasService + with RespondWithServerCorsHeaders { /** Pretty print JSON response. */ implicit val jsonPrettyResponsePrinter = PrettyPrinter override def routes(implicit transid: TransactionId): Route = { - super.routes ~ extractCredentials { - case Some(BasicHttpCredentials(username, password)) if username == systemUsername && password == systemPassword => - (path("enable") & post) { - complete(invoker.enable()) - } ~ (path("disable") & post) { - complete(invoker.disable()) - } ~ (path("isEnabled") & get) { - complete(invoker.isEnabled()) - } ~ (pathPrefix("pool") & get) { - pathEndOrSingleSlash { - complete { - invoker.getPoolState().map { - case Right(poolState) => - poolState.serialize() - case Left(value) => - value.serialize() + super.routes ~ sendCorsHeaders { + options { + complete(OK) + } ~ extractCredentials { + case Some(BasicHttpCredentials(username, password)) + if username == systemUsername && password == systemPassword => + (path("enable") & post) { + complete(invoker.enable()) + } ~ (path("disable") & post) { + complete(invoker.disable()) + } ~ (path("isEnabled") & get) { + complete(invoker.isEnabled()) + } ~ (pathPrefix("pool") & get) { + pathEndOrSingleSlash { + complete { + invoker.getPoolState().map { + case Right(poolState) => + poolState.serialize() + case Left(value) => + value.serialize() + } } - } - } ~ (path("count") & get) { - complete { - invoker.getPoolState().map { - case Right(poolState) => - (poolState.busyPool.total + poolState.pausedPool.total + poolState.inProgressCount).toJson.compactPrint - case Left(value) => - value.serialize() + } ~ (path("count") & get) { + complete { + invoker.getPoolState().map { + case Right(poolState) => + (poolState.busyPool.total + poolState.pausedPool.total + poolState.inProgressCount).toJson.compactPrint + case Left(value) => + value.serialize() + } } } } - } - case _ => terminate(StatusCodes.Unauthorized) + case _ => terminate(StatusCodes.Unauthorized) + } } } } diff --git a/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/InvokerReactive.scala b/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/InvokerReactive.scala index d7aae4a3c29..c9e32db4d70 100644 --- a/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/InvokerReactive.scala +++ b/core/invoker/src/main/scala/org/apache/openwhisk/core/invoker/InvokerReactive.scala @@ -334,5 +334,4 @@ class InvokerReactive( override def getPoolState(): Future[Either[NotSupportedPoolState, TotalContainerPoolState]] = { Future.successful(Left(NotSupportedPoolState())) } - } diff --git a/core/scheduler/src/main/scala/org/apache/openwhisk/core/scheduler/FPCSchedulerServer.scala b/core/scheduler/src/main/scala/org/apache/openwhisk/core/scheduler/FPCSchedulerServer.scala index aec923508e3..7a8152ae959 100644 --- a/core/scheduler/src/main/scala/org/apache/openwhisk/core/scheduler/FPCSchedulerServer.scala +++ b/core/scheduler/src/main/scala/org/apache/openwhisk/core/scheduler/FPCSchedulerServer.scala @@ -20,11 +20,13 @@ package org.apache.openwhisk.core.scheduler import akka.actor.ActorSystem import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.model.StatusCodes.OK import akka.http.scaladsl.model.headers.BasicHttpCredentials import akka.http.scaladsl.server.Route import org.apache.openwhisk.common.{Logging, TransactionId} import org.apache.openwhisk.core.ConfigKeys import org.apache.openwhisk.http.BasicRasService +import org.apache.openwhisk.http.CorsSettings.RespondWithServerCorsHeaders import org.apache.openwhisk.http.ErrorResponse.terminate import pureconfig.loadConfigOrThrow import spray.json.DefaultJsonProtocol._ @@ -40,43 +42,49 @@ class FPCSchedulerServer(scheduler: SchedulerCore, systemUsername: String, syste implicit val ec: ExecutionContext, implicit val actorSystem: ActorSystem, implicit val logger: Logging) - extends BasicRasService { + extends BasicRasService + with RespondWithServerCorsHeaders { override def routes(implicit transid: TransactionId): Route = { - super.routes ~ extractCredentials { - case Some(BasicHttpCredentials(username, password)) if username == systemUsername && password == systemPassword => - (path("state") & get) { - complete { - scheduler.getState.map { - case (list, creationCount) => - val sum = list.map(tuple => tuple._2).sum - (Map("queue" -> sum.toString) ++ Map("creationCount" -> creationCount.toString)).toJson + super.routes ~ sendCorsHeaders { + options { + complete(OK) + } ~ extractCredentials { + case Some(BasicHttpCredentials(username, password)) + if username == systemUsername && password == systemPassword => + (path("state") & get) { + complete { + scheduler.getState.map { + case (list, creationCount) => + val sum = list.map(tuple => tuple._2).sum + (Map("queue" -> sum.toString) ++ Map("creationCount" -> creationCount.toString)).toJson + } + } + } ~ (path("disable") & post) { + logger.warn(this, "Scheduler is disabled") + scheduler.disable() + complete("scheduler disabled") + } ~ (pathPrefix(FPCSchedulerServer.queuePathPrefix) & get) { + pathEndOrSingleSlash { + complete(scheduler.getQueueStatusData.map(s => s.toJson)) + } ~ (path("count") & get) { + complete(scheduler.getQueueSize.map(s => s.toJson)) + } + } ~ (path("activation" / "count") & get) { + pathEndOrSingleSlash { + complete( + scheduler.getQueueStatusData + .map { s => + s.map(_.waitingActivation.size) + } + .map(a => a.sum) + .map(_.toJson)) } } - } ~ (path("disable") & post) { - logger.warn(this, "Scheduler is disabled") - scheduler.disable() - complete("scheduler disabled") - } ~ (pathPrefix(FPCSchedulerServer.queuePathPrefix) & get) { - pathEndOrSingleSlash { - complete(scheduler.getQueueStatusData.map(s => s.toJson)) - } ~ (path("count") & get) { - complete(scheduler.getQueueSize.map(s => s.toJson)) - } - } ~ (path("activation" / "count") & get) { - pathEndOrSingleSlash { - complete( - scheduler.getQueueStatusData - .map { s => - s.map(_.waitingActivation.size) - } - .map(a => a.sum) - .map(_.toJson)) - } - } - case _ => - implicit val jsonPrettyResponsePrinter = PrettyPrinter - terminate(StatusCodes.Unauthorized) + case _ => + implicit val jsonPrettyResponsePrinter = PrettyPrinter + terminate(StatusCodes.Unauthorized) + } } } }