Skip to content

Add cors headers to components' server admin routes #5351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
* 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
Expand All @@ -38,6 +39,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`.*

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -78,7 +79,7 @@ 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,
Expand All @@ -98,7 +99,7 @@ class Controller(val instance: ControllerInstanceId,
(pathEndOrSingleSlash & get) {
complete(info)
}
} ~ apiV1.routes ~ swagger.swaggerRoutes ~ internalInvokerHealth ~ activationStatus ~ disable
} ~ apiV1.routes ~ swagger.swaggerRoutes ~ adminRoutes
}

// initialize datastores
Expand Down Expand Up @@ -217,6 +218,14 @@ class Controller(val instance: ControllerInstanceId,
}
}
}

private def adminRoutes(implicit transid: TransactionId) = {
sendCorsHeaders {
options {
complete(OK)
} ~ internalInvokerHealth ~ activationStatus ~ disable
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import org.apache.openwhisk.core.connector.{
ContainerCreationAckMessage,
ContainerCreationMessage,
ContainerDeletionMessage,
GetState,
ResultMetadata
}
import org.apache.openwhisk.core.containerpool.{
Expand Down Expand Up @@ -65,7 +64,6 @@ case class TotalContainerPoolState(totalContainers: Int,
prewarmedPool: PrewarmedContainerPoolState,
busyPool: WarmContainerPoolState,
pausedPool: WarmContainerPoolState) {

def serialize(): String = TotalContainerPoolState.totalPoolSerdes.write(this).compactPrint
}

Expand All @@ -78,6 +76,7 @@ case class DeletionContainer(deletionMessage: ContainerDeletionMessage)
case object Remove
case class Keep(timeout: FiniteDuration)
case class PrewarmContainer(maxConcurrent: Int)
case object GetState
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason we should define another GetState here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what happened now, I had this in progress for awhile and had a bad merge somewhere with your recent commits. Should all be cleaned up now.


/**
* A pool managing containers to run actions on.
Expand Down Expand Up @@ -436,15 +435,13 @@ 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 =
PrewarmedContainerPoolState(prewarmedPool.size, prewarmedPool.groupBy(_._2.kind).mapValues(_.size).toMap)
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 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ case class InitializedData(container: Container,
override val clientProxy: ActorRef)
extends ContainerAvailableData(container, invocationNamespace, action)
with WithClient {
override def getContainer = Some(container)
def toReschedulingData(resumeRun: RunActivation) =
ReschedulingData(container, invocationNamespace, action, clientProxy, resumeRun)
}
Expand All @@ -165,7 +164,6 @@ case class WarmData(container: Container,
override val clientProxy: ActorRef)
extends ContainerAvailableData(container, invocationNamespace, action)
with WithClient {
override def getContainer = Some(container)
def toReschedulingData(resumeRun: RunActivation) =
ReschedulingData(container, invocationNamespace, action, clientProxy, resumeRun)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import org.apache.openwhisk.core.ack.{ActiveAck, HealthActionAck, MessagingActiv
import org.apache.openwhisk.core.connector._
import org.apache.openwhisk.core.containerpool._
import org.apache.openwhisk.core.containerpool.logging.LogStoreProvider
import org.apache.openwhisk.core.containerpool.v2._
import org.apache.openwhisk.core.containerpool.v2.{GetState => GetPoolState, _}
import org.apache.openwhisk.core.database._
import org.apache.openwhisk.core.entity._
import org.apache.openwhisk.core.etcd.EtcdKV.ContainerKeys.containerPrefix
Expand Down Expand Up @@ -390,7 +390,7 @@ class FPCInvokerReactive(config: WhiskConfig,

override def getPoolState(): Future[Either[NotSupportedPoolState, TotalContainerPoolState]] = {
implicit val timeout: Timeout = 5.seconds
(pool ? GetState).mapTo[TotalContainerPoolState].map(Right(_))
(pool ? GetPoolState).mapTo[TotalContainerPoolState].map(Right(_))
}

override def isEnabled(): String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -39,42 +41,46 @@ 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)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,5 +334,4 @@ class InvokerReactive(
override def getPoolState(): Future[Either[NotSupportedPoolState, TotalContainerPoolState]] = {
Future.successful(Left(NotSupportedPoolState()))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand All @@ -40,43 +42,47 @@ 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)
}
}
}
}
Expand Down