Skip to content
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

Port Controller from Spray to Akka #2218

Merged
merged 5 commits into from Aug 12, 2017
Merged

Port Controller from Spray to Akka #2218

merged 5 commits into from Aug 12, 2017

Conversation

dubee
Copy link
Member

@dubee dubee commented May 5, 2017

  • Refactor controller to use Akka as Spray is no longer supported

@dubee
Copy link
Member Author

dubee commented May 5, 2017

Need to consider if we want to replace the existing controller with the port to Akka, or have tons of duplicate code hanging around for version 2 of the controller.

@dubee
Copy link
Member Author

dubee commented May 5, 2017

Still need to port over triggers, activations, rules, and packages. Opening the PR for comments though.

private lazy val actionSequenceLimit = whiskConfig.actionSequenceLimit.toInt

/** Custom deserializer for timeout query parameter. */
private implicit val stringToTimeoutDeserializer = new FromStringDeserializer[FiniteDuration] {
Copy link
Member Author

Choose a reason for hiding this comment

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

Need to fix this to deserialize FiniteDuration with Akka.

@rabbah
Copy link
Member

rabbah commented May 5, 2017

Need to consider if we want to replace the existing controller with the port to Akka, or have tons of duplicate code hanging around for version 2 of the controller.

I imagined - where necessary - to refactor the handlers from v1 so they are free of routing dependencies and so v1 vs v2 just become routing specific, with serializers for the corresponding http response types.

Definitely do not want to duplicate/clone-and-own all of v1 as v2.

@markusthoemmes
Copy link
Contributor

Can we port v1 as is, without going to v2? Benefits include:

  • Move away from an unsupported spray.
  • Move away from an unsupported scala version (spray makes us stay on 2.11, while 2.12 is out)

@csantanapr csantanapr added the wip label May 8, 2017
@dubee dubee force-pushed the v2 branch 4 times, most recently from 245b09b to 2d363da Compare May 10, 2017 19:06
@markusthoemmes
Copy link
Contributor

Quick q on the procedure here: Do we want to copy all the code or do we want to migrate v1 to akka-http in-place? Asking specifically because I'd like to keep the git history of these files intact.

@rabbah
Copy link
Member

rabbah commented May 11, 2017

+1 I do think there's a way to do the migration to Akka-http without all this copying.

@dubee
Copy link
Member Author

dubee commented May 11, 2017

POST requests currently require a payload. Otherwise, an error response is generated. Not sure if this is new in Akka, @rabbah. Will have to figure it out.

Another defect is that generated error responses are not currently JSON formatted.

@dubee
Copy link
Member Author

dubee commented May 11, 2017

For the port, I plan on overwriting the existing controller so that there isn't a lot of duplicate code. Will be able to reuse the existing test suites this way. For now, I'm just working with the duplicate code until I get everything converted over. Just need web actions now.

@dubee
Copy link
Member Author

dubee commented May 11, 2017

Side note: keep getting a consul test failure in Travis when running two controllers.

@dubee
Copy link
Member Author

dubee commented Jun 7, 2017

Currently I have replaced the Spray controller with the Akka controller. However, controller unit tests are failing as they are still using Spray, so these tests will have to be updated. Also the BasicHttpService will have to be updated to use Akka since the controller and invoker extend it.

@dubee dubee force-pushed the v2 branch 2 times, most recently from dd145eb to 4021c82 Compare June 13, 2017 14:39
@dubee
Copy link
Member Author

dubee commented Jun 19, 2017

I'm going to squash all the commits into one commit due to the nightmare that just resulted after rebasing against upstream.

@dubee dubee force-pushed the v2 branch 2 times, most recently from f213d4e to 0ec80f9 Compare June 20, 2017 20:31
@dubee dubee force-pushed the v2 branch 3 times, most recently from 508312c to 2af8d4b Compare August 8, 2017 16:02
Unmarshaller.byteStringUnmarshaller.forContentTypes(`application/json`).mapWithCharset { (data, charset) =>
val decoded = data.decodeString(charset.nioCharset.name)

Try {
Copy link
Member

Choose a reason for hiding this comment

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

perhaps this should check decoded.length > 0 and decode else return JsObject and skip the try/catch in that case.

Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW, here is the unmarshaller I wrote while debugging performance issues (it's the original one copied and added the data.size == 0 case)

implicit val entityToJsObject: FromEntityUnmarshaller[JsObject] =
        Unmarshaller.byteStringUnmarshaller.forContentTypes(`application/json`).mapWithCharset { (data, charset) =>
            if (data.size == 0) {
                JsObject()
            } else {
                val input =
                    if (charset == HttpCharsets.`UTF-8`) ParserInput(data.toArray)
                    else ParserInput(data.decodeString(charset.nioCharset))
                JsonParser(input).asJsObject
            }
        }

DebuggingDirectives.logRequest(logRequestInfo _) {
DebuggingDirectives.logRequestResponse(logResponseInfo _) {
routes
handleRejections(customRejectionHandler) {
Copy link
Member

Choose a reason for hiding this comment

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

@dubee why did the directive order change?


/** Assigns transaction id to every request. */
protected val assignId = extract(_ => transid())

/** Rejection handler to terminate connection on a bad request. Delegates to Spray handler. */

protected def customRejectionHandler(implicit transid: TransactionId) = RejectionHandler {
Copy link
Member

Choose a reason for hiding this comment

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

is my understanding correct that the prioritization above lowers the priority on the auth rejection?


protected def customRejectionHandler(implicit transid: TransactionId) = RejectionHandler {
case rejections => {
logging.info(this, s"[REJECT] $rejections")
Copy link
Member

Choose a reason for hiding this comment

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

are we losing the rejection log message?

Copy link
Member

Choose a reason for hiding this comment

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

i'm ok dropping this - it's of nominal value.

case Success(e) => Right(e)
case Failure(t) => Left(MalformedContent(t.getMessage))
case Success(e) => e
case Failure(t) => throw new IllegalArgumentException(s"Parameter is not a valid value for a entity name: $value")
Copy link
Member

Choose a reason for hiding this comment

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

i'd move all three throw here to messages to Messages.

Copy link
Member

Choose a reason for hiding this comment

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

pushed a commit to do this

case t => Left(CustomRejection(ServiceUnavailable))
}
def basicAuth[A](verify: Option[BasicHttpCredentials] => Future[Option[A]]) = {
authenticateOrRejectWithChallenge[BasicHttpCredentials, A] { creds =>
Copy link
Member

@rabbah rabbah Aug 12, 2017

Choose a reason for hiding this comment

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

the code above tries to return a more informative error when authentication fails because the database is not available. I think we are losing this feature now. I don't think there was a test for this (it will require mocking a db connection and failing it).

you can simulate this locally and verify this is a small change in behavior - if i'm right, then please open an issue to address it separately (this time with a test).

edit: confirmed that the new logic reports an internal server error if the database connection is severed instead of service unavailable.

Copy link
Member

Choose a reason for hiding this comment

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

opened #2607

protected[controller] trait RespondWithHeaders extends Directives {
val allowOrigin = `Access-Control-Allow-Origin`(AllOrigins)
val allowOrigin = `Access-Control-Allow-Origin`.*
Copy link
Member

Choose a reason for hiding this comment

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

🙈

complete(OK, trigger.withoutRules)
}

implicit val entityToJsObject: FromEntityUnmarshaller[JsObject] =
Copy link
Member

Choose a reason for hiding this comment

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

this method is repeated twice?
occurs also in Actions.scala.

Copy link
Member

Choose a reason for hiding this comment

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

pushed a commit to address this.

if (contentType.mediaType.binary) {
Try(JsString(Base64.getEncoder.encodeToString(data.toByteArray))) match {
case HttpEntity.Strict(contentType, data) =>
// application/json is not a binary type in Akka, but is binary in Spray
Copy link
Member

@rabbah rabbah Aug 12, 2017

Choose a reason for hiding this comment

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

this will make a lot of users happy 🎉
opened #2606.


}
}

Copy link
Member

Choose a reason for hiding this comment

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

what are we losing by removing these tests?

status should be(RequestEntityTooLarge)
val expectedErrorMsg = Messages.entityTooBig(SizeError(
fieldDescriptionForSizeError,
(largeEntity.length + 13).B,
(largeEntity.length + 8).B,
Copy link
Member

Choose a reason for hiding this comment

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

+5B again - guessing quotes are the difference.

@rabbah
Copy link
Member

rabbah commented Aug 12, 2017

@dubeejw great job on getting all the issues sorted out in this migration!
a couple of small nits to address and I'll merge.

@rabbah rabbah self-assigned this Aug 12, 2017
@rabbah rabbah added the pgapproved Pipeline has approved this change. label Aug 12, 2017
}
}
}
case CustomRejection(status, cause) :: _ => complete(status, ErrorResponse(cause, transid))
Copy link
Member

Choose a reason for hiding this comment

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

this in particular was used for the auth failure mode #2607.

we can delete CustomRejection since it will be unused now - assuming we keep this behavior.

@rabbah rabbah merged commit d564ea9 into apache:master Aug 12, 2017
@@ -214,7 +218,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
Parameters(key.toString, "a" * 10)
} reduce (_ ++ _)
val content = s"""{"parameters":$parameters}""".parseJson.asJsObject
Put(s"$collectionPath/${aname}", content) ~> sealRoute(routes(creds)) ~> check {
Put(s"$collectionPath/${aname}", content) ~> Route.seal(routes(creds)) ~> check {
Copy link
Contributor

Choose a reason for hiding this comment

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

just wondering why would want to use curly braces syntax instead of omitting them?

Copy link
Member

Choose a reason for hiding this comment

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

Around aname? Historically this may have been something else then copy paste. Should be removed.

Copy link
Contributor

Choose a reason for hiding this comment

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

sorry, yep around aname variable.

Copy link
Member

Choose a reason for hiding this comment

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

👍 if you want to remove them and send a pr

Copy link
Contributor

Choose a reason for hiding this comment

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

yup, I'll do that ;)

case Failure(usr: UnsuccessfulResponseException) if usr.response.status == StatusCodes.NotFound =>
Http().singleRequest(request).map {
case HttpResponse(StatusCodes.OK, headers, entity, _) =>
logging.info(this, s"successfully invoked ${rule.action} -> ")
Copy link
Member

Choose a reason for hiding this comment

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

looks like we missed this - was previously logging the activation id.

Copy link
Member

Choose a reason for hiding this comment

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

also looks like something is not quite right here - even after firing the trigger correctly, inspecting the controller logs shows:

[2017-08-13T13:48:32.233Z] [WARN] [#tid_7] [TriggersApi] action guest/a could not be invoked due to Source(SourceShape(StatefulMapConcat.out), CompositeModule [3c60a25e] Name: iterableSource Modules: (singleSource) GraphStage(SingleSource(List(ByteString(123, 34, 97, 99, 116, 105, 118, 97, 116, 105, 111, 110, 73, 100, 34, 58, 34, 98, 97, 53, 99, 57, 51, 55, 102, 51, 97, 57, 101, 52, 48, 56, 52, 56, 98, 49, 102, 100, 54, 99, 53, 54, 54, 52, 55, 48, 100, 50, 50, 34, 125)))) [41c5bf50] (unnamed) [703a0c97] copy of GraphStage(StatefulMapConcat) [50915aa5] Downstreams: single.out -> StatefulMapConcat.in Upstreams: StatefulMapConcat.in -> single.out MatValue: Atomic(singleSource[41c5bf50])) 

houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Aug 17, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Aug 17, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Sep 18, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Nov 13, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Nov 13, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Nov 13, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Nov 13, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Nov 14, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
houshengbo pushed a commit to houshengbo/openwhisk that referenced this pull request Nov 14, 2017
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
BillZong pushed a commit to BillZong/openwhisk that referenced this pull request Nov 18, 2019
* Port Controller from Spray to Akka
* Increase max-connections and Update JSON Unmarshaller
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
controller pgapproved Pipeline has approved this change. review Review for this PR has been requested and yet needs to be done.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants