KAFKA-14588: Move ConfigCommand to tools module#22013
Conversation
| } | ||
|
|
||
| @ClusterTest(types = {Type.CO_KRAFT, Type.KRAFT}) | ||
| public void testIntervalMsParser(ClusterInstance clusterInstance) { |
There was a problem hiding this comment.
This was moved to ConfigCommandIntegrationTest
|
|
||
| val adminClient = adminClients.head | ||
| alterSslKeystoreUsingConfigCommand(sslProperties1, SecureExternal) | ||
| alterSslKeystore(sslProperties1, SecureExternal) |
There was a problem hiding this comment.
I did not see the point in using the tool instead of the Admin API
Rewrite ConfigCommand in Java and move it to the tools module
m1a2st
left a comment
There was a problem hiding this comment.
Thanks for the patch, left some comments
| boolean noMatchInResources = listGroupConfigResources(adminClient) | ||
| .map(resources -> resources.stream().noneMatch(resource -> resource.name().equals(name))) | ||
| .orElse(true); | ||
| if (noMatchInGroups && noMatchInResources) { |
There was a problem hiding this comment.
The semantics here don’t match the Scala version. .orElse(true) returns true when listGroupConfigResources is empty, whereas Scala’s .exists(...) returns false for an empty Option.
There was a problem hiding this comment.
Good catch! I agree this should be orElse(false)
| * Alternatively, --user-defaults, --client-defaults, --broker-defaults, or --ip-defaults may be specified in place of | ||
| * --entity-type <users|clients|brokers|ips> --entity-default, respectively. | ||
| */ | ||
| class ConfigCommand { |
There was a problem hiding this comment.
Should it be public class?
| } | ||
| } | ||
|
|
||
|
|
|
Thanks @m1a2st for the review! I pushed an update |
| String name = entityEntries.get(entityType); | ||
| if (name == null) { | ||
| return Optional.empty(); | ||
| } | ||
| String typeStr = switch (entityType) { | ||
| case ClientQuotaEntity.USER -> "user-principal"; | ||
| case ClientQuotaEntity.CLIENT_ID -> "client-id"; | ||
| case ClientQuotaEntity.IP -> "ip"; | ||
| default -> throw new IllegalArgumentException("Unknown entity type: " + entityType); | ||
| }; | ||
| String result = name.isEmpty() ? "the default " + typeStr : typeStr + " '" + name + "'"; | ||
| return Optional.of(result); |
There was a problem hiding this comment.
I think this logic differs from the Scala implementation. In Scala, map.get(entityType) returns an Option[String]:
- Key exists with a
nullvalue →Some(null)(represents the default entity) - Key does not exist →
None
In the Java version, entityEntries.get(entityType), which returns null for both cases, when the key is absent and when the key is present with a null value.
As a result, default entities are incorrectly treated as missing, which can lead to incorrect behavior.
There was a problem hiding this comment.
It's unclear if the value can ever be null. That said, it's easy to handle that case so I pushed an update. Thanks
| * Alternatively, --user-defaults, --client-defaults, --broker-defaults, or --ip-defaults may be specified in place of | ||
| * --entity-type <users|clients|brokers|ips> --entity-default, respectively. | ||
| */ | ||
| public class ConfigCommand { |
There was a problem hiding this comment.
There are two spaces.
| public class ConfigCommand { | |
| public class ConfigCommand { |
| // (KIP-1142) 4.1+ admin client vs older broker: treat UnsupportedVersionException and ClusterAuthorizationException as None | ||
| if (ee.getCause() instanceof UnsupportedVersionException) return Optional.empty(); | ||
| if (ee.getCause() instanceof ClusterAuthorizationException) return Optional.empty(); | ||
| else throw (Exception) ee.getCause(); |
There was a problem hiding this comment.
| else throw (Exception) ee.getCause(); | |
| else throw (Exception) ee.getCause(); |
| .filter(options::has) | ||
| .map(options::valueOf) | ||
| .forEach(ipEntity -> { | ||
| if (!isValidIpEntity(ipEntity)) |
There was a problem hiding this comment.
We could streamline this lambda if isValidIpEntity threw an exception directly.
static void validateIpEntity(String ipEntity, String entityType) {
try {
InetAddress.getByName(ipEntity);
} catch (UnknownHostException uhe) {
throw new IllegalArgumentException("The entity name for " + entityType + " must be a valid IP or resolvable host, but it is: " + ipEntity);
}
} if (hasEntityName && entityTypeVals.contains(IP_TYPE)) {
Stream.of(entityName, ip)
.filter(options::has)
.map(options::valueOf)
.forEach(ipEntity -> validateIpEntity(ipEntity, entityTypeVals.get(0)));
}There was a problem hiding this comment.
Yes that's slightly more elegant, good idea!
chia7712
left a comment
There was a problem hiding this comment.
LGTM. The comments I left can be addressed in a follow-up. We'll open a separate PR for them so we can merge this big migration first.
| private static final Logger LOG = LoggerFactory.getLogger(ConfigCommand.class); | ||
|
|
||
| private static final String BROKER_DEFAULT_ENTITY_NAME = ""; | ||
| private static final List<String> BROKER_SUPPORTED_CONFIG_TYPES; |
There was a problem hiding this comment.
private static final List<String> BROKER_SUPPORTED_CONFIG_TYPES = Stream.concat(
Stream.of(BROKER_LOGGER_CONFIG_TYPE),
Arrays.stream(ConfigType.values()).map(ConfigType::value)).toList();| } | ||
| } | ||
|
|
||
| List<String> entities; |
There was a problem hiding this comment.
we could use Set<String> to avoid unnecessary copy
| } | ||
| } | ||
|
|
||
| for (String entity : entities) { |
There was a problem hiding this comment.
It seems we could use batch query here for a slight optimization
| throw new InvalidConfigurationException("Invalid config(s): " + String.join(",", invalidConfigs)); | ||
|
|
||
| ConfigResource configResource = new ConfigResource(resourceType, entityNameHead); | ||
| AlterConfigsOptions alterOptions = new AlterConfigsOptions().timeoutMs(30000).validateOnly(false); |
There was a problem hiding this comment.
.validateOnly(false); is redundant
| throw new InvalidConfigurationException("Invalid broker logger(s): " + String.join(",", invalidBrokerLoggers)); | ||
|
|
||
| ConfigResource configResource = new ConfigResource(ConfigResource.Type.BROKER_LOGGER, entityName); | ||
| AlterConfigsOptions alterOptions = new AlterConfigsOptions().timeoutMs(30000).validateOnly(false); |
| } | ||
| ClientQuotaEntity entity = new ClientQuotaEntity(alterEntityMap); | ||
|
|
||
| AlterClientQuotasOptions alterOptions = new AlterClientQuotasOptions().validateOnly(false); |
|
Hi @chia7712, I would like to handle this. |
go ahead |
| * An entity described or altered by the command may be one of: | ||
| * <ul> | ||
| * <li> topic: --topic <topic> OR --entity-type topics --entity-name <topic> | ||
| * <li> client: --client <client> OR --entity-type clients --entity-name <client-id> |
There was a problem hiding this comment.
@mimaison btw, should we update <client> to <client-id> here? It seems <client> was preserved from the Scala source, but <client-id> would be more internally consistent. If that sounds good to you, I'd be happy to include it in the follow-up PR.
Rewrite ConfigCommand in Java and move it to the tools module
Reviewers: Ken Huang s7133700@gmail.com, Christo Lolov
lolovc@amazon.com, Chia-Ping Tsai chia7712@gmail.com, Nick Guo
lansg0504@gmail.com