Skip to content

Commit 7f31340

Browse files
authored
Allow configure replicas (#5880)
* Add replicas tests * Add update clause to smoke test * Use uptional for default values
1 parent f6ab144 commit 7f31340

File tree

6 files changed

+150
-61
lines changed

6 files changed

+150
-61
lines changed

operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java

+18-11
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
import io.fabric8.kubernetes.api.model.apps.Deployment;
1313
import io.fabric8.kubernetes.api.model.apps.DeploymentSpec;
1414
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
15-
import org.slf4j.Logger;
16-
import org.slf4j.LoggerFactory;
1715

1816
import java.nio.charset.Charset;
1917
import java.util.ArrayList;
2018
import java.util.List;
2119
import java.util.Map;
20+
import java.util.Optional;
2221

22+
import static io.apicurio.registry.operator.Constants.DEFAULT_REPLICAS;
2323
import static io.apicurio.registry.operator.api.v1.ContainerNames.*;
2424
import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainerFromPodTemplateSpec;
2525
import static io.apicurio.registry.operator.utils.Mapper.YAML_MAPPER;
@@ -28,8 +28,6 @@
2828

2929
public class ResourceFactory {
3030

31-
private static final Logger log = LoggerFactory.getLogger(ResourceFactory.class);
32-
3331
public static final ResourceFactory INSTANCE = new ResourceFactory();
3432

3533
public static final String COMPONENT_APP = "app";
@@ -41,8 +39,11 @@ public class ResourceFactory {
4139
public static final String RESOURCE_TYPE_INGRESS = "ingress";
4240

4341
public Deployment getDefaultAppDeployment(ApicurioRegistry3 primary) {
44-
var r = initDefaultDeployment(primary, COMPONENT_APP, 1, ofNullable(primary.getSpec())
45-
.map(ApicurioRegistry3Spec::getApp).map(AppSpec::getPodTemplateSpec).orElse(null)); // TODO:
42+
var r = initDefaultDeployment(primary, COMPONENT_APP,
43+
Optional.ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp)
44+
.map(AppSpec::getReplicas).orElse(DEFAULT_REPLICAS),
45+
ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp)
46+
.map(AppSpec::getPodTemplateSpec).orElse(null)); // TODO:
4647
// Replicas
4748
mergeDeploymentPodTemplateSpec(
4849
// spotless:off
@@ -63,8 +64,11 @@ public Deployment getDefaultAppDeployment(ApicurioRegistry3 primary) {
6364
}
6465

6566
public Deployment getDefaultUIDeployment(ApicurioRegistry3 primary) {
66-
var r = initDefaultDeployment(primary, COMPONENT_UI, 1, ofNullable(primary.getSpec())
67-
.map(ApicurioRegistry3Spec::getUi).map(UiSpec::getPodTemplateSpec).orElse(null)); // TODO:
67+
var r = initDefaultDeployment(primary, COMPONENT_UI,
68+
Optional.ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getUi)
69+
.map(UiSpec::getReplicas).orElse(DEFAULT_REPLICAS),
70+
ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getUi)
71+
.map(UiSpec::getPodTemplateSpec).orElse(null)); // TODO:
6872
// Replicas
6973
mergeDeploymentPodTemplateSpec(
7074
// spotless:off
@@ -85,9 +89,12 @@ public Deployment getDefaultUIDeployment(ApicurioRegistry3 primary) {
8589
}
8690

8791
public Deployment getDefaultStudioUIDeployment(ApicurioRegistry3 primary) {
88-
var r = initDefaultDeployment(primary, COMPONENT_STUDIO_UI, 1, ofNullable(primary.getSpec())
89-
.map(ApicurioRegistry3Spec::getStudioUi).map(StudioUiSpec::getPodTemplateSpec).orElse(null)); // TODO:
90-
// Replicas
92+
var r = initDefaultDeployment(primary, COMPONENT_STUDIO_UI,
93+
Optional.ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getStudioUi)
94+
.map(StudioUiSpec::getReplicas).orElse(DEFAULT_REPLICAS),
95+
ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getStudioUi)
96+
.map(StudioUiSpec::getPodTemplateSpec).orElse(null)); // TODO:
97+
// Replicas
9198
mergeDeploymentPodTemplateSpec(
9299
// spotless:off
93100
r.getSpec().getTemplate(),

operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java

+46
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,52 @@ public void beforeEach(TestInfo testInfo) {
105105
// spotless:on
106106
}
107107

108+
protected static void checkDeploymentExists(ApicurioRegistry3 primary, String component, int replicas) {
109+
await().ignoreExceptions().untilAsserted(() -> {
110+
assertThat(client.apps().deployments()
111+
.withName(primary.getMetadata().getName() + "-" + component + "-deployment").get()
112+
.getStatus().getReadyReplicas()).isEqualTo(replicas);
113+
});
114+
}
115+
116+
protected static void checkDeploymentDoesNotExist(ApicurioRegistry3 primary, String component) {
117+
await().ignoreExceptions().untilAsserted(() -> {
118+
assertThat(client.apps().deployments()
119+
.withName(primary.getMetadata().getName() + "-" + component + "-deployment").get())
120+
.isNull();
121+
});
122+
}
123+
124+
protected static void checkServiceExists(ApicurioRegistry3 primary, String component) {
125+
await().ignoreExceptions().untilAsserted(() -> {
126+
assertThat(client.services()
127+
.withName(primary.getMetadata().getName() + "-" + component + "-service").get())
128+
.isNotNull();
129+
});
130+
}
131+
132+
protected static void checkServiceDoesNotExist(ApicurioRegistry3 primary, String component) {
133+
await().ignoreExceptions().untilAsserted(() -> {
134+
assertThat(client.services()
135+
.withName(primary.getMetadata().getName() + "-" + component + "-service").get()).isNull();
136+
});
137+
}
138+
139+
protected static void checkIngressExists(ApicurioRegistry3 primary, String component) {
140+
await().ignoreExceptions().untilAsserted(() -> {
141+
assertThat(client.network().v1().ingresses()
142+
.withName(primary.getMetadata().getName() + "-" + component + "-ingress").get())
143+
.isNotNull();
144+
});
145+
}
146+
147+
protected static void checkIngressDoesNotExist(ApicurioRegistry3 primary, String component) {
148+
await().ignoreExceptions().untilAsserted(() -> {
149+
assertThat(client.network().v1().ingresses()
150+
.withName(primary.getMetadata().getName() + "-" + component + "-ingress").get()).isNull();
151+
});
152+
}
153+
108154
static KubernetesClient createK8sClient(String namespace) {
109155
return new KubernetesClientBuilder()
110156
.withConfig(new ConfigBuilder(Config.autoConfigure(null)).withNamespace(namespace).build())

operator/controller/src/test/java/io/apicurio/registry/operator/it/SmokeITTest.java

+32
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import java.net.URI;
1717

1818
import static io.apicurio.registry.operator.api.v1.ContainerNames.REGISTRY_UI_CONTAINER_NAME;
19+
import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_APP;
20+
import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_UI;
1921
import static io.restassured.RestAssured.given;
2022
import static org.assertj.core.api.Assertions.assertThat;
2123
import static org.awaitility.Awaitility.await;
@@ -70,6 +72,36 @@ void smoke() {
7072
});
7173
}
7274

75+
@Test
76+
void replicas() {
77+
var registry = ResourceFactory.deserialize("/k8s/examples/simple.apicurioregistry3.yaml",
78+
ApicurioRegistry3.class);
79+
80+
registry.getMetadata().setNamespace(namespace);
81+
registry.getSpec().getApp().setHost(ingressManager.getIngressHost("app"));
82+
registry.getSpec().getUi().setHost(ingressManager.getIngressHost("ui"));
83+
84+
client.resource(registry).create();
85+
86+
// Verify first replica
87+
checkDeploymentExists(registry, COMPONENT_APP, 1);
88+
checkDeploymentExists(registry, COMPONENT_UI, 1);
89+
90+
// Scale up
91+
registry.getSpec().getApp().setReplicas(3);
92+
registry.getSpec().getUi().setReplicas(3);
93+
client.resource(registry).update();
94+
checkDeploymentExists(registry, COMPONENT_APP, 3);
95+
checkDeploymentExists(registry, COMPONENT_UI, 3);
96+
97+
// Scale down
98+
registry.getSpec().getApp().setReplicas(2);
99+
registry.getSpec().getUi().setReplicas(2);
100+
client.resource(registry).update();
101+
checkDeploymentExists(registry, COMPONENT_APP, 2);
102+
checkDeploymentExists(registry, COMPONENT_UI, 2);
103+
}
104+
73105
@Test
74106
void testService() {
75107

operator/controller/src/test/java/io/apicurio/registry/operator/it/StudioSmokeITTest.java

+37-50
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import static io.apicurio.registry.operator.it.SmokeITTest.ingressDisabled;
1313
import static io.apicurio.registry.operator.resource.ResourceFactory.*;
1414
import static io.restassured.RestAssured.given;
15-
import static org.assertj.core.api.Assertions.assertThat;
1615
import static org.awaitility.Awaitility.await;
1716

1817
@QuarkusTest
@@ -36,8 +35,8 @@ void smoke() {
3635

3736
client.resource(simpleRegistry).create();
3837

39-
checkDeploymentExists(simpleRegistry, COMPONENT_APP);
40-
checkDeploymentExists(simpleRegistry, COMPONENT_UI);
38+
checkDeploymentExists(simpleRegistry, COMPONENT_APP, 1);
39+
checkDeploymentExists(simpleRegistry, COMPONENT_UI, 1);
4140
checkDeploymentDoesNotExist(simpleRegistry, COMPONENT_STUDIO_UI);
4241

4342
checkServiceExists(simpleRegistry, COMPONENT_APP);
@@ -52,9 +51,9 @@ void smoke() {
5251
simpleRegistry.getSpec().withStudioUi().setEnabled(true);
5352
client.resource(simpleRegistry).update();
5453

55-
checkDeploymentExists(simpleRegistry, COMPONENT_APP);
56-
checkDeploymentExists(simpleRegistry, COMPONENT_UI);
57-
checkDeploymentExists(simpleRegistry, COMPONENT_STUDIO_UI);
54+
checkDeploymentExists(simpleRegistry, COMPONENT_APP, 1);
55+
checkDeploymentExists(simpleRegistry, COMPONENT_UI, 1);
56+
checkDeploymentExists(simpleRegistry, COMPONENT_STUDIO_UI, 1);
5857

5958
checkServiceExists(simpleRegistry, COMPONENT_APP);
6059
checkServiceExists(simpleRegistry, COMPONENT_UI);
@@ -96,9 +95,9 @@ void smoke() {
9695
simpleRegistry.getSpec().getStudioUi().getIngress().setHost(null);
9796
client.resource(simpleRegistry).update();
9897

99-
checkDeploymentExists(simpleRegistry, COMPONENT_APP);
100-
checkDeploymentExists(simpleRegistry, COMPONENT_UI);
101-
checkDeploymentExists(simpleRegistry, COMPONENT_STUDIO_UI);
98+
checkDeploymentExists(simpleRegistry, COMPONENT_APP, 1);
99+
checkDeploymentExists(simpleRegistry, COMPONENT_UI, 1);
100+
checkDeploymentExists(simpleRegistry, COMPONENT_STUDIO_UI, 1);
102101

103102
checkServiceExists(simpleRegistry, COMPONENT_APP);
104103
checkServiceExists(simpleRegistry, COMPONENT_UI);
@@ -112,8 +111,8 @@ void smoke() {
112111
simpleRegistry.getSpec().getStudioUi().setEnabled(false);
113112
client.resource(simpleRegistry).update();
114113

115-
checkDeploymentExists(simpleRegistry, COMPONENT_APP);
116-
checkDeploymentExists(simpleRegistry, COMPONENT_UI);
114+
checkDeploymentExists(simpleRegistry, COMPONENT_APP, 1);
115+
checkDeploymentExists(simpleRegistry, COMPONENT_UI, 1);
117116
checkDeploymentDoesNotExist(simpleRegistry, COMPONENT_STUDIO_UI);
118117

119118
checkServiceExists(simpleRegistry, COMPONENT_APP);
@@ -125,49 +124,37 @@ void smoke() {
125124
checkIngressDoesNotExist(simpleRegistry, COMPONENT_STUDIO_UI);
126125
}
127126

128-
private static void checkDeploymentExists(ApicurioRegistry3 primary, String component) {
129-
await().ignoreExceptions().untilAsserted(() -> {
130-
assertThat(client.apps().deployments()
131-
.withName(primary.getMetadata().getName() + "-" + component + "-deployment").get()
132-
.getStatus().getReadyReplicas()).isEqualTo(1);
133-
});
134-
}
127+
/**
128+
* Scenario: We want to check that the Studio component is not deployed by default unless the enabled
129+
* field is set to true, and, when activated, the number of replicas is set to the value specified while
130+
* checking that the basic Kubernetes resources are deployed as expected. We do not check Registry
131+
* components in detail, because that's done in other tests.
132+
*/
133+
@Test
134+
void replicas() {
135+
var simpleRegistry = ResourceFactory.deserialize("/k8s/examples/simple.apicurioregistry3.yaml",
136+
ApicurioRegistry3.class);
135137

136-
private static void checkDeploymentDoesNotExist(ApicurioRegistry3 primary, String component) {
137-
await().ignoreExceptions().untilAsserted(() -> {
138-
assertThat(client.apps().deployments()
139-
.withName(primary.getMetadata().getName() + "-" + component + "-deployment").get())
140-
.isNull();
141-
});
142-
}
138+
simpleRegistry.getMetadata().setNamespace(namespace);
139+
simpleRegistry.getSpec().getApp().setHost(ingressManager.getIngressHost(COMPONENT_APP));
140+
simpleRegistry.getSpec().getUi().setHost(ingressManager.getIngressHost(COMPONENT_UI));
143141

144-
private static void checkServiceExists(ApicurioRegistry3 primary, String component) {
145-
await().ignoreExceptions().untilAsserted(() -> {
146-
assertThat(client.services()
147-
.withName(primary.getMetadata().getName() + "-" + component + "-service").get())
148-
.isNotNull();
149-
});
150-
}
142+
client.resource(simpleRegistry).create();
151143

152-
private static void checkServiceDoesNotExist(ApicurioRegistry3 primary, String component) {
153-
await().ignoreExceptions().untilAsserted(() -> {
154-
assertThat(client.services()
155-
.withName(primary.getMetadata().getName() + "-" + component + "-service").get()).isNull();
156-
});
157-
}
144+
// We start with one replica for Registry
145+
checkDeploymentExists(simpleRegistry, COMPONENT_APP, 1);
146+
checkDeploymentExists(simpleRegistry, COMPONENT_UI, 1);
147+
checkDeploymentDoesNotExist(simpleRegistry, COMPONENT_STUDIO_UI);
158148

159-
private static void checkIngressExists(ApicurioRegistry3 primary, String component) {
160-
await().ignoreExceptions().untilAsserted(() -> {
161-
assertThat(client.network().v1().ingresses()
162-
.withName(primary.getMetadata().getName() + "-" + component + "-ingress").get())
163-
.isNotNull();
164-
});
165-
}
149+
// Now let's enable the Studio component and scale Registry to 3 replicas
150+
simpleRegistry.getSpec().withStudioUi().setEnabled(true);
151+
simpleRegistry.getSpec().getApp().setReplicas(3);
152+
simpleRegistry.getSpec().getUi().setReplicas(3);
153+
simpleRegistry.getSpec().getStudioUi().setReplicas(3);
154+
client.resource(simpleRegistry).update();
166155

167-
private static void checkIngressDoesNotExist(ApicurioRegistry3 primary, String component) {
168-
await().ignoreExceptions().untilAsserted(() -> {
169-
assertThat(client.network().v1().ingresses()
170-
.withName(primary.getMetadata().getName() + "-" + component + "-ingress").get()).isNull();
171-
});
156+
checkDeploymentExists(simpleRegistry, COMPONENT_APP, 3);
157+
checkDeploymentExists(simpleRegistry, COMPONENT_UI, 3);
158+
checkDeploymentExists(simpleRegistry, COMPONENT_STUDIO_UI, 3);
172159
}
173160
}

operator/install/install.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -3019,6 +3019,9 @@ spec:
30193019
type: array
30203020
type: object
30213021
type: object
3022+
replicas:
3023+
description: Number of replicas for the component
3024+
type: integer
30223025
sql:
30233026
description: |-
30243027
DEPRECATED: Use the `app.storage.type` and `app.storage.sql` fields instead.
@@ -6088,6 +6091,9 @@ spec:
60886091
type: array
60896092
type: object
60906093
type: object
6094+
replicas:
6095+
description: Number of replicas for the component
6096+
type: integer
60916097
type: object
60926098
ui:
60936099
description: |
@@ -9074,6 +9080,9 @@ spec:
90749080
type: array
90759081
type: object
90769082
type: object
9083+
replicas:
9084+
description: Number of replicas for the component
9085+
type: integer
90779086
type: object
90789087
type: object
90799088
status:

operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ public abstract class ComponentSpec {
8484
@JsonSetter(nulls = Nulls.SKIP)
8585
private String host;
8686

87+
/**
88+
* Number of replicas for the component
89+
*/
90+
@JsonProperty("replicas")
91+
@JsonPropertyDescription("Number of replicas for the component")
92+
@JsonSetter(nulls = Nulls.SKIP)
93+
private Integer replicas;
94+
8795
public IngressSpec withIngress() {
8896
if (ingress == null) {
8997
ingress = new IngressSpec();

0 commit comments

Comments
 (0)