-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
461 additions
and
213 deletions.
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
48 changes: 48 additions & 0 deletions
48
core/runtime/src/main/java/io/quarkus/runtime/configuration/RandomPorts.java
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 |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package io.quarkus.runtime.configuration; | ||
|
||
import static java.net.InetAddress.getByName; | ||
|
||
import java.io.IOException; | ||
import java.net.InetSocketAddress; | ||
import java.net.ServerSocket; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* Services requiring random ports should use {@link RandomPorts} to assign a port during configuration, usually in a | ||
* {@link io.smallrye.config.ConfigSourceFactory}. This allows the reference of the assigned port in configuration | ||
* expressions. | ||
*/ | ||
public class RandomPorts { | ||
private static final Map<Integer, Integer> randomPorts = new ConcurrentHashMap<>(); | ||
|
||
/** | ||
* Assigns a random port. | ||
* <p> | ||
* The <code>port</code> parameter is used to cache the assignments of random ports, meaning that if a call is | ||
* executed to assign a random port to <code>-1</code>, every call with <code>port</code> equals to | ||
* <code>-1</code> returns the same assigned random port. | ||
* | ||
* @param host the host the server will bind to. | ||
* @param port the port number, must be greater than zero. | ||
* @return the assigned random port. | ||
*/ | ||
public static int get(final String host, final int port) { | ||
if (port > 0) { | ||
throw new IllegalArgumentException("port must be greater than zero"); | ||
} | ||
return randomPorts.computeIfAbsent(port, new Function<Integer, Integer>() { | ||
@Override | ||
public Integer apply(final Integer socketAddress) { | ||
try (ServerSocket serverSocket = new ServerSocket()) { | ||
serverSocket.setReuseAddress(true); | ||
serverSocket.bind(new InetSocketAddress(getByName(host), 0), 0); | ||
return serverSocket.getLocalPort(); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
}); | ||
} | ||
} |
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
103 changes: 103 additions & 0 deletions
103
extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcConfig.java
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 |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package io.quarkus.grpc.runtime.config; | ||
|
||
import static io.quarkus.runtime.LaunchMode.TEST; | ||
import static io.quarkus.runtime.annotations.ConfigPhase.RUN_TIME; | ||
|
||
import java.util.Map; | ||
import java.util.OptionalInt; | ||
|
||
import io.quarkus.runtime.LaunchMode; | ||
import io.quarkus.runtime.annotations.ConfigDocIgnore; | ||
import io.quarkus.runtime.annotations.ConfigRoot; | ||
import io.smallrye.config.ConfigMapping; | ||
import io.smallrye.config.ConfigValue; | ||
import io.smallrye.config.WithDefault; | ||
import io.smallrye.config.WithName; | ||
import io.smallrye.config.WithParentName; | ||
|
||
@ConfigMapping(prefix = "quarkus.grpc") | ||
@ConfigRoot(phase = RUN_TIME) | ||
public interface GrpcConfig { | ||
@ConfigDocIgnore | ||
GrpcServer server(); | ||
|
||
@ConfigDocIgnore | ||
Map<String, GrpcClient> clients(); | ||
|
||
@ConfigDocIgnore | ||
@WithParentName | ||
Map<String, String> properties(); | ||
|
||
interface GrpcServer { | ||
@WithDefault("true") | ||
boolean useSeparateServer(); | ||
|
||
@ConfigDocIgnore | ||
@WithDefault("9000") | ||
int port(); | ||
|
||
@ConfigDocIgnore | ||
@WithDefault("9001") | ||
int testPort(); | ||
|
||
default int determinePort(LaunchMode mode) { | ||
return mode.equals(TEST) ? testPort() : port(); | ||
} | ||
|
||
@ConfigDocIgnore | ||
@WithDefault("0.0.0.0") | ||
String host(); | ||
|
||
@ConfigDocIgnore | ||
@WithDefault("true") | ||
boolean plainText(); | ||
|
||
@ConfigDocIgnore | ||
Map<String, String> ssl(); | ||
|
||
@ConfigDocIgnore | ||
@WithParentName | ||
Map<String, String> properties(); | ||
} | ||
|
||
interface GrpcClient { | ||
@ConfigDocIgnore | ||
@WithDefault("9000") | ||
int port(); | ||
|
||
/** | ||
* A duplicate mapping of {@link GrpcClient#port()}, to retrieve the full configuration key. We may need | ||
* the original property name to reassign the port, and because the client configuration contains a dynamic path | ||
* segment, we can avoid reconstructing the original property name from the | ||
* {@link GrpcConfig#clients()} <code>Map</code>. | ||
*/ | ||
@ConfigDocIgnore | ||
@WithName("port") | ||
ConfigValue portName(); | ||
|
||
@ConfigDocIgnore | ||
OptionalInt testPort(); | ||
|
||
/** | ||
* A duplicate mapping of {@link GrpcClient#testPort()}, to retrieve the full configuration key. We may need | ||
* the original property name to reassign the port, and because the client configuration contains a dynamic path | ||
* segment, we can avoid reconstructing the original property name from the | ||
* {@link GrpcConfig#clients()} <code>Map</code>. | ||
*/ | ||
@ConfigDocIgnore | ||
@WithName("test-port") | ||
ConfigValue testPortName(); | ||
|
||
default int determinePort(LaunchMode mode, int defaultPort) { | ||
return mode.equals(TEST) ? testPort().orElse(defaultPort) : port(); | ||
} | ||
|
||
@ConfigDocIgnore | ||
@WithDefault("localhost") | ||
String host(); | ||
|
||
@ConfigDocIgnore | ||
@WithParentName | ||
Map<String, String> properties(); | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/GrpcConfigBuilder.java
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 |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package io.quarkus.grpc.runtime.config; | ||
|
||
import static io.quarkus.runtime.LaunchMode.TEST; | ||
import static java.lang.Integer.MAX_VALUE; | ||
import static java.util.Collections.emptyList; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.eclipse.microprofile.config.spi.ConfigSource; | ||
|
||
import io.quarkus.grpc.runtime.GrpcTestPortUtils; | ||
import io.quarkus.grpc.runtime.config.GrpcConfig.GrpcClient; | ||
import io.quarkus.runtime.LaunchMode; | ||
import io.quarkus.runtime.configuration.ConfigBuilder; | ||
import io.quarkus.runtime.configuration.RandomPorts; | ||
import io.quarkus.vertx.http.runtime.HttpConfig; | ||
import io.smallrye.config.ConfigSourceContext; | ||
import io.smallrye.config.ConfigSourceFactory; | ||
import io.smallrye.config.SmallRyeConfig; | ||
import io.smallrye.config.SmallRyeConfigBuilder; | ||
import io.smallrye.config.common.MapBackedConfigSource; | ||
|
||
public class GrpcConfigBuilder implements ConfigBuilder { | ||
@Override | ||
public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) { | ||
return builder.withSources(new RandomPortConfigSourceFactory()); | ||
} | ||
|
||
private static class RandomPortConfigSourceFactory implements ConfigSourceFactory { | ||
@Override | ||
public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) { | ||
Map<String, String> randomPorts = new HashMap<>(); | ||
|
||
SmallRyeConfig config = new SmallRyeConfigBuilder() | ||
.withSources(new ConfigSourceContext.ConfigSourceContextConfigSource(context)) | ||
.withSources(context.getConfigSources()) | ||
.withMapping(HttpConfig.class) | ||
.withMapping(GrpcConfig.class) | ||
.build(); | ||
|
||
GrpcConfig grpcConfig = config.getConfigMapping(GrpcConfig.class); | ||
|
||
int port = grpcConfig.server().determinePort(LaunchMode.current()); | ||
if (port <= 0) { | ||
String randomPort = RandomPorts.get(grpcConfig.server().host(), port) + ""; | ||
randomPorts.put("quarkus.grpc.server.port", randomPort); | ||
if (LaunchMode.current().equals(TEST)) { | ||
randomPorts.put("quarkus.grpc.server.test-port", randomPort); | ||
} | ||
} | ||
|
||
HttpConfig httpConfig = config.getConfigMapping(HttpConfig.class); | ||
for (Map.Entry<String, GrpcClient> client : grpcConfig.clients().entrySet()) { | ||
int clientPort = client.getValue().determinePort(LaunchMode.current(), | ||
testPort(grpcConfig.server(), httpConfig)); | ||
if (clientPort <= 0) { | ||
String randomPort = RandomPorts.get(client.getValue().host(), clientPort) + ""; | ||
randomPorts.put(client.getValue().portName().getName(), randomPort); | ||
if (LaunchMode.current().equals(TEST)) { | ||
randomPorts.put(client.getValue().testPortName().getName(), randomPort); | ||
} | ||
} | ||
} | ||
|
||
return randomPorts.isEmpty() ? emptyList() | ||
: List.of(new MapBackedConfigSource("Quarkus GRPC Random Ports", randomPorts, MAX_VALUE - 1000) { | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Mostly a copy of {@link GrpcTestPortUtils#testPort(GrpcServerConfiguration)}, with some changes, because it | ||
* seems that the original implementation returns the same value in different if branches. | ||
* <p> | ||
* Still, we should validate if this is really what we want to do. | ||
*/ | ||
private static int testPort(GrpcConfig.GrpcServer server, HttpConfig httpConfig) { | ||
if (server.useSeparateServer()) { | ||
return server.testPort(); | ||
} | ||
|
||
if (!server.ssl().isEmpty() || !server.plainText()) { | ||
return httpConfig.determineSslPort(TEST); | ||
} | ||
|
||
return httpConfig.determinePort(TEST); | ||
} | ||
} |
Oops, something went wrong.