Skip to content

Commit 931e20c

Browse files
authored
Merge pull request quarkusio#48990 from LarsSven/ls/manifests-from-url
Apply manifests from URL and await resources with readiness
2 parents 8f33083 + a89bef3 commit 931e20c

File tree

4 files changed

+80
-16
lines changed

4 files changed

+80
-16
lines changed

docs/src/main/asciidoc/kubernetes-dev-services.adoc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,21 @@ And then add the following property to your `application.properties`:
8888
quarkus.kubernetes-client.devservices.manifests=kubernetes/namespace.yaml
8989
```
9090

91+
When applying multiple properties, you can apply them as:
92+
```
93+
quarkus.kubernetes-client.devservices.manifests=kubernetes/first_manifest.yaml,kubernetes/second_manifest.yaml
94+
```
95+
96+
It is also possible to apply manifests from a URL. So the following syntax would also be possible:
97+
```
98+
quarkus.kubernetes-client.devservices.manifests=https://example.com/kubernetes/namespace.yaml
99+
```
100+
101+
NOTE: Quarkus will apply the manifests in the order they are specified in the list.
102+
103+
NOTE: Quarkus will await resources such as deployments to be ready before moving on to the next manifest.
104+
All applied resources are guaranteed to be ready by the time your application finishes startup.
105+
91106
=== Deploying helm charts
92107
`quarkus.kubernetes-client.devservices.manifests` only supports manifests. If you want to deploy a helm chart, you can use the `k3s` flavor and deploy a `HelmChart` manifest. See https://docs.k3s.io/helm for more information on how to use helm charts with k3s.
93108

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

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111
import java.io.ByteArrayOutputStream;
1212
import java.io.IOException;
1313
import java.io.InputStream;
14+
import java.net.MalformedURLException;
15+
import java.net.URL;
1416
import java.time.Duration;
15-
import java.util.Base64;
16-
import java.util.List;
17-
import java.util.Map;
18-
import java.util.Objects;
19-
import java.util.Optional;
17+
import java.util.*;
18+
import java.util.concurrent.TimeUnit;
2019
import java.util.function.Function;
2120
import java.util.function.Supplier;
2221
import java.util.stream.Collectors;
@@ -45,8 +44,12 @@
4544
import com.github.dockerjava.api.DockerClient;
4645
import com.github.dockerjava.api.command.InspectContainerResponse;
4746

48-
import io.fabric8.kubernetes.api.model.HasMetadata;
47+
import io.fabric8.kubernetes.api.model.*;
48+
import io.fabric8.kubernetes.api.model.apps.Deployment;
49+
import io.fabric8.kubernetes.api.model.apps.ReplicaSet;
50+
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
4951
import io.fabric8.kubernetes.client.*;
52+
import io.fabric8.kubernetes.client.Config;
5053
import io.quarkus.deployment.Feature;
5154
import io.quarkus.deployment.IsDevServicesSupportedByLaunchMode;
5255
import io.quarkus.deployment.annotations.BuildProducer;
@@ -196,10 +199,7 @@ public void applyManifests(
196199
.withConfig(Config.fromKubeconfig(kubernetesDevServiceInfoBuildItem.getKubeConfig()))
197200
.build()) {
198201
for (String manifestPath : manifests.get()) {
199-
// Load the manifest from the resources directory
200-
InputStream manifestStream = Thread.currentThread()
201-
.getContextClassLoader()
202-
.getResourceAsStream(manifestPath);
202+
InputStream manifestStream = getManifestStream(manifestPath);
203203

204204
if (manifestStream == null) {
205205
log.errorf("Could not find manifest file in resources: %s", manifestPath);
@@ -210,21 +210,63 @@ public void applyManifests(
210210
try {
211211
// A single manifest file may contain multiple resources to deploy
212212
List<HasMetadata> resources = client.load(manifestStream).items();
213+
List<HasMetadata> resourcesWithReadiness = new ArrayList<>();
213214

214215
if (resources.isEmpty()) {
215216
log.warnf("No resources found in manifest: %s", manifestPath);
216217
} else {
217-
resources.forEach(resource -> client.resource(resource).create());
218+
resources.forEach(resource -> {
219+
client.resource(resource).create();
220+
221+
if (isReadinessApplicable(resource)) {
222+
resourcesWithReadiness.add(resource);
223+
}
224+
});
225+
226+
resourcesWithReadiness.forEach(resource -> {
227+
log.info("Waiting for " + resource.getClass().getSimpleName() + " "
228+
+ resource.getMetadata().getName()
229+
+ " to be ready...");
230+
client.resource(resource).waitUntilReady(60, TimeUnit.SECONDS);
231+
});
218232
}
219233
} catch (Exception ex) {
220-
log.errorf("Failed to deploy manifest %s: %s", manifestPath, ex.getMessage());
234+
log.errorf("Failed to apply manifest %s: %s", manifestPath, ex.getMessage());
221235
}
222236
}
223237

224238
log.infof("Applied manifest %s.", manifestPath);
225239
}
226240
} catch (Exception e) {
227-
log.error("Failed to create Kubernetes client while trying to deploy manifests.", e);
241+
log.error("Failed to create Kubernetes client while trying to apply manifests.", e);
242+
}
243+
}
244+
245+
private boolean isReadinessApplicable(HasMetadata item) {
246+
return (item instanceof Deployment ||
247+
item instanceof io.fabric8.kubernetes.api.model.extensions.Deployment ||
248+
item instanceof ReplicaSet ||
249+
item instanceof Pod ||
250+
item instanceof ReplicationController ||
251+
item instanceof Endpoints ||
252+
item instanceof Node ||
253+
item instanceof StatefulSet);
254+
}
255+
256+
private InputStream getManifestStream(String manifestPath) throws IOException {
257+
try {
258+
URL url = new URL(manifestPath);
259+
// For file:// URLs, optionally check if you want to support those or not
260+
return url.openStream();
261+
} catch (MalformedURLException e) {
262+
// Not a URL, so treat as classpath resource
263+
InputStream stream = Thread.currentThread()
264+
.getContextClassLoader()
265+
.getResourceAsStream(manifestPath);
266+
if (stream == null) {
267+
throw new IOException("Resource not found: " + manifestPath);
268+
}
269+
return stream;
228270
}
229271
}
230272

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
quarkus.kubernetes-client.devservices.manifests=kubernetes/namespace.yaml
1+
quarkus.kubernetes-client.devservices.manifests=kubernetes/namespace.yaml,https://k8s.io/examples/admin/namespace-dev.yaml

integration-tests/kubernetes-client-devservices/src/test/java/io/quarkus/kubernetes/client/devservices/it/DevServicesKubernetesTest.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,15 @@ public void shouldReturnAllKeys() {
2626
}
2727

2828
@Test
29-
@DisplayName("specified manifest must be applied to the cluster by the dev service")
30-
public void manifestIsApplied() {
29+
@DisplayName("specified manifest in the resources folder must be applied to the cluster by the dev service")
30+
public void resourceManifestIsApplied() {
3131
Assertions.assertNotNull(kubernetesClient.namespaces().withName("example-namespace").get());
3232
}
33+
34+
@Test
35+
@DisplayName("specified manifest from a URL must be applied to the cluster by the dev service")
36+
public void urlManifestIsApplied() {
37+
// Applied by https://k8s.io/examples/admin/namespace-dev.yaml
38+
Assertions.assertNotNull(kubernetesClient.namespaces().withName("development").get());
39+
}
3340
}

0 commit comments

Comments
 (0)