Skip to content

Commit

Permalink
feat: Adds support for getting instances and instance operators (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
hofmeister authored Feb 5, 2024
1 parent 6d5f2fb commit bb41b01
Show file tree
Hide file tree
Showing 15 changed files with 602 additions and 101 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/check-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Verify build

on: [push]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache local Maven repository
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Set up JDK 19
uses: actions/setup-java@v3
with:
java-version: '19'
distribution: 'adopt'

- name: Package
run: mvn package

- name: Test
run: mvn test
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<groupId>com.kapeta</groupId>
<artifactId>spring-boot</artifactId>
<packaging>jar</packaging>
<version>0.3.0</version>
<version>1.0.0</version>

<name>${project.groupId}:${project.artifactId}</name>
<description>Kapeta Spring Boot SDK</description>
Expand Down Expand Up @@ -226,6 +226,12 @@
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>

<dependency>
<groupId>com.kapeta</groupId>
<artifactId>schemas</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
Expand All @@ -18,7 +19,9 @@
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableAutoConfiguration(
exclude = RabbitAutoConfiguration.class
)
@ComponentScan(excludeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)
})
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/com/kapeta/spring/config/BeanHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2023 Kapeta Inc.
* SPDX-License-Identifier: MIT
*/
package com.kapeta.spring.config;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;

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

public class BeanHelper {
private final BeanDefinitionRegistry beanRegistry;
private final ConfigurableListableBeanFactory beanFactory;
private final Map<Class,List<Object>> instances = new HashMap<>();

public BeanHelper(ConfigurableListableBeanFactory beanFactory) {
this.beanRegistry = (BeanDefinitionRegistry) beanFactory;
this.beanFactory = beanFactory;
}

private boolean isBeanRegistered(Class clz, Object value) {
if (!instances.containsKey(clz)) {
instances.put(clz, new ArrayList<>());
}
if (instances.get(clz).contains(value)) {
return true;
}
instances.get(clz).add(value);
return false;
}

public <T,U extends T> void registerBean(String beanName, Class<T> clz, U value) {
if (isBeanRegistered(clz, value)) {
return;
}
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(clz);
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setInstanceSupplier(() -> value);
beanRegistry.registerBeanDefinition(beanName, beanDefinition);
}

public <T,U extends T> void registerBean( Class<T> clz, U value) {
if (isBeanRegistered(clz, value)) {
return;
}
beanFactory.registerResolvableDependency(clz, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ static String getSystemConfiguration(final Environment environment, String envir

private static String getSystemConfiguration(final Environment environment, String propertyKey, String environmentKey, String configKey, String defaultValue) {
String value = System.getProperty(propertyKey);
if (!StringUtils.isEmpty(value)) {
if (StringUtils.hasText(value)) {
return value;
}

value = System.getenv(environmentKey);

if (!StringUtils.isEmpty(value)) {
if (StringUtils.hasText(value)) {
return value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

package com.kapeta.spring.config.providers;

import com.kapeta.schemas.entity.BlockDefinition;
import com.kapeta.spring.config.providers.types.*;
import org.springframework.core.env.Environment;

import java.util.HashMap;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public interface KapetaConfigurationProvider {
Expand Down Expand Up @@ -63,104 +66,34 @@ default int getServerPort() {
/**
* Gets the configuration for the current instance
*/
Map<String,Object> getInstanceConfig() throws Exception;
Map<String, Object> getInstanceConfig() throws Exception;

/**
* Get unique source ID for this configuration source
*/
String getProviderId();

<BlockType> BlockInstanceDetails<BlockType> getInstanceForConsumer(String resourceName, Class<BlockType> clz) throws IOException;

class InstanceInfo {

private String pid;

private String health;

public InstanceInfo(String pid, String health) {
this.pid = pid;
this.health = health;
}

public String getPid() {
return pid;
}

public String getHealth() {
return health;
}
default BlockInstanceDetails<BlockDefinition> getInstanceForConsumer(String resourceName) throws IOException {
return getInstanceForConsumer(resourceName, BlockDefinition.class);
}

class ResourceInfo {

private String host;

private String port;

private String type;

private String protocol;

private String resource;

private Map<String, Object> options = new HashMap<>();

private Map<String, String> credentials = new HashMap<>();
<Options, Credentials> InstanceOperator<Options, Credentials> getInstanceOperator(String instanceId, Class<Options> optionsClass, Class<Credentials> credentialsClass) throws IOException;

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public String getPort() {
return port;
}

public void setPort(String port) {
this.port = port;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getProtocol() {
return protocol;
}

public void setProtocol(String protocol) {
this.protocol = protocol;
}

public Map<String, Object> getOptions() {
return options;
}
default <Options> InstanceOperator<Options, DefaultCredentials> getInstanceOperator(String instanceId, Class<Options> optionsClass) throws IOException {
return getInstanceOperator(instanceId, optionsClass, DefaultCredentials.class);
}

public void setOptions(Map<String, Object> options) {
this.options = options;
}
default InstanceOperator<DefaultOptions, DefaultCredentials> getInstanceOperator(String instanceId) throws IOException {
return getInstanceOperator(instanceId, DefaultOptions.class);
}

public Map<String, String> getCredentials() {
return credentials;
}
<BlockType> List<BlockInstanceDetails<BlockType>> getInstancesForProvider(String resourceName, Class<BlockType> clz) throws IOException;

public void setCredentials(Map<String, String> credentials) {
this.credentials = credentials;
}
default List<BlockInstanceDetails<BlockDefinition>> getInstancesForProvider(String resourceName) throws IOException {
return getInstancesForProvider(resourceName, BlockDefinition.class);
}

public String getResource() {
return resource;
}

public void setResource(String resource) {
this.resource = resource;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@

package com.kapeta.spring.config.providers;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kapeta.spring.config.providers.types.BlockInstanceDetails;
import com.kapeta.spring.config.providers.types.InstanceOperator;
import com.kapeta.spring.config.providers.types.ResourceInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.kapeta.spring.config.KapetaDefaultConfig.createDefaultObjectMapper;
Expand All @@ -39,13 +43,13 @@ public KubernetesConfigProvider(String systemId, Environment environment) {

@Override
public int getServerPort(String portType) {
if (StringUtils.isEmpty(portType)) {
if (!StringUtils.hasText(portType)) {
portType = DEFAULT_SERVER_PORT_TYPE;
}

String envVar = "KAPETA_PROVIDER_PORT_%s".formatted(toEnvName(portType));
String envVarValue = environment.getProperty(envVar);
if (!StringUtils.isEmpty(envVarValue)) {
if (StringUtils.hasText(envVarValue)) {
return Integer.parseInt(envVarValue);
}

Expand Down Expand Up @@ -94,8 +98,9 @@ public Map<String,Object> getInstanceConfig() {
var envVarName = "KAPETA_INSTANCE_CONFIG";
if (environment.containsProperty(envVarName)) {
var value = environment.getProperty(envVarName);
var typeRef = new TypeReference<Map<String,Object>>() {};
try {
return objectMapper.readValue(value, Map.class);
return objectMapper.readValue(value, typeRef);
} catch (IOException e) {
throw new IllegalStateException("Failed to parse configuration from env var: %s".formatted(envVarName), e);
}
Expand All @@ -112,8 +117,9 @@ public String getInstanceHost(String instanceId) {
var envVarName = "KAPETA_BLOCK_HOSTS";
if (environment.containsProperty(envVarName)) {
var value = environment.getProperty(envVarName);
var typeRef = new TypeReference<Map<String, String>>() {};
try {
this.instanceHosts = objectMapper.readValue(value, Map.class);
this.instanceHosts = objectMapper.readValue(value, typeRef);
} catch (IOException e) {
throw new IllegalStateException("Failed to parse instance hosts from env var: %s".formatted(envVarName), e);
}
Expand All @@ -134,13 +140,53 @@ public String getProviderId() {
return "kubernetes";
}

@Override
public <Options, Credentials> InstanceOperator<Options, Credentials> getInstanceOperator(String instanceId, Class<Options> optionsClass, Class<Credentials> credentialsClass) throws IOException {
var envVarName = "KAPETA_INSTANCE_OPERATOR_%s".formatted(toEnvName(instanceId));
var value = requireEnvVar(envVarName);
var typeRef = objectMapper.getTypeFactory()
.constructParametricType(InstanceOperator.class, optionsClass, credentialsClass);
try {
return objectMapper.readValue(value, typeRef);
} catch (IOException e) {
throw new IllegalStateException("Failed to parse resource info from env var: %s".formatted(envVarName), e);
}
}

@Override
public <BlockType> BlockInstanceDetails<BlockType> getInstanceForConsumer(String resourceName, Class<BlockType> clz) throws IOException {
var envVarName = "KAPETA_INSTANCE_FOR_CONSUMER_%s".formatted(toEnvName(resourceName));
var value = requireEnvVar(envVarName);
var typeRef = objectMapper.getTypeFactory()
.constructParametricType(BlockInstanceDetails.class, clz);
try {
return objectMapper.readValue(value, typeRef);
} catch (IOException e) {
throw new IllegalStateException("Failed to parse resource info from env var: %s".formatted(envVarName), e);
}
}


@Override
public <BlockType> List<BlockInstanceDetails<BlockType>> getInstancesForProvider(String resourceName, Class<BlockType> clz) throws IOException {
var envVarName = "KAPETA_INSTANCES_FOR_PROVIDER_%s".formatted(toEnvName(resourceName));
var value = requireEnvVar(envVarName);
var typeRef = objectMapper.getTypeFactory()
.constructParametricType(BlockInstanceDetails.class, clz);
try {
return objectMapper.readValue(value, typeRef);
} catch (IOException e) {
throw new IllegalStateException("Failed to parse resource info from env var: %s".formatted(envVarName), e);
}
}

private String toEnvName(String name) {
return name.toUpperCase().trim().replaceAll("[.,-]", "_");
}

private String requireEnvVar(String envVarName) {
var envVarValue = environment.getProperty(envVarName);
if (!StringUtils.isEmpty(envVarValue)) {
if (StringUtils.hasText(envVarValue)) {
return envVarValue;
}

Expand Down
Loading

0 comments on commit bb41b01

Please sign in to comment.