-
Notifications
You must be signed in to change notification settings - Fork 14.9k
KAFKA-19931 Align broker and controller behavior for the Admin.incrementalAlterConfigs API #21005
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
base: trunk
Are you sure you want to change the base?
Changes from 2 commits
1fcfe35
cc33873
f5f78e7
59d3be2
c8d913f
f8d01df
a165eb5
48184c2
1ff2a4c
da21c8b
fdbb81c
a0198d9
d6ae031
138e8e8
72e787b
bfd3860
2a4c9ea
d388063
6df68ba
bb8c33a
16ca216
ca12d22
ac4b124
b07102e
500b7ae
8d1cf15
d896bfb
a690de7
e7251e2
374c301
59264ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,7 @@ import org.apache.kafka.common.resource.Resource.CLUSTER_NAME | |
| import org.apache.kafka.common.resource.ResourceType.{CLUSTER, GROUP, TOPIC, USER} | ||
| import org.apache.kafka.common.utils.Time | ||
| import org.apache.kafka.common.Uuid | ||
| import org.apache.kafka.common.message.IncrementalAlterConfigsRequestData.AlterConfigsResource | ||
| import org.apache.kafka.controller.ControllerRequestContext.requestTimeoutMsToDeadlineNs | ||
| import org.apache.kafka.controller.{Controller, ControllerRequestContext} | ||
| import org.apache.kafka.image.publisher.ControllerRegistrationsPublisher | ||
|
|
@@ -720,47 +721,76 @@ class ControllerApis( | |
| val configChanges = new util.HashMap[ConfigResource, | ||
| util.Map[String, Entry[AlterConfigOp.OpType, String]]]() | ||
| val brokerLoggerResponses = new util.ArrayList[AlterConfigsResourceResponse](1) | ||
| val nullConfigsErrorResults = new util.IdentityHashMap[AlterConfigsResource, ApiError]() | ||
| alterConfigsRequest.data.resources.forEach { resource => | ||
| val configResource = new ConfigResource( | ||
| ConfigResource.Type.forId(resource.resourceType), resource.resourceName()) | ||
| if (configResource.`type`().equals(ConfigResource.Type.BROKER_LOGGER)) { | ||
| val apiError = try { | ||
| runtimeLoggerManager.applyChangesForResource( | ||
| authHelper.authorize(request.context, CLUSTER_ACTION, CLUSTER, CLUSTER_NAME), | ||
| alterConfigsRequest.data().validateOnly(), | ||
| resource) | ||
| ApiError.NONE | ||
| } catch { | ||
| case t: Throwable => ApiError.fromThrowable(t) | ||
| try { | ||
| val nullUpdates = new util.ArrayList[String]() | ||
| resource.configs().forEach { config => | ||
| if (config.configOperation() != AlterConfigOp.OpType.DELETE.id() && | ||
| config.value() == null) { | ||
| nullUpdates.add(config.name()) | ||
| } | ||
| } | ||
| brokerLoggerResponses.add(new AlterConfigsResourceResponse(). | ||
| setResourceName(resource.resourceName()). | ||
| setResourceType(resource.resourceType()). | ||
| setErrorCode(apiError.error().code()). | ||
| setErrorMessage(if (apiError.isFailure) apiError.messageWithFallback() else null)) | ||
| } else if (configResource.`type`().equals(ConfigResource.Type.UNKNOWN)) { | ||
| response.responses().add(new AlterConfigsResourceResponse(). | ||
| setErrorCode(UNSUPPORTED_VERSION.code()). | ||
| setErrorMessage("Unknown resource type " + resource.resourceType() + "."). | ||
| setResourceName(resource.resourceName()). | ||
| setResourceType(resource.resourceType())) | ||
| } else if (!duplicateResources.contains(configResource)) { | ||
| val altersByName = new util.HashMap[String, Entry[AlterConfigOp.OpType, String]]() | ||
| resource.configs.forEach { config => | ||
| altersByName.put(config.name, new util.AbstractMap.SimpleEntry[AlterConfigOp.OpType, String]( | ||
| AlterConfigOp.OpType.forId(config.configOperation), config.value)) | ||
| if (!nullUpdates.isEmpty) { | ||
| throw new InvalidRequestException("Null value not supported for : " + | ||
|
||
| String.join(", ", nullUpdates)) | ||
| } | ||
| if (configChanges.put(configResource, altersByName) != null) { | ||
| duplicateResources.add(configResource) | ||
| configChanges.remove(configResource) | ||
| val configResource = new ConfigResource( | ||
| ConfigResource.Type.forId(resource.resourceType), resource.resourceName()) | ||
| if (configResource.`type`().equals(ConfigResource.Type.BROKER_LOGGER)) { | ||
| val apiError = try { | ||
| runtimeLoggerManager.applyChangesForResource( | ||
| authHelper.authorize(request.context, CLUSTER_ACTION, CLUSTER, CLUSTER_NAME), | ||
| alterConfigsRequest.data().validateOnly(), | ||
| resource) | ||
| ApiError.NONE | ||
| } catch { | ||
| case t: Throwable => ApiError.fromThrowable(t) | ||
|
||
| } | ||
| brokerLoggerResponses.add(new AlterConfigsResourceResponse(). | ||
| setResourceName(resource.resourceName()). | ||
| setResourceType(resource.resourceType()). | ||
| setErrorCode(apiError.error().code()). | ||
| setErrorMessage(if (apiError.isFailure) apiError.messageWithFallback() else null)) | ||
| } else if (configResource.`type`().equals(ConfigResource.Type.UNKNOWN)) { | ||
| response.responses().add(new AlterConfigsResourceResponse(). | ||
| setErrorCode(INVALID_REQUEST.code()). | ||
| setErrorMessage("Duplicate resource."). | ||
| setErrorCode(UNSUPPORTED_VERSION.code()). | ||
| setErrorMessage("Unknown resource type " + resource.resourceType() + "."). | ||
| setResourceName(resource.resourceName()). | ||
| setResourceType(resource.resourceType())) | ||
| } else if (!duplicateResources.contains(configResource)) { | ||
| val altersByName = new util.HashMap[String, Entry[AlterConfigOp.OpType, String]]() | ||
| resource.configs.forEach { config => | ||
| altersByName.put(config.name, new util.AbstractMap.SimpleEntry[AlterConfigOp.OpType, String]( | ||
| AlterConfigOp.OpType.forId(config.configOperation), config.value)) | ||
| } | ||
| if (configChanges.put(configResource, altersByName) != null) { | ||
| duplicateResources.add(configResource) | ||
| configChanges.remove(configResource) | ||
| response.responses().add(new AlterConfigsResourceResponse(). | ||
| setErrorCode(INVALID_REQUEST.code()). | ||
| setErrorMessage("Duplicate resource."). | ||
| setResourceName(resource.resourceName()). | ||
| setResourceType(resource.resourceType())) | ||
| } | ||
| } | ||
| } catch { | ||
| case t: Throwable => | ||
| val err = ApiError.fromThrowable(t) | ||
| error(s"Error on processing incrementalAlterConfigs request on ${resource.resourceName()}", t) | ||
| nullConfigsErrorResults.put(resource, err) | ||
| } | ||
| } | ||
| if (!nullConfigsErrorResults.isEmpty) { | ||
| nullConfigsErrorResults.forEach((resource, apiError) => { | ||
| response.responses().add(new AlterConfigsResourceResponse(). | ||
| setErrorCode(apiError.error().code()). | ||
| setErrorMessage(apiError.messageWithFallback()). | ||
| setResourceName(resource.resourceName()). | ||
| setResourceType(resource.resourceType())) | ||
| configChanges.remove(resource) | ||
| }) | ||
| } | ||
| val iterator = configChanges.keySet().iterator() | ||
| while (iterator.hasNext) { | ||
| val resource = iterator.next() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, there are quite a few pre-processing checks in ConfigAdminManager. The doc says the following. Could we restructure the code between ConfigAdminManager and ControllerApis to (1) avoid duplicates logic in verification (2) prevent missing verification in one of the two places in the future?