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

feature and refactor: add lombok, introduce package structure and add interface to configure dependent resources #208

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
import io.javaoperatorsdk.operator.api.config.ConfigurationServiceOverrider;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.springboot.starter.OperatorConfigurationProperties;
import io.javaoperatorsdk.operator.springboot.starter.ReconcilerProperties;
import io.javaoperatorsdk.operator.springboot.starter.properties.OperatorConfigurationProperties;
import io.javaoperatorsdk.operator.springboot.starter.properties.ReconcilerProperties;
import io.javaoperatorsdk.operator.springboot.starter.sample.CustomService;
import io.javaoperatorsdk.operator.springboot.starter.sample.CustomServiceReconciler;
import io.javaoperatorsdk.operator.springboot.starter.sample.ServiceSpec;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.javaoperatorsdk.operator.springboot.starter;

@FunctionalInterface
public interface DependentResourceConfigurator {

String getName();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one note regarding to this, altevatively we could avoid inheritance, so the configuration bean would not inherit this interface, but we could put here also method getConfiguraiton that would result in the configuration bean.
But no strong opinion one way or another.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or as an atlernative we could have this just as an annotation on top of a conig bean: @DependentConfiguration(dependentName="dr name", reconcilerName="optional reconciler name")


}
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package io.javaoperatorsdk.operator.springboot.starter;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -28,14 +25,12 @@
import io.javaoperatorsdk.operator.ReconcilerUtils;
import io.javaoperatorsdk.operator.api.config.Cloner;
import io.javaoperatorsdk.operator.api.config.ConfigurationServiceOverrider;
import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider;
import io.javaoperatorsdk.operator.api.config.DefaultResourceClassResolver;
import io.javaoperatorsdk.operator.api.config.ResourceClassResolver;
import io.javaoperatorsdk.operator.api.monitoring.Metrics;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.processing.retry.GenericRetry;
import io.javaoperatorsdk.operator.springboot.starter.CRDApplier.CRDTransformer;
import io.javaoperatorsdk.operator.springboot.starter.CRDApplier.DefaultCRDApplier;
import io.javaoperatorsdk.operator.springboot.starter.crd.CRDApplier;
import io.javaoperatorsdk.operator.springboot.starter.properties.OperatorConfigurationProperties;

@Configuration
@EnableConfigurationProperties(OperatorConfigurationProperties.class)
Expand All @@ -44,25 +39,23 @@ public class OperatorAutoConfiguration {
private final static Logger log = LoggerFactory.getLogger(OperatorAutoConfiguration.class);

@Autowired
private OperatorConfigurationProperties configuration;
private OperatorConfigurationProperties configurationProperties;

@Bean
@ConditionalOnMissingBean
public KubernetesClient kubernetesClient(Optional<HttpClient.Factory> httpClientFactory,
Config config) {
return configuration.getClient().isOpenshift()
? httpClientFactory
.map(it -> new KubernetesClientBuilder().withHttpClientFactory(it).withConfig(config)
.build().adapt(OpenShiftClient.class))
// new DefaultOpenShiftClient(it.createHttpClient(config),
// new OpenShiftConfig(config)))
.orElseGet(() -> new KubernetesClientBuilder().withConfig(config)
.build().adapt(OpenShiftClient.class))
: httpClientFactory
.map(it -> new KubernetesClientBuilder().withHttpClientFactory(it).withConfig(config)
.build())
.orElseGet(() -> new KubernetesClientBuilder().withConfig(config)
.build());
public KubernetesClient kubernetesClient(
@Autowired(required = false) HttpClient.Factory httpClientFactory, Config config) {
KubernetesClientBuilder clientBuilder = new KubernetesClientBuilder().withConfig(config);

if (httpClientFactory != null) {
clientBuilder = clientBuilder.withHttpClientFactory(httpClientFactory);
}
KubernetesClient client = clientBuilder.build();

if (configurationProperties.getClient().isOpenshift()) {
return client.adapt(OpenShiftClient.class);
}
return client;
}

@Bean
Expand All @@ -71,107 +64,78 @@ public ResourceClassResolver resourceClassResolver() {
return new DefaultResourceClassResolver();
}

@Bean
@ConditionalOnProperty(value = "javaoperatorsdk.crd.apply-on-startup", havingValue = "true")
public CRDApplier crdApplier(KubernetesClient client, List<CRDTransformer> transformers) {
var crd = configuration.getCrd();
return new DefaultCRDApplier(client, transformers, crd.getPath(), crd.getSuffix());
}

@Bean
@ConditionalOnMissingBean(CRDApplier.class)
public CRDApplier disabledCrdApplier() {
if (log.isDebugEnabled()) {
log.debug("no CRDApplier loaded, using NOOP");
}
return CRDApplier.NOOP;
}

@Bean
public OperatorStarter operatorStarter(Operator operator, CRDApplier applier) {
return new OperatorStarter(operator, applier);
}

@Bean(destroyMethod = "stop")
@ConditionalOnMissingBean(Operator.class)
public Operator operator(
BiConsumer<Operator, Reconciler<?>> reconcilerRegisterer,
@Qualifier("compositeConfigurationServiceOverrider") Consumer<ConfigurationServiceOverrider> compositeConfigurationServiceOverrider,
KubernetesClient kubernetesClient,
List<Reconciler<?>> reconcilers) {

var operator = new Operator(compositeConfigurationServiceOverrider);
reconcilers.forEach(reconciler -> reconcilerRegisterer.accept(operator, reconciler));

return operator;
}

@Bean
public BiConsumer<Operator, Reconciler<?>> reconcilerRegisterer() {
return (operator, reconciler) -> {
var name = ReconcilerUtils.getNameFor(reconciler);
var props = configuration.getReconcilers().get(name);

operator.register(reconciler, overrider -> overrideFromProps(overrider, props));
};
}

@Bean
public Consumer<ConfigurationServiceOverrider> compositeConfigurationServiceOverrider(
List<Consumer<ConfigurationServiceOverrider>> configServiceOverriders) {
return configServiceOverriders.stream()
public Operator operator(List<Consumer<ConfigurationServiceOverrider>> configServiceOverriders,
List<Reconciler<?>> reconcilers,
List<DependentResourceConfigurator> dependentResourceConfigurators) {
var chainedOverriders = configServiceOverriders.stream()
.reduce(Consumer::andThen)
.orElseThrow(
() -> new IllegalStateException("Default Config Service Overrider Not Created"));

var dependentResourceConfiguratorsMap = dependentResourceConfigurators == null ? null
: dependentResourceConfigurators.stream()
.collect(Collectors.groupingBy(DependentResourceConfigurator::getName));

var operator = new Operator(chainedOverriders);
reconcilers.forEach(reconciler -> {
var name = ReconcilerUtils.getNameFor(reconciler);
var props = configurationProperties.getReconcilers().get(name);
operator.register(reconciler, o -> {
ReconcilerRegistrationUtil.overrideFromProps(o, props);
if (dependentResourceConfiguratorsMap != null) {
var drsForReconciler = ReconcilerRegistrationUtil.filterConfigurators(reconciler,
dependentResourceConfiguratorsMap);
drsForReconciler.forEach(dr -> o.replacingNamedDependentResourceConfig(dr.getName(), dr));
}
});
});
return operator;
}

@Bean
@Order(0)
public Consumer<ConfigurationServiceOverrider> defaultConfigServiceOverrider(
@Autowired(required = false) Cloner cloner,
Metrics metrics, KubernetesClient kubernetesClient) {
@Autowired(required = false) Cloner cloner, Metrics metrics,
KubernetesClient kubernetesClient) {
return overrider -> {
doIfPresent(cloner, overrider::withResourceCloner);
doIfPresent(configuration.getStopOnInformerErrorDuringStartup(),
ReconcilerRegistrationUtil.doIfPresent(cloner, overrider::withResourceCloner);
ReconcilerRegistrationUtil.doIfPresent(
configurationProperties.getStopOnInformerErrorDuringStartup(),
overrider::withStopOnInformerErrorDuringStartup);
doIfPresent(configuration.getConcurrentWorkflowExecutorThreads(),
ReconcilerRegistrationUtil.doIfPresent(
configurationProperties.getConcurrentWorkflowExecutorThreads(),
overrider::withConcurrentWorkflowExecutorThreads);
doIfPresent(configuration.getCloseClientOnStop(), overrider::withCloseClientOnStop);
doIfPresent(configuration.getCacheSyncTimeout(), overrider::withCacheSyncTimeout);
ReconcilerRegistrationUtil.doIfPresent(configurationProperties.getCloseClientOnStop(),
overrider::withCloseClientOnStop);
ReconcilerRegistrationUtil.doIfPresent(configurationProperties.getCacheSyncTimeout(),
overrider::withCacheSyncTimeout);

overrider
.withConcurrentReconciliationThreads(configuration.getConcurrentReconciliationThreads())
.withConcurrentReconciliationThreads(
configurationProperties.getConcurrentReconciliationThreads())
.withMetrics(metrics)
.checkingCRDAndValidateLocalModel(configuration.getCheckCrdAndValidateLocalModel())
.checkingCRDAndValidateLocalModel(
configurationProperties.isCheckCrdAndValidateLocalModel())
.withKubernetesClient(kubernetesClient);
};
}

private void overrideFromProps(ControllerConfigurationOverrider<?> overrider,
ReconcilerProperties props) {
if (props != null) {
doIfPresent(props.getFinalizerName(), overrider::withFinalizer);
doIfPresent(props.getName(), overrider::withName);
doIfPresent(props.getNamespaces(), overrider::settingNamespaces);
doIfPresent(props.getRetry(), r -> {
var retry = new GenericRetry();
doIfPresent(r.getInitialInterval(), retry::setInitialInterval);
doIfPresent(r.getMaxAttempts(), retry::setMaxAttempts);
doIfPresent(r.getMaxInterval(), retry::setMaxInterval);
doIfPresent(r.getIntervalMultiplier(), retry::setIntervalMultiplier);
overrider.withRetry(retry);
});
doIfPresent(props.isGenerationAware(), overrider::withGenerationAware);
doIfPresent(props.isClusterScoped(), clusterScoped -> {
if (clusterScoped) {
overrider.watchingAllNamespaces();
}
});
doIfPresent(props.getLabelSelector(), overrider::withLabelSelector);
doIfPresent(props.getReconciliationMaxInterval(), overrider::withReconciliationMaxInterval);
}
}

@Bean
@ConditionalOnMissingBean(name = "reconciliationExecutorService")
public ExecutorService reconciliationExecutorService() {
return Executors.newFixedThreadPool(configuration.getConcurrentReconciliationThreads());
return Executors
.newFixedThreadPool(configurationProperties.getConcurrentReconciliationThreads());
}

@Bean
Expand All @@ -183,7 +147,7 @@ public Metrics metrics() {
@Bean
public Config getClientConfiguration(
@Autowired(required = false) KubernetesConfigCustomizer configCustomizer) {
return configuration.getClient().getContext()
return configurationProperties.getClient().getContext()
.map(Config::autoConfigure)
.map(it -> {
if (configCustomizer != null) {
Expand All @@ -195,7 +159,7 @@ public Config getClientConfiguration(
}
})
.orElseGet(() -> {
final var clientCfg = configuration.getClient();
final var clientCfg = configurationProperties.getClient();
ConfigBuilder config = new ConfigBuilder();
config.withTrustCerts(clientCfg.isTrustSelfSignedCertificates());
clientCfg.getMasterUrl().ifPresent(config::withMasterUrl);
Expand All @@ -210,8 +174,4 @@ public Config getClientConfiguration(
});
}

private <T> void doIfPresent(T prop, Consumer<T> action) {
Optional.ofNullable(prop).ifPresent(action);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
@Component
public class OperatorHealthIndicator extends AbstractHealthIndicator {

private final Operator operator;

private final static Logger log = LoggerFactory.getLogger(OperatorHealthIndicator.class);
private final Operator operator;

public OperatorHealthIndicator(final Operator operator) {
super("OperatorSDK health check failed");
Expand Down
Loading