Skip to content

Commit

Permalink
Provide Service Registration feature in a standalone way
Browse files Browse the repository at this point in the history
  • Loading branch information
aureamunoz committed Jul 17, 2024
1 parent acd61c7 commit 73820d6
Show file tree
Hide file tree
Showing 62 changed files with 1,459 additions and 749 deletions.
66 changes: 66 additions & 0 deletions api/src/main/java/io/smallrye/stork/api/Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ public Service(String serviceName,
this.instanceSelectionLock = requiresStrictRecording ? new Semaphore(1) : null;
}

private Service(Builder builder) {
this.serviceName = builder.serviceName;
this.serviceSelectionType = builder.serviceSelectionType;
this.serviceDiscoveryType = builder.serviceDiscoveryType;
this.observations = builder.observations;
this.loadBalancer = builder.loadBalancer;
this.serviceDiscovery = builder.serviceDiscovery;
this.serviceRegistrar = builder.serviceRegistrar;
this.instanceSelectionLock = builder.requiresStrictRecording ? new Semaphore(1) : null;
}

/**
* Selects a service instance.
* <p>
Expand Down Expand Up @@ -212,4 +223,59 @@ public ObservationCollector getObservations() {
public String getServiceName() {
return serviceName;
}

public static class Builder {
private String serviceName;
private final ObservationCollector observations;
private ServiceDiscovery serviceDiscovery;
private String serviceSelectionType = "round-robin";
private String serviceDiscoveryType;
private LoadBalancer loadBalancer;
private ServiceRegistrar<?> serviceRegistrar;
private boolean requiresStrictRecording = false;

public Builder serviceName(String serviceName) {
this.serviceName = serviceName;
return this;
}

public Builder(ObservationCollector observations) {
this.observations = observations;
}

public Builder serviceDiscovery(ServiceDiscovery serviceDiscovery) {
this.serviceDiscovery = serviceDiscovery;
return this;
}

public Builder serviceSelectionType(String serviceSelectionType) {
this.serviceSelectionType = serviceSelectionType;
return this;
}

public Builder serviceDiscoveryType(String serviceDiscoveryType) {
this.serviceDiscoveryType = serviceDiscoveryType;
return this;
}

public Builder loadBalancer(LoadBalancer loadBalancer) {
this.loadBalancer = loadBalancer;
return this;
}

public Builder serviceRegistrar(ServiceRegistrar<?> serviceRegistrar) {
this.serviceRegistrar = serviceRegistrar;
return this;
}

public Builder requiresStrictRecording(boolean requiresStrictRecording) {
this.requiresStrictRecording = requiresStrictRecording;
return this;
}

public Service build() {
return new Service(this);
}
}

}
10 changes: 10 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@
<artifactId>stork-spring-boot-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>stork-service-registration-consul</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>stork-service-registration-static-list</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>stork-test-utils</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@
"io.smallrye.stork.api.config.LoadBalancerAttributes",
"io.smallrye.stork.api.config.ServiceDiscoveryType",
"io.smallrye.stork.api.config.ServiceDiscoveryAttribute",
"io.smallrye.stork.api.config.ServiceDiscoveryAttributes"
"io.smallrye.stork.api.config.ServiceDiscoveryAttributes",
"io.smallrye.stork.api.config.ServiceRegistrarType",
"io.smallrye.stork.api.config.ServiceRegistrarAttribute",
"io.smallrye.stork.api.config.ServiceRegistrarAttributes"
})
@SupportedSourceVersion(SourceVersion.RELEASE_11)
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@AutoService(Processor.class)
public class ConfigurationGenerator extends AbstractProcessor {

Expand Down
84 changes: 47 additions & 37 deletions core/src/main/java/io/smallrye/stork/Stork.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ public Stork(StorkInfrastructure storkInfrastructure) {
services.put(serviceConfig.serviceName(), service);
}
for (Service service : services.values()) {
service.getServiceDiscovery().initialize(this);
if (service.getServiceDiscovery() != null) {
service.getServiceDiscovery().initialize(this);
}
}
}

Expand Down Expand Up @@ -178,54 +180,64 @@ private Service createService(Map<String, LoadBalancerLoader> loadBalancerLoader
Map<String, ServiceDiscoveryLoader> serviceDiscoveryProviders,
Map<String, ServiceRegistrarLoader> serviceRegistrarLoaders, ServiceConfig serviceConfig) {
var serviceDiscoveryConfig = serviceConfig.serviceDiscovery();
if (serviceDiscoveryConfig == null) {
throw new IllegalArgumentException(
"No service discovery defined for service " + serviceConfig.serviceName());
}
String serviceDiscoveryType = serviceDiscoveryConfig.type();
if (serviceDiscoveryType == null) {
var serviceBuilder = new Service.Builder(infrastructure.getObservationCollector());
if (serviceDiscoveryConfig != null && serviceDiscoveryConfig.type() == null) {
throw new IllegalArgumentException(
"Service discovery type not defined for service " + serviceConfig.serviceName());
}

final var serviceDiscoveryProvider = serviceDiscoveryProviders.get(serviceDiscoveryType);
if (serviceDiscoveryProvider == null) {
throw new IllegalArgumentException("ServiceDiscoveryProvider not found for type " + serviceDiscoveryType);
if (serviceDiscoveryConfig != null) {
if (serviceDiscoveryProviders.get(serviceDiscoveryConfig.type()) == null) {
throw new IllegalArgumentException(
"ServiceDiscoveryProvider not found for type " + serviceDiscoveryConfig.type());
}
}

if (serviceConfig.secure()) {
// Backward compatibility
LOGGER.warn("The 'secure' attribute is deprecated, use the 'secure' service discovery attribute instead");
// We do not know if we can add to the parameters, such create a new SimpleConfigWithType
Map<String, String> newConfig = new HashMap<>(serviceDiscoveryConfig.parameters());
newConfig.put("secure", "true");
serviceDiscoveryConfig = new SimpleServiceConfig.SimpleServiceDiscoveryConfig(serviceDiscoveryType, newConfig);
if (serviceDiscoveryConfig != null) {
Map<String, String> newConfig = new HashMap<>(serviceDiscoveryConfig.parameters());
newConfig.put("secure", "true");
serviceDiscoveryConfig = new SimpleServiceConfig.SimpleServiceDiscoveryConfig(serviceDiscoveryConfig.type(),
newConfig);
}
}

final var serviceDiscovery = serviceDiscoveryProvider.createServiceDiscovery(serviceDiscoveryConfig,
serviceConfig.serviceName(), serviceConfig, infrastructure);

final var loadBalancerConfig = serviceConfig.loadBalancer();
final LoadBalancer loadBalancer;
String loadBalancerType;
if (loadBalancerConfig == null) {
// no load balancer, use round-robin
LOGGER.debug("No load balancer configured for type {}, using {}", serviceDiscoveryType,
RoundRobinLoadBalancerProvider.ROUND_ROBIN_TYPE);
loadBalancerType = RoundRobinLoadBalancerProvider.ROUND_ROBIN_TYPE;
loadBalancer = new RoundRobinLoadBalancer();
} else {
loadBalancerType = loadBalancerConfig.type();
final var loadBalancerProvider = loadBalancerLoaders.get(loadBalancerType);
if (loadBalancerProvider == null) {
throw new IllegalArgumentException("No LoadBalancerProvider for type " + loadBalancerType);
if (serviceDiscoveryConfig != null) {
final var serviceDiscoveryProvider = serviceDiscoveryProviders.get(serviceDiscoveryConfig.type());
final var serviceDiscovery = serviceDiscoveryProvider.createServiceDiscovery(serviceDiscoveryConfig,
serviceConfig.serviceName(), serviceConfig, infrastructure);

final var loadBalancerConfig = serviceConfig.loadBalancer();
final LoadBalancer loadBalancer;
String loadBalancerType;
if (loadBalancerConfig == null) {
// no load balancer, use round-robin
LOGGER.debug("No load balancer configured for type {}, using {}", serviceDiscoveryConfig.type(),
RoundRobinLoadBalancerProvider.ROUND_ROBIN_TYPE);
loadBalancerType = RoundRobinLoadBalancerProvider.ROUND_ROBIN_TYPE;
loadBalancer = new RoundRobinLoadBalancer();
} else {
loadBalancerType = loadBalancerConfig.type();
final var loadBalancerProvider = loadBalancerLoaders.get(loadBalancerType);
if (loadBalancerProvider == null) {
throw new IllegalArgumentException("No LoadBalancerProvider for type " + loadBalancerType);
}

loadBalancer = loadBalancerProvider.createLoadBalancer(loadBalancerConfig, serviceDiscovery);
}

loadBalancer = loadBalancerProvider.createLoadBalancer(loadBalancerConfig, serviceDiscovery);
serviceBuilder = serviceBuilder.serviceName(serviceConfig.serviceName())
.serviceDiscovery(serviceDiscovery)
.serviceSelectionType(loadBalancerType)
.serviceDiscoveryType(serviceDiscoveryConfig.type())
.loadBalancer(loadBalancer)
.requiresStrictRecording(loadBalancer.requiresStrictRecording());
}

final var serviceRegistrarConfig = serviceConfig.serviceRegistrar();
ServiceRegistrar<?> serviceRegistrar = null;
final ServiceRegistrar<?> serviceRegistrar;
if (serviceRegistrarConfig == null) {
LOGGER.debug("No service registrar configured for service {}", serviceConfig.serviceName());
} else {
Expand All @@ -237,12 +249,10 @@ private Service createService(Map<String, LoadBalancerLoader> loadBalancerLoader

serviceRegistrar = serviceRegistrarLoader.createServiceRegistrar(serviceRegistrarConfig,
serviceConfig.serviceName(), infrastructure);
serviceBuilder.serviceName(serviceConfig.serviceName()).serviceRegistrar(serviceRegistrar);
}

return new Service(serviceConfig.serviceName(),
loadBalancerType, serviceDiscoveryType, infrastructure.getObservationCollector(),
loadBalancer, serviceDiscovery, serviceRegistrar,
loadBalancer.requiresStrictRecording());
return serviceBuilder.build();
}

private <T extends ElementWithType> Map<String, T> loadFromServiceLoader(Class<T> loaderClass) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.smallrye.stork.servicediscovery.consul;
package io.smallrye.stork.impl;

import io.smallrye.stork.api.MetadataKey;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.smallrye.stork.servicediscovery.eureka;
package io.smallrye.stork.impl;

import io.smallrye.stork.api.MetadataKey;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.smallrye.stork.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class InMemoryAddressesBackend {

private static Map<String, List<String>> backend = new HashMap<>();

public static List<String> getAddresses(String serviceName) {
return backend.get(serviceName);
}

public static void add(String serviceName, String address) {
if (serviceName == null || serviceName.length() == 0) {
throw new IllegalArgumentException("No service name provided for address " + address);
}
if (backend.get(serviceName) != null) {
if (!backend.get(serviceName).contains(address)) {
backend.get(serviceName).add(address);
}
} else {
List<String> addresses = new ArrayList<>();
addresses.add(address);
backend.put(serviceName, addresses);
}
}

public static void clear(String serviceName) {
if (backend != null) {
backend.remove(serviceName);
}
}

public static void clearAll() {
backend.clear();
}
}
44 changes: 44 additions & 0 deletions core/src/test/java/io/smallrye/stork/StorkTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ public Map<String, String> parameters() {
}
};

private static final ConfigWithType SERVICE_REGISTRAR_CONFIG = new ConfigWithType() {

@Override
public String type() {
return "test";
}

@Override
public Map<String, String> parameters() {
return Collections.emptyMap();
}
};

@BeforeEach
public void init() {
TestEnv.SPI_ROOT.mkdirs();
Expand Down Expand Up @@ -277,6 +290,23 @@ public Map<String, String> parameters() {
Assertions.assertTrue(stork.getService("a").getLoadBalancer() instanceof RoundRobinLoadBalancer);
}

@Test
public void initWithOnlyServiceRegistrarConfiguration() {
TestEnv.configurations.add(new FakeServiceConfig("test", null, null, SERVICE_REGISTRAR_CONFIG));
TestEnv.install(ConfigProvider.class, RegistrarConfigProvider.class);
Assertions.assertDoesNotThrow(() -> Stork.initialize());
}

@Test
void initWithoutServiceDiscovery() {
TestEnv.configurations.add(new FakeServiceConfig("a", null, null, null));
TestEnv.install(ConfigProvider.class, TestEnv.AnchoredConfigProvider.class);
Assertions.assertDoesNotThrow(() -> Stork.initialize());
Stork stork = Stork.getInstance();
Assertions.assertTrue(stork.getServiceOptional("missing").isEmpty());
Assertions.assertThrows(NoSuchServiceDefinitionException.class, () -> stork.getService("missing"));
}

public static class ServiceAConfigProvider implements ConfigProvider {

@Override
Expand Down Expand Up @@ -305,6 +335,20 @@ public int priority() {
}
}

public static class RegistrarConfigProvider implements ConfigProvider {

@Override
public List<ServiceConfig> getConfigs() {
ServiceConfig service = new FakeServiceConfig("test", null, null, SERVICE_REGISTRAR_CONFIG);
return List.of(service);
}

@Override
public int priority() {
return 100;
}
}

private static class FakeSecureServiceConfig extends FakeServiceConfig {

private FakeSecureServiceConfig(String name, ConfigWithType sd, ConfigWithType lb, ConfigWithType sr) {
Expand Down
36 changes: 36 additions & 0 deletions core/src/test/java/io/smallrye/stork/TestMetadataKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.smallrye.stork;

import io.smallrye.stork.api.MetadataKey;

public enum TestMetadataKey implements MetadataKey {

/**
* The key for the consul service id.
*/
META_CONSUL_SERVICE_ID("consul-service-id"),
/**
* The key for the consul service node.
*/
META_CONSUL_SERVICE_NODE("consul-service-node"),
/**
* The key for the consul service node address.
*/
META_CONSUL_SERVICE_NODE_ADDRESS("consul-service-node-address");

private final String name;

/**
* Creates a new ConsulMetadataKey
*
* @param name the name
*/
TestMetadataKey(String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}

}
Loading

0 comments on commit 73820d6

Please sign in to comment.