Skip to content

Commit 354990d

Browse files
committed
Add KubernetesClusterFixtures to apply fixtures to dev service cluster in dev and test
1 parent 91ebc92 commit 354990d

File tree

4 files changed

+70
-73
lines changed

4 files changed

+70
-73
lines changed

extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/DevServicesKubernetesProcessor.java

Lines changed: 22 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
import java.net.MalformedURLException;
1010
import java.net.URL;
1111
import java.time.Duration;
12-
import java.util.ArrayList;
1312
import java.util.List;
1413
import java.util.Optional;
15-
import java.util.concurrent.TimeUnit;
1614
import java.util.function.Function;
1715
import java.util.stream.Collectors;
1816

@@ -28,34 +26,30 @@
2826
import com.dajudge.kindcontainer.KubernetesImageSpec;
2927
import com.dajudge.kindcontainer.KubernetesVersionEnum;
3028

31-
import io.fabric8.kubernetes.api.model.Endpoints;
3229
import io.fabric8.kubernetes.api.model.HasMetadata;
33-
import io.fabric8.kubernetes.api.model.Node;
34-
import io.fabric8.kubernetes.api.model.Pod;
35-
import io.fabric8.kubernetes.api.model.ReplicationController;
36-
import io.fabric8.kubernetes.api.model.apps.Deployment;
37-
import io.fabric8.kubernetes.api.model.apps.ReplicaSet;
38-
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
3930
import io.fabric8.kubernetes.client.Config;
4031
import io.fabric8.kubernetes.client.KubernetesClient;
4132
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
4233
import io.quarkus.deployment.Feature;
4334
import io.quarkus.deployment.IsDevServicesSupportedByLaunchMode;
35+
import io.quarkus.deployment.IsDevelopment;
36+
import io.quarkus.deployment.IsTest;
4437
import io.quarkus.deployment.annotations.BuildProducer;
4538
import io.quarkus.deployment.annotations.BuildStep;
4639
import io.quarkus.deployment.annotations.BuildSteps;
47-
import io.quarkus.deployment.annotations.Produce;
40+
import io.quarkus.deployment.annotations.ExecutionTime;
41+
import io.quarkus.deployment.annotations.Record;
4842
import io.quarkus.deployment.builditem.DevServicesComposeProjectBuildItem;
4943
import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
5044
import io.quarkus.deployment.builditem.DevServicesSharedNetworkBuildItem;
5145
import io.quarkus.deployment.builditem.DockerStatusBuildItem;
5246
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
53-
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
5447
import io.quarkus.deployment.dev.devservices.DevServicesConfig;
5548
import io.quarkus.devservices.common.ComposeLocator;
5649
import io.quarkus.devservices.common.ConfigureUtil;
5750
import io.quarkus.devservices.common.ContainerLocator;
5851
import io.quarkus.kubernetes.client.runtime.internal.KubernetesClientBuildConfig;
52+
import io.quarkus.kubernetes.client.runtime.internal.KubernetesClusterFixtures;
5953
import io.quarkus.kubernetes.client.runtime.internal.KubernetesDevServicesBuildTimeConfig;
6054
import io.quarkus.kubernetes.client.runtime.internal.KubernetesDevServicesBuildTimeConfig.Flavor;
6155
import io.quarkus.kubernetes.client.spi.KubernetesDevServiceInfoBuildItem;
@@ -69,7 +63,7 @@ public class DevServicesKubernetesProcessor {
6963
static final int KUBERNETES_PORT = 6443;
7064
private static final String KUBERNETES_CLIENT_DEVSERVICES_OVERRIDE_KUBECONFIG = "quarkus.kubernetes-client.devservices.override-kubeconfig";
7165
private static final Logger log = Logger.getLogger(DevServicesKubernetesProcessor.class);
72-
static final String KUBERNETES_CLIENT_MASTER_URL = "quarkus.kubernetes-client.api-server-url";
66+
static final String KUBERNETES_CLIENT_API_SERVER_URL = "quarkus.kubernetes-client.api-server-url";
7367
private static final String DEFAULT_MASTER_URL_ENDING_WITH_SLASH = Config.DEFAULT_MASTER_URL + "/";
7468
private static final ContainerLocator KubernetesContainerLocator = locateContainerWithLabels(KUBERNETES_PORT,
7569
DEV_SERVICE_LABEL);
@@ -135,8 +129,9 @@ public String getContainerId() {
135129
.startable(() -> container)
136130
.configProvider(
137131
StartableKubernetesContainer.getKubernetesClientConfigFromRunningContainerKubeConfig())
138-
.postStartHook(unused -> log.info(
139-
"Dev Services for Kubernetes started. Other Quarkus applications in dev mode will find the cluster automatically."))
132+
.postStartHook(started -> log.infof(
133+
"Dev Services for Kubernetes started at %s. Other Quarkus applications in dev mode will find the cluster automatically.",
134+
started.getKubeClientConfigFor(KUBERNETES_CLIENT_API_SERVER_URL)))
140135
.build();
141136

142137
devServicesKube.produce(new KubernetesDevServiceInfoBuildItem(
@@ -167,9 +162,9 @@ private boolean devServiceDisabled(DockerStatusBuildItem dockerStatusBuildItem,
167162
}
168163

169164
// Check if kubernetes-client.api-server-url is set
170-
if (ConfigUtils.isPropertyNonEmpty(KUBERNETES_CLIENT_MASTER_URL)) {
165+
if (ConfigUtils.isPropertyNonEmpty(KUBERNETES_CLIENT_API_SERVER_URL)) {
171166
log.debug("Not starting Dev Services for Kubernetes as the client has been explicitly configured via "
172-
+ KUBERNETES_CLIENT_MASTER_URL);
167+
+ KUBERNETES_CLIENT_API_SERVER_URL);
173168
return true;
174169
}
175170

@@ -227,34 +222,22 @@ private StartableKubernetesContainer createContainer(KubernetesDevServicesBuildT
227222
}
228223

229224
/**
230-
* Deploys a set of manifests as files in the resources directory to the Kubernetes dev service.
231-
* This build step produces a {@link ServiceStartBuildItem} that ensures the Build Step always runs even if no other build
232-
* step consumes it.
225+
* Prepares configured manifests via {@link KubernetesDevServicesBuildTimeConfig#manifests()} to be applied at runtime
233226
*
234-
* @param kubernetesDevServiceInfoBuildItem This ensures the manifests are deployed after the Kubernetes dev service is
235-
* started.
236227
* @param kubernetesClientBuildTimeConfig This config is used to read the extension configuration for dev services.
228+
* @param clusterFixtures records byte-code to apply configure manifests at runtime
237229
*/
238-
@BuildStep
239-
@Produce(ServiceStartBuildItem.class)
230+
@BuildStep(onlyIf = { IsDevelopment.class, IsTest.class })
231+
@Record(ExecutionTime.STATIC_INIT)
240232
public void applyManifests(
241-
KubernetesDevServiceInfoBuildItem kubernetesDevServiceInfoBuildItem,
242-
KubernetesClientBuildConfig kubernetesClientBuildTimeConfig) {
243-
if (kubernetesDevServiceInfoBuildItem == null) {
244-
// Gracefully return in case the Kubernetes dev service could not be spun up.
245-
log.warn("Cannot apply manifests because the Kubernetes dev service is not running");
246-
return;
247-
}
248-
233+
KubernetesClientBuildConfig kubernetesClientBuildTimeConfig, KubernetesClusterFixtures clusterFixtures) {
249234
var manifests = kubernetesClientBuildTimeConfig.devservices().manifests();
250235

251236
// Do not run the manifest deployment if no manifests are configured
252237
if (manifests.isEmpty())
253238
return;
254239

255-
try (KubernetesClient client = new KubernetesClientBuilder()
256-
.withConfig(Config.fromKubeconfig(kubernetesDevServiceInfoBuildItem.getKubeConfig()))
257-
.build()) {
240+
try (KubernetesClient client = new KubernetesClientBuilder().build()) {
258241
for (String manifestPath : manifests.get()) {
259242
InputStream manifestStream = getManifestStream(manifestPath);
260243

@@ -267,49 +250,19 @@ public void applyManifests(
267250
try {
268251
// A single manifest file may contain multiple resources to deploy
269252
List<HasMetadata> resources = client.load(manifestStream).items();
270-
List<HasMetadata> resourcesWithReadiness = new ArrayList<>();
271-
272-
if (resources.isEmpty()) {
273-
log.warnf("No resources found in manifest: %s", manifestPath);
274-
} else {
275-
resources.forEach(resource -> {
276-
client.resource(resource).create();
277-
278-
if (isReadinessApplicable(resource)) {
279-
resourcesWithReadiness.add(resource);
280-
}
281-
});
282-
283-
resourcesWithReadiness.forEach(resource -> {
284-
log.info("Waiting for " + resource.getClass().getSimpleName() + " "
285-
+ resource.getMetadata().getName()
286-
+ " to be ready...");
287-
client.resource(resource).waitUntilReady(60, TimeUnit.SECONDS);
288-
});
289-
290-
log.infof("Applied manifest %s.", manifestPath);
291-
}
253+
// records byte-code to apply the loaded resources at runtime
254+
clusterFixtures.apply(resources);
255+
log.infof("==== Recorded manifest %s to be applied at runtime", manifestPath);
292256
} catch (Exception ex) {
293-
log.errorf("Failed to apply manifest %s: %s", manifestPath, ex.getMessage());
257+
log.errorf("Failed to record manifest %s: %s", manifestPath, ex.getMessage());
294258
}
295259
}
296260
}
297261
} catch (Exception e) {
298-
log.error("Failed to create Kubernetes client while trying to apply manifests.", e);
262+
log.error("Failed to create Kubernetes client to load manifests", e);
299263
}
300264
}
301265

302-
private boolean isReadinessApplicable(HasMetadata item) {
303-
return (item instanceof Deployment ||
304-
item instanceof io.fabric8.kubernetes.api.model.extensions.Deployment ||
305-
item instanceof ReplicaSet ||
306-
item instanceof Pod ||
307-
item instanceof ReplicationController ||
308-
item instanceof Endpoints ||
309-
item instanceof Node ||
310-
item instanceof StatefulSet);
311-
}
312-
313266
private InputStream getManifestStream(String manifestPath) throws IOException {
314267
try {
315268
URL url = new URL(manifestPath);

extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/RunningKubeContainer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.quarkus.kubernetes.client.deployment;
22

3-
import static io.quarkus.kubernetes.client.deployment.DevServicesKubernetesProcessor.KUBERNETES_CLIENT_MASTER_URL;
3+
import static io.quarkus.kubernetes.client.deployment.DevServicesKubernetesProcessor.KUBERNETES_CLIENT_API_SERVER_URL;
44
import static java.nio.charset.StandardCharsets.UTF_8;
55

66
import java.io.ByteArrayOutputStream;
@@ -68,7 +68,7 @@ static Map<String, String> getKubernetesClientConfigFromKubeConfig(KubeConfig ku
6868
.getClusters().get(0).getCluster();
6969
UserSpec user = kubeConfig.getUsers().get(0).getUser();
7070
return Map.of(
71-
KUBERNETES_CLIENT_MASTER_URL, cluster.getServer(),
71+
KUBERNETES_CLIENT_API_SERVER_URL, cluster.getServer(),
7272
"quarkus.kubernetes-client.ca-cert-data",
7373
cluster.getCertificateAuthorityData(),
7474
"quarkus.kubernetes-client.client-cert-data",

extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/StartableKubernetesContainer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.quarkus.kubernetes.client.deployment;
22

3-
import static io.quarkus.kubernetes.client.deployment.DevServicesKubernetesProcessor.KUBERNETES_CLIENT_MASTER_URL;
3+
import static io.quarkus.kubernetes.client.deployment.DevServicesKubernetesProcessor.KUBERNETES_CLIENT_API_SERVER_URL;
44

55
import java.util.Map;
66
import java.util.function.Function;
@@ -71,7 +71,7 @@ private static Function<StartableKubernetesContainer, String> mapperFor(String k
7171

7272
static Map<String, Function<StartableKubernetesContainer, String>> getKubernetesClientConfigFromRunningContainerKubeConfig() {
7373
return Map.of(
74-
KUBERNETES_CLIENT_MASTER_URL, mapperFor(KUBERNETES_CLIENT_MASTER_URL),
74+
KUBERNETES_CLIENT_API_SERVER_URL, mapperFor(KUBERNETES_CLIENT_API_SERVER_URL),
7575
"quarkus.kubernetes-client.ca-cert-data",
7676
mapperFor("quarkus.kubernetes-client.ca-cert-data"),
7777
"quarkus.kubernetes-client.client-cert-data",
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.quarkus.kubernetes.client.runtime.internal;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.concurrent.TimeUnit;
6+
7+
import org.jboss.logging.Logger;
8+
9+
import io.fabric8.kubernetes.api.model.HasMetadata;
10+
import io.fabric8.kubernetes.client.KubernetesClient;
11+
import io.fabric8.kubernetes.client.readiness.Readiness;
12+
import io.quarkus.arc.Arc;
13+
import io.quarkus.runtime.annotations.Recorder;
14+
15+
@Recorder
16+
public class KubernetesClusterFixtures {
17+
private static final Logger log = Logger.getLogger(KubernetesClusterFixtures.class.getName());
18+
19+
public void apply(List<HasMetadata> resources) {
20+
try (final var clientHandle = Arc.container().instance(KubernetesClient.class)) {
21+
final var client = clientHandle.get();
22+
List<HasMetadata> resourcesWithReadiness = new ArrayList<>();
23+
resources.forEach(resource -> {
24+
log.infof("Applying %s %s to the cluster",
25+
resource.getClass().getSimpleName(),
26+
resource.getMetadata().getName());
27+
client.resource(resource).create();
28+
29+
if (Readiness.getInstance().isReadinessApplicable(resource)) {
30+
resourcesWithReadiness.add(resource);
31+
}
32+
});
33+
34+
resourcesWithReadiness.forEach(resource -> {
35+
log.infof("Waiting for %s %s to be ready...",
36+
resource.getClass().getSimpleName(),
37+
resource.getMetadata().getName());
38+
client.resource(resource).waitUntilReady(60, TimeUnit.SECONDS);
39+
});
40+
} catch (Exception e) {
41+
throw new RuntimeException(e);
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)