Skip to content

Commit a4582a3

Browse files
authored
feat: dependent resource context + my sql e2e test improvements (#979)
1 parent 019771f commit a4582a3

File tree

12 files changed

+118
-47
lines changed

12 files changed

+118
-47
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Optional;
44

55
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
6+
import io.javaoperatorsdk.operator.api.reconciler.dependent.ManagedDependentResourceContext;
67

78
public interface Context extends AttributeHolder {
89

@@ -22,4 +23,6 @@ default <T> T getMandatory(Object key, Class<T> expectedType) {
2223
}
2324

2425
ConfigurationService getConfigurationService();
26+
27+
ManagedDependentResourceContext managedDependentResourceContext();
2528
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import io.fabric8.kubernetes.api.model.HasMetadata;
66
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
7+
import io.javaoperatorsdk.operator.api.reconciler.dependent.ManagedDependentResourceContext;
78
import io.javaoperatorsdk.operator.processing.Controller;
89

910
public class DefaultContext<P extends HasMetadata> extends MapAttributeHolder implements Context {
@@ -12,12 +13,15 @@ public class DefaultContext<P extends HasMetadata> extends MapAttributeHolder im
1213
private final Controller<P> controller;
1314
private final P primaryResource;
1415
private final ConfigurationService configurationService;
16+
private ManagedDependentResourceContext managedDependentResourceContext;
1517

1618
public DefaultContext(RetryInfo retryInfo, Controller<P> controller, P primaryResource) {
1719
this.retryInfo = retryInfo;
1820
this.controller = controller;
1921
this.primaryResource = primaryResource;
2022
this.configurationService = controller.getConfiguration().getConfigurationService();
23+
this.managedDependentResourceContext = new ManagedDependentResourceContext(
24+
controller.getDependents());
2125
}
2226

2327
@Override
@@ -36,4 +40,8 @@ public <T> Optional<T> getSecondaryResource(Class<T> expectedType, String eventS
3640
public ConfigurationService getConfigurationService() {
3741
return configurationService;
3842
}
43+
44+
public ManagedDependentResourceContext managedDependentResourceContext() {
45+
return managedDependentResourceContext;
46+
}
3947
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.javaoperatorsdk.operator.api.reconciler.dependent;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.stream.Collectors;
6+
7+
import io.javaoperatorsdk.operator.OperatorException;
8+
9+
public class ManagedDependentResourceContext {
10+
11+
private List<DependentResource> dependentResources;
12+
13+
public ManagedDependentResourceContext(List<DependentResource> dependentResources) {
14+
this.dependentResources = dependentResources;
15+
}
16+
17+
public List<DependentResource> getDependentResources() {
18+
return Collections.unmodifiableList(dependentResources);
19+
}
20+
21+
public <T extends DependentResource> T getDependentResource(Class<T> resourceClass) {
22+
var resourceList =
23+
dependentResources.stream()
24+
.filter(dr -> dr.getClass().equals(resourceClass))
25+
.collect(Collectors.toList());
26+
if (resourceList.isEmpty()) {
27+
throw new OperatorException(
28+
"No dependent resource found for class: " + resourceClass.getName());
29+
}
30+
if (resourceList.size() > 1) {
31+
throw new OperatorException(
32+
"More than one dependent resource found for class: " + resourceClass.getName());
33+
}
34+
return (T) resourceList.get(0);
35+
}
36+
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
3030
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
3131
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
32+
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
3233
import io.javaoperatorsdk.operator.processing.dependent.DependentResourceManager;
3334
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
3435
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
@@ -281,4 +282,8 @@ public void stop() {
281282
eventSourceManager.stop();
282283
}
283284
}
285+
286+
public List<DependentResource> getDependents() {
287+
return dependents.getDependents();
288+
}
284289
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceManager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,8 @@ private DependentResource createAndConfigureFrom(DependentResourceSpec dependent
105105
throw new IllegalStateException(e);
106106
}
107107
}
108+
109+
public List<DependentResource> getDependents() {
110+
return dependents;
111+
}
108112
}

sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import io.javaoperatorsdk.operator.api.config.ConfigurationServiceOverrider;
1818
import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService;
1919
import io.javaoperatorsdk.operator.monitoring.micrometer.MicrometerMetrics;
20+
import io.javaoperatorsdk.operator.sample.dependent.ResourcePollerConfig;
21+
import io.javaoperatorsdk.operator.sample.dependent.SchemaDependentResource;
2022
import io.micrometer.core.instrument.logging.LoggingMeterRegistry;
2123

2224
public class MySQLSchemaOperator {

sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22

33
import java.util.Optional;
44

5-
import org.apache.commons.lang3.RandomStringUtils;
65
import org.slf4j.Logger;
76
import org.slf4j.LoggerFactory;
87

8+
import io.fabric8.kubernetes.api.model.Secret;
99
import io.javaoperatorsdk.operator.api.reconciler.Context;
10-
import io.javaoperatorsdk.operator.api.reconciler.ContextInitializer;
1110
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
1211
import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler;
1312
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
1413
import io.javaoperatorsdk.operator.api.reconciler.RetryInfo;
1514
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
1615
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
17-
import io.javaoperatorsdk.operator.sample.schema.Schema;
16+
import io.javaoperatorsdk.operator.sample.dependent.SchemaDependentResource;
17+
import io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource;
1818

1919
import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_FINALIZER;
20+
import static io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource.MYSQL_SECRET_USERNAME;
2021
import static java.lang.String.format;
2122

2223
// todo handle this, should work with finalizer
@@ -26,42 +27,23 @@
2627
@Dependent(type = SchemaDependentResource.class)
2728
})
2829
public class MySQLSchemaReconciler
29-
implements Reconciler<MySQLSchema>, ErrorStatusHandler<MySQLSchema>,
30-
ContextInitializer<MySQLSchema> {
30+
implements Reconciler<MySQLSchema>, ErrorStatusHandler<MySQLSchema> {
3131

32-
static final String SECRET_FORMAT = "%s-secret";
33-
static final String USERNAME_FORMAT = "%s-user";
34-
35-
static final String MYSQL_SECRET_NAME = "mysql.secret.name";
36-
static final String MYSQL_SECRET_USERNAME = "mysql.secret.user.name";
37-
static final String MYSQL_SECRET_PASSWORD = "mysql.secret.user.password";
38-
static final String BUILT_SCHEMA = "built schema";
3932
static final Logger log = LoggerFactory.getLogger(MySQLSchemaReconciler.class);
4033

4134
public MySQLSchemaReconciler() {}
4235

43-
@Override
44-
public void initContext(MySQLSchema primary, Context context) {
45-
final var name = primary.getMetadata().getName();
46-
final var password = RandomStringUtils
47-
.randomAlphanumeric(16); // NOSONAR: we don't need cryptographically-strong randomness here
48-
49-
final var secretName = String.format(SECRET_FORMAT, name);
50-
final var userName = String.format(USERNAME_FORMAT, name);
51-
52-
// put information in context for other dependents and reconciler to use
53-
context.put(MYSQL_SECRET_PASSWORD, password);
54-
context.put(MYSQL_SECRET_NAME, secretName);
55-
context.put(MYSQL_SECRET_USERNAME, userName);
56-
}
5736

5837
@Override
5938
public UpdateControl<MySQLSchema> reconcile(MySQLSchema schema, Context context) {
6039
// we only need to update the status if we just built the schema, i.e. when it's present in the
6140
// context
62-
return context.get(BUILT_SCHEMA, Schema.class).map(s -> {
63-
updateStatusPojo(schema, context.getMandatory(MYSQL_SECRET_NAME, String.class),
64-
context.getMandatory(MYSQL_SECRET_USERNAME, String.class));
41+
Secret secret = context.getSecondaryResource(Secret.class).orElseThrow();
42+
SchemaDependentResource schemaDependentResource = context.managedDependentResourceContext()
43+
.getDependentResource(SchemaDependentResource.class);
44+
return schemaDependentResource.getResource(schema).map(s -> {
45+
updateStatusPojo(schema, secret.getMetadata().getName(),
46+
secret.getData().get(MYSQL_SECRET_USERNAME));
6547
log.info("Schema {} created - updating CR status", schema.getMetadata().getName());
6648
return UpdateControl.updateStatus(schema);
6749
}).orElse(UpdateControl.noUpdate());

sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/ResourcePollerConfig.java renamed to sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/ResourcePollerConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
package io.javaoperatorsdk.operator.sample;
1+
package io.javaoperatorsdk.operator.sample.dependent;
2+
3+
import io.javaoperatorsdk.operator.sample.MySQLDbConfig;
24

35
public class ResourcePollerConfig {
46

sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaDependentResource.java renamed to sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
package io.javaoperatorsdk.operator.sample;
1+
package io.javaoperatorsdk.operator.sample.dependent;
22

33
import java.sql.Connection;
44
import java.sql.DriverManager;
55
import java.sql.SQLException;
6+
import java.util.Base64;
67
import java.util.Optional;
78

9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
import io.fabric8.kubernetes.api.model.Secret;
813
import io.javaoperatorsdk.operator.api.reconciler.Context;
914
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
1015
import io.javaoperatorsdk.operator.api.reconciler.dependent.AbstractDependentResource;
@@ -14,9 +19,12 @@
1419
import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider;
1520
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
1621
import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource;
22+
import io.javaoperatorsdk.operator.sample.*;
1723
import io.javaoperatorsdk.operator.sample.schema.Schema;
1824
import io.javaoperatorsdk.operator.sample.schema.SchemaService;
1925

26+
import static io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource.MYSQL_SECRET_PASSWORD;
27+
import static io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource.MYSQL_SECRET_USERNAME;
2028
import static java.lang.String.format;
2129

2230
public class SchemaDependentResource
@@ -26,6 +34,8 @@ public class SchemaDependentResource
2634
Creator<Schema, MySQLSchema>,
2735
Deleter<MySQLSchema> {
2836

37+
private static final Logger log = LoggerFactory.getLogger(SchemaDependentResource.class);
38+
2939
private MySQLDbConfig dbConfig;
3040
private int pollPeriod = 500;
3141

@@ -53,25 +63,22 @@ public Schema desired(MySQLSchema primary, Context context) {
5363
@Override
5464
public void create(Schema target, MySQLSchema mySQLSchema, Context context) {
5565
try (Connection connection = getConnection()) {
66+
Secret secret = context.getSecondaryResource(Secret.class).orElseThrow();
67+
var username = decode(secret.getData().get(MYSQL_SECRET_USERNAME));
68+
var password = decode(secret.getData().get(MYSQL_SECRET_PASSWORD));
5669
final var schema = SchemaService.createSchemaAndRelatedUser(
5770
connection,
5871
target.getName(),
59-
target.getCharacterSet(),
60-
context.getMandatory(MySQLSchemaReconciler.MYSQL_SECRET_USERNAME, String.class),
61-
context.getMandatory(MySQLSchemaReconciler.MYSQL_SECRET_PASSWORD, String.class));
62-
63-
// put the newly built schema in the context to let the reconciler know we just built it
64-
context.put(MySQLSchemaReconciler.BUILT_SCHEMA, schema);
72+
target.getCharacterSet(), username, password);
6573
} catch (SQLException e) {
66-
MySQLSchemaReconciler.log.error("Error while creating Schema", e);
74+
log.error("Error while creating Schema", e);
6775
throw new IllegalStateException(e);
6876
}
6977
}
7078

7179
private Connection getConnection() throws SQLException {
7280
String connectURL = format("jdbc:mysql://%1$s:%2$s", dbConfig.getHost(), dbConfig.getPort());
73-
74-
MySQLSchemaReconciler.log.debug("Connecting to '{}' with user '{}'", connectURL,
81+
log.debug("Connecting to '{}' with user '{}'", connectURL,
7582
dbConfig.getUser());
7683
return DriverManager.getConnection(connectURL, dbConfig.getUser(), dbConfig.getPassword());
7784
}
@@ -99,4 +106,8 @@ public Optional<Schema> getResource(MySQLSchema primaryResource) {
99106
}
100107
}
101108

109+
private static String decode(String value) {
110+
return new String(Base64.getDecoder().decode(value.getBytes()));
111+
}
112+
102113
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
package io.javaoperatorsdk.operator.sample;
1+
package io.javaoperatorsdk.operator.sample.dependent;
22

33
import java.util.Optional;
44

55
import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource;
6+
import io.javaoperatorsdk.operator.sample.MySQLDbConfig;
7+
import io.javaoperatorsdk.operator.sample.MySQLSchema;
68
import io.javaoperatorsdk.operator.sample.schema.Schema;
79
import io.javaoperatorsdk.operator.sample.schema.SchemaService;
810

sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SecretDependentResource.java renamed to sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SecretDependentResource.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,49 @@
1-
package io.javaoperatorsdk.operator.sample;
1+
package io.javaoperatorsdk.operator.sample.dependent;
22

33
import java.util.Base64;
44

5+
import org.apache.commons.lang3.RandomStringUtils;
6+
57
import io.fabric8.kubernetes.api.model.Secret;
68
import io.fabric8.kubernetes.api.model.SecretBuilder;
79
import io.javaoperatorsdk.operator.api.reconciler.Context;
8-
import io.javaoperatorsdk.operator.api.reconciler.dependent.Updater;
10+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Creator;
911
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
1012
import io.javaoperatorsdk.operator.processing.event.ResourceID;
1113
import io.javaoperatorsdk.operator.processing.event.source.AssociatedSecondaryResourceIdentifier;
14+
import io.javaoperatorsdk.operator.sample.MySQLSchema;
1215

1316
import static io.javaoperatorsdk.operator.sample.MySQLSchemaReconciler.*;
1417

1518
public class SecretDependentResource extends KubernetesDependentResource<Secret, MySQLSchema>
16-
implements AssociatedSecondaryResourceIdentifier<MySQLSchema>, Updater<Secret, MySQLSchema> {
19+
implements AssociatedSecondaryResourceIdentifier<MySQLSchema>, Creator<Secret, MySQLSchema> {
20+
21+
public static final String SECRET_FORMAT = "%s-secret";
22+
public static final String USERNAME_FORMAT = "%s-user";
23+
public static final String MYSQL_SECRET_USERNAME = "mysql.secret.user.name";
24+
public static final String MYSQL_SECRET_PASSWORD = "mysql.secret.user.password";
1725

1826
private static String encode(String value) {
1927
return Base64.getEncoder().encodeToString(value.getBytes());
2028
}
2129

2230
@Override
2331
protected Secret desired(MySQLSchema schema, Context context) {
32+
final var password = RandomStringUtils
33+
.randomAlphanumeric(16); // NOSONAR: we don't need cryptographically-strong randomness here
34+
final var name = schema.getMetadata().getName();
35+
final var secretName = String.format(SECRET_FORMAT, name);
36+
final var userName = String.format(USERNAME_FORMAT, name);
37+
2438
return new SecretBuilder()
2539
.withNewMetadata()
26-
.withName(context.getMandatory(MYSQL_SECRET_NAME, String.class))
40+
.withName(secretName)
2741
.withNamespace(schema.getMetadata().getNamespace())
2842
.endMetadata()
2943
.addToData(
30-
"MYSQL_USERNAME", encode(context.getMandatory(MYSQL_SECRET_USERNAME, String.class)))
44+
MYSQL_SECRET_USERNAME, encode(userName))
3145
.addToData(
32-
"MYSQL_PASSWORD", encode(context.getMandatory(MYSQL_SECRET_PASSWORD, String.class)))
46+
MYSQL_SECRET_PASSWORD, encode(password))
3347
.build();
3448
}
3549

sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import io.javaoperatorsdk.operator.junit.AbstractOperatorExtension;
2222
import io.javaoperatorsdk.operator.junit.E2EOperatorExtension;
2323
import io.javaoperatorsdk.operator.junit.OperatorExtension;
24+
import io.javaoperatorsdk.operator.sample.dependent.ResourcePollerConfig;
25+
import io.javaoperatorsdk.operator.sample.dependent.SchemaDependentResource;
2426

2527
import static java.util.concurrent.TimeUnit.MINUTES;
2628
import static org.awaitility.Awaitility.await;

0 commit comments

Comments
 (0)