Skip to content
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

feat: Adds support for getting instances and instance operators #16

Merged
merged 4 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading