Skip to content

Commit 564302f

Browse files
authored
Merge pull request #358 from java-operator-sdk/fix-354
feat: make it possible to deactivate CR validation
2 parents 2fb386f + 322a1df commit 564302f

File tree

13 files changed

+106
-21
lines changed

13 files changed

+106
-21
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,18 @@ public class WebServerSpec {
165165
}
166166
```
167167

168+
#### Deactivating CustomResource implementations validation
169+
170+
The operator will, by default, query the deployed CRDs to check that the `CustomResource`
171+
implementations match what is known to the cluster. This requires an additional query to the cluster
172+
and, sometimes, elevated privileges for the operator to be able to read the CRDs from the cluster.
173+
This validation is mostly meant to help users new to operator development get started and avoid
174+
common mistakes. Advanced users or production deployments might want to skip this step. This is done
175+
by setting
176+
the `JAVA_OPERATOR_SDK_VALIDATE_CR` environment variable to `false`. Quarkus users can also add
177+
`quarkus.operator-sdk.validate-custom-resources=false` to their `application.properties` for the
178+
same purpose.
179+
168180
#### Automatic generation of CRDs
169181

170182
To automatically generate CRD manifests from your annotated Custom Resource classes, you only need

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java

+19-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.javaoperatorsdk.operator;
22

3+
import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
34
import io.fabric8.kubernetes.client.CustomResource;
45
import io.fabric8.kubernetes.client.KubernetesClient;
56
import io.fabric8.kubernetes.client.Version;
@@ -96,23 +97,26 @@ public <R extends CustomResource> void register(
9697
Class<R> resClass = configuration.getCustomResourceClass();
9798
String finalizer = configuration.getFinalizer();
9899

99-
// check that the custom resource is known by the cluster
100-
final var crdName = configuration.getCRDName();
101-
final var crd =
102-
k8sClient.apiextensions().v1().customResourceDefinitions().withName(crdName).get();
103-
final var controllerName = configuration.getName();
104-
if (crd == null) {
105-
throw new OperatorException(
106-
"'"
107-
+ crdName
108-
+ "' CRD was not found on the cluster, controller "
109-
+ controllerName
110-
+ " cannot be registered");
100+
final String controllerName = configuration.getName();
101+
102+
// check that the custom resource is known by the cluster if configured that way
103+
final CustomResourceDefinition crd;
104+
if (configurationService.validateCustomResources()) {
105+
final var crdName = configuration.getCRDName();
106+
crd = k8sClient.apiextensions().v1().customResourceDefinitions().withName(crdName).get();
107+
if (crd == null) {
108+
throw new OperatorException(
109+
"'"
110+
+ crdName
111+
+ "' CRD was not found on the cluster, controller "
112+
+ controllerName
113+
+ " cannot be registered");
114+
}
115+
116+
// Apply validations that are not handled by fabric8
117+
CustomResourceUtils.assertCustomResource(resClass, crd);
111118
}
112119

113-
// Apply validations that are not handled by fabric8
114-
CustomResourceUtils.assertCustomResource(resClass, crd);
115-
116120
final var client = k8sClient.customResources(resClass);
117121
EventDispatcher dispatcher =
118122
new EventDispatcher(

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java

+13
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,17 @@ default Config getClientConfiguration() {
4242
* @return the version information
4343
*/
4444
Version getVersion();
45+
46+
/**
47+
* Whether the operator should query the CRD to make sure it's deployed and validate {@link
48+
* CustomResource} implementations before attempting to register the associated controllers.
49+
*
50+
* <p>Note that this might require elevating the privileges associated with the operator to gain
51+
* read access on the CRD resources.
52+
*
53+
* @return {@code true} if CRDs should be checked (default), {@code false} otherwise
54+
*/
55+
default boolean validateCustomResources() {
56+
return true;
57+
}
4558
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
public class Utils {
1313

1414
private static final Logger log = LoggerFactory.getLogger(Utils.class);
15+
public static final String VALIDATE_CR_ENV_KEY = "JAVA_OPERATOR_SDK_VALIDATE_CR";
1516

1617
/**
1718
* Attempts to load version information from a properties file produced at build time, currently
@@ -48,4 +49,13 @@ public static Version loadFromProperties() {
4849
properties.getProperty("git.commit.id.abbrev", "unknown"),
4950
builtTime);
5051
}
52+
53+
public static boolean isValidateCustomResourcesEnvVarSet() {
54+
return System.getProperty(VALIDATE_CR_ENV_KEY) != null;
55+
}
56+
57+
public static boolean shouldValidateCustomResources() {
58+
final var value = System.getProperty(VALIDATE_CR_ENV_KEY);
59+
return value == null || Boolean.getBoolean(value);
60+
}
5161
}

operator-framework-quarkus-extension/deployment/src/main/java/io/javaoperatorsdk/quarkus/extension/deployment/QuarkusExtensionProcessor.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,16 @@ void createConfigurationServiceAndOperator(
8181
.collect(Collectors.toList());
8282

8383
final var version = Utils.loadFromProperties();
84+
final var validateCustomResources =
85+
Utils.isValidateCustomResourcesEnvVarSet()
86+
? Utils.shouldValidateCustomResources()
87+
: externalConfiguration.validateCustomResources.orElse(true);
8488

8589
final var supplier =
8690
recorder.configurationServiceSupplier(
8791
new Version(version.getSdkVersion(), version.getCommit(), version.getBuiltTime()),
88-
controllerConfigs);
92+
controllerConfigs,
93+
validateCustomResources);
8994
syntheticBeanBuildItemBuildProducer.produce(
9095
SyntheticBeanBuildItem.configure(QuarkusConfigurationService.class)
9196
.scope(Singleton.class)

operator-framework-quarkus-extension/runtime/src/main/java/io/javaoperatorsdk/quarkus/extension/ConfigurationServiceRecorder.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@
1313
public class ConfigurationServiceRecorder {
1414

1515
public Supplier<ConfigurationService> configurationServiceSupplier(
16-
Version version, List<ControllerConfiguration> controllerConfigs) {
16+
Version version,
17+
List<ControllerConfiguration> controllerConfigs,
18+
boolean validateCustomResources) {
1719
return () ->
1820
new QuarkusConfigurationService(
19-
version, controllerConfigs, Arc.container().instance(KubernetesClient.class).get());
21+
version,
22+
controllerConfigs,
23+
Arc.container().instance(KubernetesClient.class).get(),
24+
validateCustomResources);
2025
}
2126
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
package io.javaoperatorsdk.quarkus.extension;
22

3+
import io.fabric8.kubernetes.client.CustomResource;
34
import io.quarkus.runtime.annotations.ConfigItem;
45
import io.quarkus.runtime.annotations.ConfigPhase;
56
import io.quarkus.runtime.annotations.ConfigRoot;
67
import java.util.Map;
8+
import java.util.Optional;
79

810
@ConfigRoot(name = "operator-sdk", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
911
public class ExternalConfiguration {
1012

1113
/** Maps a controller name to its configuration. */
1214
@ConfigItem public Map<String, ExternalControllerConfiguration> controllers;
15+
16+
/**
17+
* Whether the operator should validate the {@link CustomResource} implementation before
18+
* registering the associated controller.
19+
*/
20+
@ConfigItem(defaultValue = "true")
21+
public Optional<Boolean> validateCustomResources;
1322
}

operator-framework-quarkus-extension/runtime/src/main/java/io/javaoperatorsdk/quarkus/extension/ExternalControllerConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ public class ExternalControllerConfiguration {
2828
public Optional<Boolean> generationAware;
2929

3030
/** The optional controller retry configuration */
31-
public Optional<ExternalRetryConfiguration> retry;
31+
@ConfigItem public Optional<ExternalRetryConfiguration> retry;
3232
}

operator-framework-quarkus-extension/runtime/src/main/java/io/javaoperatorsdk/quarkus/extension/QuarkusConfigurationService.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,19 @@
1313
public class QuarkusConfigurationService extends AbstractConfigurationService {
1414
private static final ClientProxyUnwrapper unwrapper = new ClientProxyUnwrapper();
1515
private final KubernetesClient client;
16+
private final boolean validateCustomResources;
1617

1718
public QuarkusConfigurationService(
18-
Version version, List<ControllerConfiguration> configurations, KubernetesClient client) {
19+
Version version,
20+
List<ControllerConfiguration> configurations,
21+
KubernetesClient client,
22+
boolean validateCustomResources) {
1923
super(version);
2024
this.client = client;
2125
if (configurations != null && !configurations.isEmpty()) {
2226
configurations.forEach(this::register);
2327
}
28+
this.validateCustomResources = validateCustomResources;
2429
}
2530

2631
@Override
@@ -35,6 +40,11 @@ public <R extends CustomResource> ControllerConfiguration<R> getConfigurationFor
3540
return super.getConfigurationFor(unwrapped);
3641
}
3742

43+
@Override
44+
public boolean validateCustomResources() {
45+
return validateCustomResources;
46+
}
47+
3848
private static <R extends CustomResource> ResourceController<R> unwrap(
3949
ResourceController<R> controller) {
4050
return (ResourceController<R>) unwrapper.apply(controller);

operator-framework-quarkus-extension/tests/src/main/java/io/javaoperatorsdk/quarkus/it/TestOperatorApp.java

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ public class TestOperatorApp {
1818
@Inject Instance<ResourceController<? extends CustomResource>> controllers;
1919
@Inject ConfigurationService configurationService;
2020

21+
@GET
22+
@Path("validateCR")
23+
public boolean validateCR() {
24+
return configurationService.validateCustomResources();
25+
}
26+
2127
@GET
2228
@Path("{name}")
2329
public boolean getController(@PathParam("name") String name) {
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
quarkus.operator-sdk.controllers.annotation.finalizer=from-property/finalizer
2-
quarkus.operator-sdk.controllers.annotation.namespaces=bar
2+
quarkus.operator-sdk.controllers.annotation.namespaces=bar
3+
quarkus.operator-sdk.validate-custom-resources=false

operator-framework-quarkus-extension/tests/src/test/java/io/javaoperatorsdk/quarkus/it/QuarkusExtensionProcessorTest.java

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
@QuarkusTestResource(KubernetesMockServerTestResource.class)
1919
public class QuarkusExtensionProcessorTest {
2020

21+
@Test
22+
void shouldNotValidateCRs() {
23+
given().when().get("/operator/validateCR").then().statusCode(200).body(is("false"));
24+
}
25+
2126
@Test
2227
void controllerShouldExist() {
2328
// first check that we're not always returning true for any controller name :)

operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationService.java

+5
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ public <R extends CustomResource> ControllerConfiguration<R> getConfigurationFor
3636
}
3737
return config;
3838
}
39+
40+
@Override
41+
public boolean validateCustomResources() {
42+
return Utils.shouldValidateCustomResources();
43+
}
3944
}

0 commit comments

Comments
 (0)