Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
Add administrative interface to invoker and controller to reconfigure runtimes #4790
Conversation
| extractCredentials { | ||
| case Some(BasicHttpCredentials(username, password)) => | ||
| if (username == controllerUsername && password == controllerPassword) { | ||
| entity(as[String]) { runtime => |
ningyougang
Jan 7, 2020
Author
Contributor
Usually, entity(as[Runtimes]) may be better, but if we apply this, need to change Runtimes to support convert json to entity Runtimes, and on the other hand, runtime.json's format doesn't match Runtimes entity, need to change a lot if use entity(as[Runtimes]) to receive the data.
Fortunately,we can reuse openwhisk itself's initialize method, just convert the runtime string to Runtimes.
So here, i think pass runtime string would be ok.
Usually, entity(as[Runtimes]) may be better, but if we apply this, need to change Runtimes to support convert json to entity Runtimes, and on the other hand, runtime.json's format doesn't match Runtimes entity, need to change a lot if use entity(as[Runtimes]) to receive the data.
Fortunately,we can reuse openwhisk itself's initialize method, just convert the runtime string to Runtimes.
So here, i think pass runtime string would be ok.
| logging.error(this, s"Received invalid runtimes manifest") | ||
| complete(s"Received invalid runtimes manifest") | ||
| } else { | ||
| parameter('limit.?) { limit => |
ningyougang
Jan 7, 2020
•
Author
Contributor
Support passed limit invokers, e.g. ?limit=0:1 ( sent to invoker0, invoker1 only)
And the config runtime request can be sent to some limited invokers which included in managed invokers as well.
Support passed limit invokers, e.g. ?limit=0:1 ( sent to invoker0, invoker1 only)
And the config runtime request can be sent to some limited invokers which included in managed invokers as well.
| @@ -27,7 +27,7 @@ whisk.spi { | |||
| EntitlementSpiProvider = org.apache.openwhisk.core.entitlement.LocalEntitlementProvider | |||
| AuthenticationDirectiveProvider = org.apache.openwhisk.core.controller.BasicAuthenticationDirective | |||
| InvokerProvider = org.apache.openwhisk.core.invoker.InvokerReactive | |||
| InvokerServerProvider = org.apache.openwhisk.core.invoker.DefaultInvokerServer | |||
| InvokerServerProvider = org.apache.openwhisk.core.invoker.InvokerServer | |||
style95
Jan 7, 2020
Member
This should be reverted.
This should be reverted.
ningyougang
Jan 13, 2020
Author
Contributor
DefaultInvokerServer is already reverted
DefaultInvokerServer is already reverted
| val invokerArray = targetValue.split(":") | ||
| val beginIndex = invokerArray(0).toInt | ||
| val finishIndex = invokerArray(1).toInt | ||
| if (finishIndex < beginIndex) { |
style95
Jan 7, 2020
Member
Would be great to support 0:0 case as well.
Ansible is using the same way.
Would be great to support 0:0 case as well.
Ansible is using the same way.
ningyougang
Jan 13, 2020
Author
Contributor
Yes, already support 0:0 case.
Yes, already support 0:0 case.
5be2eab
to
8573912
Codecov Report
@@ Coverage Diff @@
## master #4790 +/- ##
==========================================
- Coverage 82.35% 75.49% -6.87%
==========================================
Files 198 199 +1
Lines 9021 9123 +102
Branches 353 379 +26
==========================================
- Hits 7429 6887 -542
- Misses 1592 2236 +644
Continue to review full report at Codecov.
|
| @@ -203,6 +203,9 @@ | |||
| "CONFIG_whisk_db_activationsFilterDdoc": "{{ db_whisk_activations_filter_ddoc | default() }}" | |||
| "CONFIG_whisk_userEvents_enabled": "{{ user_events | default(false) | lower }}" | |||
|
|
|||
| "CONFIG_whisk_credentials_controller_username": "{{ controller.username }}" | |||
| "CONFIG_whisk_credentials_controller_password": "{{ controller.password }}" | |||
|
|
|||
ningyougang
Jan 13, 2020
Author
Contributor
If generate controller/invoker credentials to container's /conf/, the Standalone Tests run failed due to lack /conf/ under the build machine
On the other hand, couchdb credentials is passed via environment variable, so controller/invoker credentials can be passed via environment variable as well.
If generate controller/invoker credentials to container's /conf/, the Standalone Tests run failed due to lack /conf/ under the build machine
On the other hand, couchdb credentials is passed via environment variable, so controller/invoker credentials can be passed via environment variable as well.
556b5bd
to
1ec0720
|
Already added test cases |
|
The limiting of a runtime to specific invokers is related to how invokers are partitioned for black box vs non-black box invokers (based on a % of active invokers in the system). I suggest splitting out the "limit" feature into a separate PR and having a mechanism for specifying distributions of runtimes to invokers and letting the load balancer calculate the partition. That isn't powerful enough to when it is desirable to route to a specific invoker - is that a desired feature (vs limiting to a range of invokers active in the system)? |
| val execManifest = ExecManifest.initialize(runtime) | ||
| if (execManifest.isFailure) { | ||
| logging.error(this, s"Received invalid runtimes manifest") | ||
| complete(s"Received invalid runtimes manifest") |
rabbah
Jan 26, 2020
Member
this completes the https request with status code 200 - is that what was intended?
this completes the https request with status code 200 - is that what was intended?
ningyougang
Feb 27, 2020
Author
Contributor
Hi, almost forgot this patch :(
I changed like below
logging.info(this, s"received invalid runtimes manifest")
complete(StatusCodes.BadRequest)
Hi, almost forgot this patch :(
I changed like below
logging.info(this, s"received invalid runtimes manifest")
complete(StatusCodes.BadRequest)| entity(as[String]) { runtime => | ||
| val execManifest = ExecManifest.initialize(runtime) | ||
| if (execManifest.isFailure) { | ||
| logging.error(this, s"Received invalid runtimes manifest") |
rabbah
Jan 26, 2020
Member
.error is for internal errors, where here this is user input error, i think .info is better.
.error is for internal errors, where here this is user input error, i think .info is better.
ningyougang
Feb 27, 2020
Author
Contributor
Already changed to .info
Already changed to .info
| parameter('limit.?) { limit => | ||
| limit match { | ||
| case Some(targetValue) => | ||
| val pattern = "\\d+:\\d" |
rabbah
Jan 26, 2020
Member
use
Suggested change
val pattern = "\\d+:\\d"
val pattern = """\d+:\d"""
using triple quotes allows you to use a regex without escape characters.
use
| val pattern = "\\d+:\\d" | |
| val pattern = """\d+:\d""" |
using triple quotes allows you to use a regex without escape characters.
ningyougang
Feb 27, 2020
Author
Contributor
Changed
Changed
| if (username == controllerCredentials.username && password == controllerCredentials.password) { | ||
| entity(as[String]) { runtime => | ||
| val execManifest = ExecManifest.initialize(runtime) | ||
| if (execManifest.isFailure) { |
rabbah
Jan 26, 2020
Member
scala nit: you can use a case match here instead.
scala nit: you can use a case match here instead.
ningyougang
Feb 27, 2020
Author
Contributor
I just follow other codes using execManifest.isFailure to judge whether success or fail
I just follow other codes using execManifest.isFailure to judge whether success or fail
| complete(s"finishIndex can't be less than beginIndex") | ||
| } else { | ||
| val targetInvokers = (beginIndex to finishIndex).toList | ||
| loadBalancer.sendRuntimeToInvokers(runtime, Some(targetInvokers)) |
rabbah
Jan 26, 2020
Member
is there a later validation to check that the invoker indexing is in range?
is there a later validation to check that the invoker indexing is in range?
ningyougang
Feb 27, 2020
Author
Contributor
Yes, validate it in ShardingContainerPoolLoadbalacer.scala
/** send runtime to invokers*/
override def sendRuntimeToInvokers(runtime: String, targetInvokers: Option[List[Int]]): Unit = {
val runtimeMessage = RuntimeMessage(runtime)
schedulingState.managedInvokers.filter { manageInvoker =>
targetInvokers.getOrElse(schedulingState.managedInvokers.map(_.id.instance)).contains(manageInvoker.id.instance)
} foreach { invokerHealth =>
val topic = s"invoker${invokerHealth.id.toInt}"
messageProducer.send(topic, runtimeMessage).andThen {
case Success(_) =>
logging.info(this, s"Successfully posted runtime to topic $topic")
case Failure(_) =>
logging.error(this, s"Failed posted runtime to topic $topic")
}
}
}
Yes, validate it in ShardingContainerPoolLoadbalacer.scala
/** send runtime to invokers*/
override def sendRuntimeToInvokers(runtime: String, targetInvokers: Option[List[Int]]): Unit = {
val runtimeMessage = RuntimeMessage(runtime)
schedulingState.managedInvokers.filter { manageInvoker =>
targetInvokers.getOrElse(schedulingState.managedInvokers.map(_.id.instance)).contains(manageInvoker.id.instance)
} foreach { invokerHealth =>
val topic = s"invoker${invokerHealth.id.toInt}"
messageProducer.send(topic, runtimeMessage).andThen {
case Success(_) =>
logging.info(this, s"Successfully posted runtime to topic $topic")
case Failure(_) =>
logging.error(this, s"Failed posted runtime to topic $topic")
}
}
}
| limit match { | ||
| case Some(targetValue) => | ||
| val pattern = "\\d+:\\d" | ||
| if (targetValue.matches(pattern)) { |
rabbah
Jan 26, 2020
Member
scala nit: you can rewrite this either if/else and nested clauses using case matching on regex.
scala nit: you can rewrite this either if/else and nested clauses using case matching on regex.
ningyougang
Feb 27, 2020
•
Author
Contributor
hm.. can you show a example? my codes like below
if (targetValue.matches(pattern)) {
val invokerArray = targetValue.split(":")
val beginIndex = invokerArray(0).toInt
val finishIndex = invokerArray(1).toInt
if (finishIndex < beginIndex) {
logging.info(this, "finishIndex can't be less than beginIndex")
complete(StatusCodes.BadRequest)
} else {
val targetInvokers = (beginIndex to finishIndex).toList
loadBalancer.sendRuntimeToInvokers(runtime, Some(targetInvokers))
logging.info(this, "config runtime request is already sent to target invokers")
complete(StatusCodes.BadRequest)
}
} else {
logging.info(this, "limit value can't match [beginIndex:finishIndex]")
complete(StatusCodes.BadRequest)
}
hm.. can you show a example? my codes like below
if (targetValue.matches(pattern)) {
val invokerArray = targetValue.split(":")
val beginIndex = invokerArray(0).toInt
val finishIndex = invokerArray(1).toInt
if (finishIndex < beginIndex) {
logging.info(this, "finishIndex can't be less than beginIndex")
complete(StatusCodes.BadRequest)
} else {
val targetInvokers = (beginIndex to finishIndex).toList
loadBalancer.sendRuntimeToInvokers(runtime, Some(targetInvokers))
logging.info(this, "config runtime request is already sent to target invokers")
complete(StatusCodes.BadRequest)
}
} else {
logging.info(this, "limit value can't match [beginIndex:finishIndex]")
complete(StatusCodes.BadRequest)
}
|
|
||
| private val invokerCredentials = loadConfigOrThrow[InvokerCredentials](ConfigKeys.invokerCredentials) | ||
|
|
||
| override def routes(implicit transid: TransactionId): Route = { |
rabbah
Jan 26, 2020
Member
how will new runtime images get pulled to the invoker nodes? are you assuming they get pulled either via stem-cell "run" or when an action actually runs in the future?
how will new runtime images get pulled to the invoker nodes? are you assuming they get pulled either via stem-cell "run" or when an action actually runs in the future?
ningyougang
Feb 27, 2020
•
Author
Contributor
When send to reqeust to backend (e.g. curl -u xxx:xxx-X POST http://xxx:xxx:10001/config/runtime), it will pull new runtime image immediately and create prewarm container if the stem-cell > 0.
When send to reqeust to backend (e.g. curl -u xxx:xxx-X POST http://xxx:xxx:10001/config/runtime), it will pull new runtime image immediately and create prewarm container if the stem-cell > 0.
| @@ -258,10 +258,40 @@ public static int getControllerBasePort() { | |||
| return Integer.parseInt(whiskProperties.getProperty("controller.host.basePort")); | |||
| } | |||
|
|
|||
| public static String getControllerProtocol() { | |||
rabbah
Jan 26, 2020
Member
i dont think any of these changes are necessary if you read configs through pureconfig.
i dont think any of these changes are necessary if you read configs through pureconfig.
ningyougang
Feb 27, 2020
Author
Contributor
Yes, already deleted.
Yes, already deleted.
| response.status shouldBe StatusCodes.OK | ||
| } | ||
|
|
||
| Thread.sleep(5.seconds.toMillis) |
rabbah
Jan 26, 2020
Member
why?
why?
ningyougang
Feb 27, 2020
Author
Contributor
Just make previous's request handled successfully, because previous is a ansyc operation.
Just make previous's request handled successfully, because previous is a ansyc operation.
81caa3f
to
3e0603d
hm.. this patch just reconfigure the runtime.json(recreate prewarm containers according to passed runtime string), i don't understand what's mean
Yes, it is not powerful, so i deleted |
| case None => | ||
| loadBalancer.sendRuntimeToInvokers(runtime, None) | ||
| logging.info(this, "config runtime request is already sent to all managed invokers") | ||
| complete(StatusCodes.Accepted) |
ningyougang
Feb 27, 2020
Author
Contributor
I changed to complete(StatusCodes.Accepted), because it is a async operation.
I changed to complete(StatusCodes.Accepted), because it is a async operation.
| @@ -279,6 +285,23 @@ class ContainerPool(childFactory: ActorRefFactory => ActorRef, | |||
| case RescheduleJob => | |||
| freePool = freePool - sender() | |||
| busyPool = busyPool - sender() | |||
| case prewarmConfigList: PreWarmConfigList => | |||
| laststPrewarmConfig = prewarmConfigList.list | |||
ningyougang
Feb 27, 2020
Author
Contributor
Add a inner variable laststPrewarmConfig, just make it pointed to the passed config runtime.
Because in this patch: #4698
when received ContainerRemoved(may be prewarm container crashed), it will backfill the prewarm.
Add a inner variable laststPrewarmConfig, just make it pointed to the passed config runtime.
Because in this patch: #4698
when received ContainerRemoved(may be prewarm container crashed), it will backfill the prewarm.
be5a6e5
to
6750ea9
fc63c07
to
12201bb
|
Rebased. |
6733631
to
30a3e64
|
Rebased |
| passedConfig.exec.kind == config.exec.kind && passedConfig.memoryLimit == config.memoryLimit) | ||
| .getOrElse(config) | ||
| } | ||
| latestPrewarmConfig = newPrewarmConfig |
ningyougang
Jun 8, 2020
•
Author
Contributor
Support just change specify runtime config as well, e.g.
Let's assume runtimes.json include nodejs:12, python:2, swift4.1, we can change nodejs:12 runtime only via just pass nodejs:12 runtime info, for other runtimes, just use previous runtime info directly.
Support just change specify runtime config as well, e.g.
Let's assume runtimes.json include nodejs:12, python:2, swift4.1, we can change nodejs:12 runtime only via just pass nodejs:12 runtime info, for other runtimes, just use previous runtime info directly.
Codecov Report
@@ Coverage Diff @@
## master #4790 +/- ##
==========================================
- Coverage 83.56% 76.99% -6.57%
==========================================
Files 201 202 +1
Lines 9515 9613 +98
Branches 400 415 +15
==========================================
- Hits 7951 7402 -549
- Misses 1564 2211 +647
Continue to review full report at Codecov.
|
Sometimes, admin may want to reinitalize the runtime config, e.g. nodejs:10 prewarm container number is less, lead to
cold start, in order to handle user's request as soon as possible, admin may want to reinitalize the runtime configuration to increase the nodejs:10 prewarm containers.And admin may want to reinitalize the runtime config on some limited invokers, this patch includes below changes
Description
Related issue and scope
My changes affect the following components
Types of changes
Checklist: