-
Notifications
You must be signed in to change notification settings - Fork 10
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
Use admin client to confirm cluster is correctly started #24
Merged
SamBarker
merged 12 commits into
kroxylicious:main
from
SamBarker:issue-7-using_awaitility
Feb 16, 2023
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
37694ae
Unified bootstrap string generation
SamBarker c948586
Ensure correct number of brokers are started before injecting clusters
SamBarker 2766f83
Fix style issues
SamBarker f7268b6
Use try with resources to ensure the client is closed.
SamBarker 006f39b
Make `buildBootstrapServers` private
SamBarker af8b4cf
Introduce `listenAddress` and `advertisedAddress` to the endpoint.
SamBarker e476870
Add an anonymous listener for the extension to ensure it can always d…
SamBarker 713d51b
typo fix
SamBarker 208737e
`ensureExpectedBrokerCountInCluster` -> `awaitExpectedBrokerCountInCl…
SamBarker 4f7529e
RedHat -> kroxylicious.io
SamBarker 06823d6
`b` -> `controllerId`
SamBarker 8475323
update impsort config and reformat.
SamBarker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,19 @@ | |
*/ | ||
package io.kroxylicious.testing.kafka.common; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.Singular; | ||
import lombok.ToString; | ||
import org.apache.kafka.clients.CommonClientConfigs; | ||
import org.apache.kafka.common.Uuid; | ||
import org.apache.kafka.common.config.SaslConfigs; | ||
import org.apache.kafka.common.config.SslConfigs; | ||
import org.apache.kafka.common.config.internals.BrokerSecurityConfigs; | ||
import org.apache.kafka.common.security.auth.SecurityProtocol; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.junit.jupiter.api.TestInfo; | ||
|
||
import java.io.IOException; | ||
import java.lang.annotation.Annotation; | ||
import java.nio.file.Files; | ||
|
@@ -20,25 +33,13 @@ | |
import java.util.Properties; | ||
import java.util.Set; | ||
import java.util.TreeMap; | ||
import java.util.function.IntFunction; | ||
import java.util.function.Supplier; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
import java.util.stream.Stream; | ||
|
||
import org.apache.kafka.clients.CommonClientConfigs; | ||
import org.apache.kafka.common.Uuid; | ||
import org.apache.kafka.common.config.SaslConfigs; | ||
import org.apache.kafka.common.config.SslConfigs; | ||
import org.apache.kafka.common.config.internals.BrokerSecurityConfigs; | ||
import org.apache.kafka.common.security.auth.SecurityProtocol; | ||
import org.junit.jupiter.api.TestInfo; | ||
|
||
import io.kroxylicious.testing.kafka.api.KafkaClusterProvisioningStrategy; | ||
import io.kroxylicious.testing.kafka.common.KafkaClusterConfig.KafkaEndpoints.Endpoint; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.Singular; | ||
import lombok.ToString; | ||
|
||
@Builder(toBuilder = true) | ||
@Getter | ||
|
@@ -106,7 +107,6 @@ public static boolean supportsConstraint(Class<? extends Annotation> annotation) | |
} | ||
|
||
public static KafkaClusterConfig fromConstraints(List<Annotation> annotations) { | ||
System.Logger logger = System.getLogger(KafkaClusterProvisioningStrategy.class.getName()); | ||
var builder = builder(); | ||
builder.brokersNum(1); | ||
boolean sasl = false; | ||
|
@@ -126,7 +126,8 @@ public static KafkaClusterConfig fromConstraints(List<Annotation> annotations) { | |
tls = true; | ||
try { | ||
builder.brokerKeytoolCertificateGenerator(new KeytoolCertificateGenerator()); | ||
} catch (IOException e) { | ||
} | ||
catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
@@ -168,8 +169,10 @@ public Stream<ConfigHolder> getBrokerConfigs(Supplier<KafkaEndpoints> endPointCo | |
|
||
var interBrokerEndpoint = kafkaEndpoints.getInterBrokerEndpoint(brokerNum); | ||
var clientEndpoint = kafkaEndpoints.getClientEndpoint(brokerNum); | ||
var anonEndpoint = kafkaEndpoints.getAnonEndpoint(brokerNum); | ||
|
||
// - EXTERNAL: used for communications to/from consumers/producers | ||
// - EXTERNAL: used for communications to/from consumers/producers optionally with authentication | ||
// - ANON: used for communications to/from consumers/producers without authentication primarily for the extension to validate the cluster | ||
// - INTERNAL: used for inter-broker communications (always no auth) | ||
// - CONTROLLER: used for inter-broker controller communications (kraft - always no auth) | ||
|
||
|
@@ -180,20 +183,25 @@ public Stream<ConfigHolder> getBrokerConfigs(Supplier<KafkaEndpoints> endPointCo | |
var advertisedListeners = new TreeMap<String, String>(); | ||
|
||
protocolMap.put("EXTERNAL", externalListenerTransport); | ||
listeners.put("EXTERNAL", clientEndpoint.getBind().toString()); | ||
advertisedListeners.put("EXTERNAL", clientEndpoint.getConnect().toString()); | ||
listeners.put("EXTERNAL", clientEndpoint.listenAddress()); | ||
advertisedListeners.put("EXTERNAL", clientEndpoint.advertisedAddress()); | ||
|
||
protocolMap.put("ANON", SecurityProtocol.PLAINTEXT.name()); | ||
listeners.put("ANON", anonEndpoint.listenAddress()); | ||
advertisedListeners.put("ANON", anonEndpoint.advertisedAddress()); | ||
|
||
protocolMap.put("INTERNAL", SecurityProtocol.PLAINTEXT.name()); | ||
listeners.put("INTERNAL", interBrokerEndpoint.getBind().toString()); | ||
advertisedListeners.put("INTERNAL", interBrokerEndpoint.getConnect().toString()); | ||
listeners.put("INTERNAL", interBrokerEndpoint.listenAddress()); | ||
advertisedListeners.put("INTERNAL", interBrokerEndpoint.advertisedAddress()); | ||
putConfig(server, "inter.broker.listener.name", "INTERNAL"); | ||
|
||
if (isKraftMode()) { | ||
putConfig(server, "node.id", Integer.toString(brokerNum)); // Required by Kafka 3.3 onwards. | ||
|
||
var controllerEndpoint = kafkaEndpoints.getControllerEndpoint(brokerNum); | ||
var quorumVoters = IntStream.range(0, kraftControllers) | ||
.mapToObj(b -> String.format("%d@%s", b, kafkaEndpoints.getControllerEndpoint(b).getConnect().toString())).collect(Collectors.joining(",")); | ||
.mapToObj(controllerId -> String.format("%d@//%s", controllerId, kafkaEndpoints.getControllerEndpoint(controllerId).connectAddress())) | ||
.collect(Collectors.joining(",")); | ||
putConfig(server, "controller.quorum.voters", quorumVoters); | ||
putConfig(server, "controller.listener.names", "CONTROLLER"); | ||
protocolMap.put("CONTROLLER", SecurityProtocol.PLAINTEXT.name()); | ||
|
@@ -241,7 +249,9 @@ public Stream<ConfigHolder> getBrokerConfigs(Supplier<KafkaEndpoints> endPointCo | |
throw new RuntimeException("brokerKeytoolCertificateGenerator needs to be initialized when calling KafkaClusterConfig"); | ||
} | ||
try { | ||
brokerKeytoolCertificateGenerator.generateSelfSignedCertificateEntry("[email protected]", clientEndpoint.getConnect().getHost(), "KI", "RedHat", null, null, | ||
brokerKeytoolCertificateGenerator.generateSelfSignedCertificateEntry("[email protected]", clientEndpoint.getConnect().getHost(), "Dev", | ||
"Kroxylicious.io", null, | ||
null, | ||
"US"); | ||
if (clientKeytoolCertificateGenerator != null && Path.of(clientKeytoolCertificateGenerator.getCertFilePath()).toFile().exists()) { | ||
server.put(BrokerSecurityConfigs.SSL_CLIENT_AUTH_CONFIG, "required"); | ||
|
@@ -265,8 +275,8 @@ public Stream<ConfigHolder> getBrokerConfigs(Supplier<KafkaEndpoints> endPointCo | |
// Disable delay during every re-balance | ||
putConfig(server, "group.initial.rebalance.delay.ms", Integer.toString(0)); | ||
|
||
properties.add(new ConfigHolder(server, clientEndpoint.getConnect().getPort(), | ||
String.format("%s:%d", clientEndpoint.getConnect().getHost(), clientEndpoint.getConnect().getPort()), brokerNum, kafkaKraftClusterId)); | ||
properties.add(new ConfigHolder(server, clientEndpoint.getConnect().getPort(), anonEndpoint.getConnect().getPort(), | ||
clientEndpoint.connectAddress(), brokerNum, kafkaKraftClusterId)); | ||
} | ||
|
||
return properties.stream(); | ||
|
@@ -279,34 +289,60 @@ private static void putConfig(Properties server, String key, String value) { | |
} | ||
} | ||
|
||
@NotNull | ||
public String buildClientBootstrapServers(KafkaEndpoints endPointConfig) { | ||
return buildBootstrapServers(getBrokersNum(), endPointConfig::getClientEndpoint); | ||
} | ||
|
||
@NotNull | ||
public String buildAnonBootstrapServers(KafkaEndpoints endPointConfig) { | ||
return buildBootstrapServers(getBrokersNum(), endPointConfig::getAnonEndpoint); | ||
} | ||
|
||
@NotNull | ||
public String buildControllerBootstrapServers(KafkaEndpoints kafkaEndpoints) { | ||
return buildBootstrapServers(getBrokersNum(), kafkaEndpoints::getControllerEndpoint); | ||
} | ||
|
||
@NotNull | ||
public String buildInterBrokerBootstrapServers(KafkaEndpoints kafkaEndpoints) { | ||
return buildBootstrapServers(getBrokersNum(), kafkaEndpoints::getInterBrokerEndpoint); | ||
} | ||
|
||
public Map<String, Object> getAnonConnectConfigForCluster(KafkaEndpoints kafkaEndpoints) { | ||
return getConnectConfigForCluster(buildAnonBootstrapServers(kafkaEndpoints), null, null, null, null); | ||
} | ||
|
||
public Map<String, Object> getConnectConfigForCluster(String bootstrapServers) { | ||
if (saslMechanism != null) { | ||
Map<String, String> users = getUsers(); | ||
if (!users.isEmpty()) { | ||
Map.Entry<String, String> first = users.entrySet().iterator().next(); | ||
return getConnectConfigForCluster(bootstrapServers, first.getKey(), first.getKey()); | ||
return getConnectConfigForCluster(bootstrapServers, first.getKey(), first.getKey(), getSecurityProtocol(), getSaslMechanism()); | ||
} | ||
else { | ||
return getConnectConfigForCluster(bootstrapServers, null, null); | ||
return getConnectConfigForCluster(bootstrapServers, null, null, getSecurityProtocol(), getSaslMechanism()); | ||
} | ||
} | ||
else { | ||
return getConnectConfigForCluster(bootstrapServers, null, null); | ||
return getConnectConfigForCluster(bootstrapServers, null, null, getSecurityProtocol(), getSaslMechanism()); | ||
} | ||
} | ||
|
||
public Map<String, Object> getConnectConfigForCluster(String bootstrapServers, String user, String password) { | ||
return getConnectConfigForCluster(bootstrapServers, user, password, getSecurityProtocol(), getSaslMechanism()); | ||
} | ||
|
||
public Map<String, Object> getConnectConfigForCluster(String bootstrapServers, String user, String password, String securityProtocol, String saslMechanism) { | ||
Map<String, Object> kafkaConfig = new HashMap<>(); | ||
String saslMechanism = getSaslMechanism(); | ||
String securityProtocol = getSecurityProtocol(); | ||
|
||
if (securityProtocol != null) { | ||
kafkaConfig.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); | ||
|
||
if (securityProtocol.contains("SSL")) { | ||
String clientTrustStoreFilePath; | ||
String clientTrustStorePassword; | ||
if(clientKeytoolCertificateGenerator != null) { | ||
if (clientKeytoolCertificateGenerator != null) { | ||
if (Path.of(clientKeytoolCertificateGenerator.getKeyStoreLocation()).toFile().exists()) { | ||
// SSL client auth case | ||
kafkaConfig.put(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, clientKeytoolCertificateGenerator.getKeyStoreLocation()); | ||
|
@@ -315,7 +351,8 @@ public Map<String, Object> getConnectConfigForCluster(String bootstrapServers, S | |
} | ||
try { | ||
clientKeytoolCertificateGenerator.generateTrustStore(brokerKeytoolCertificateGenerator.getCertFilePath(), "client"); | ||
} catch (GeneralSecurityException | IOException e) { | ||
} | ||
catch (GeneralSecurityException | IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
clientTrustStoreFilePath = clientKeytoolCertificateGenerator.getTrustStoreLocation(); | ||
|
@@ -328,8 +365,10 @@ public Map<String, Object> getConnectConfigForCluster(String bootstrapServers, S | |
clientTrustStore = Paths.get(certsDirectory.toAbsolutePath().toString(), "kafka.truststore.jks"); | ||
certsDirectory.toFile().deleteOnExit(); | ||
clientTrustStore.toFile().deleteOnExit(); | ||
brokerKeytoolCertificateGenerator.generateTrustStore(brokerKeytoolCertificateGenerator.getCertFilePath(), "client", clientTrustStore.toAbsolutePath().toString()); | ||
} catch (GeneralSecurityException | IOException e) { | ||
brokerKeytoolCertificateGenerator.generateTrustStore(brokerKeytoolCertificateGenerator.getCertFilePath(), "client", | ||
clientTrustStore.toAbsolutePath().toString()); | ||
} | ||
catch (GeneralSecurityException | IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
clientTrustStoreFilePath = clientTrustStore.toAbsolutePath().toString(); | ||
|
@@ -371,11 +410,19 @@ public String clusterId() { | |
return isKraftMode() ? kafkaKraftClusterId : null; | ||
} | ||
|
||
private String buildBootstrapServers(Integer numBrokers, IntFunction<KafkaEndpoints.EndpointPair> brokerEndpoint) { | ||
return IntStream.range(0, numBrokers) | ||
.mapToObj(brokerEndpoint) | ||
.map(KafkaEndpoints.EndpointPair::connectAddress) | ||
.collect(Collectors.joining(",")); | ||
} | ||
|
||
@Builder | ||
@Getter | ||
public static class ConfigHolder { | ||
private final Properties properties; | ||
private final Integer externalPort; | ||
private final Integer anonPort; | ||
private final String endpoint; | ||
private final int brokerNum; | ||
private final String kafkaKraftClusterId; | ||
|
@@ -388,11 +435,23 @@ public interface KafkaEndpoints { | |
class EndpointPair { | ||
private final Endpoint bind; | ||
private final Endpoint connect; | ||
|
||
public String connectAddress() { | ||
return String.format("%s:%d", connect.host, connect.port); | ||
} | ||
|
||
public String listenAddress() { | ||
return String.format("//%s:%d", bind.host, bind.port); | ||
} | ||
|
||
public String advertisedAddress() { | ||
return String.format("//%s:%d", connect.host, connect.port); | ||
} | ||
} | ||
|
||
@Builder | ||
@Getter | ||
public class Endpoint { | ||
class Endpoint { | ||
private final String host; | ||
private final int port; | ||
|
||
|
@@ -412,5 +471,7 @@ public String toString() { | |
EndpointPair getControllerEndpoint(int brokerId); | ||
|
||
EndpointPair getClientEndpoint(int brokerId); | ||
|
||
EndpointPair getAnonEndpoint(int brokerId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
The
%d
is locale-dependent, according to the "Number Localization Algorithm" section inFormatter
Javadoc, which says:To avoid this I would use concatenation using
+
.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.
Wow, I never knew that. I've written so much code that must fall on its face in the Arabic world.