Skip to content

Commit bb4a65d

Browse files
authored
Merge pull request #48552 from anisikram/add-config-for-custom-k8s-dev-container-image
Add the possibility to configure a custom image for k8s dev container
2 parents 663e799 + b209439 commit bb4a65d

File tree

4 files changed

+50
-16
lines changed

4 files changed

+50
-16
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ quarkus.kubernetes-client.devservices.flavor=api-only # k3s or kind
5555
quarkus.kubernetes-client.devservices.api-version=1.22
5656
----
5757

58+
You can also configure a custom image compatible with standard images (kind, k3s & api-server) using the `quarkus.kubernetes-client.devservices.image-name` property. However, it must be consistent with the flavor and api-version properties:
59+
60+
[source, properties]
61+
----
62+
quarkus.kubernetes-client.devservices.flavor=api-only # k3s or kind
63+
quarkus.kubernetes-client.devservices.api-version=1.24.1
64+
quarkus.kubernetes-client.devservices.image-name=quay.io/giantswarm/kube-apiserver:v1.24.1
65+
----
66+
5867
`api-only` only starts a Kubernetes API Server (plus the required etcd). If you need a fully-featured Kubernetes cluster that can spin up Pods, you can use `k3s` or `kind`. `k3s` requires to start the container with `privileged mode`. The `kind` test container now also supports using podman's rootless mode.
5968

6069
If `api-version` is not set, the latest version for the given flavor will be used. Otherwise, the version must match a https://github.com/dajudge/kindcontainer/blob/master/k8s-versions.json[version supported by the given flavor].

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

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.Map;
1818
import java.util.Objects;
1919
import java.util.Optional;
20+
import java.util.function.Function;
2021
import java.util.function.Supplier;
2122
import java.util.stream.Collectors;
2223

@@ -30,6 +31,8 @@
3031
import com.dajudge.kindcontainer.K3sContainerVersion;
3132
import com.dajudge.kindcontainer.KindContainer;
3233
import com.dajudge.kindcontainer.KindContainerVersion;
34+
import com.dajudge.kindcontainer.KubernetesContainer;
35+
import com.dajudge.kindcontainer.KubernetesImageSpec;
3336
import com.dajudge.kindcontainer.KubernetesVersionEnum;
3437
import com.dajudge.kindcontainer.client.KubeConfigUtils;
3538
import com.dajudge.kindcontainer.client.config.Cluster;
@@ -225,18 +228,12 @@ private RunningDevService startKubernetes(DockerStatusBuildItem dockerStatusBuil
225228
.orElse(api_only);
226229

227230
@SuppressWarnings("rawtypes")
228-
final var container = switch (clusterType) {
229-
case api_only -> new ApiServerContainer(
230-
config.apiVersion
231-
.map(version -> findOrElseThrow(clusterType, version, ApiServerContainerVersion.class))
232-
.orElseGet(() -> latest(ApiServerContainerVersion.class)));
233-
case k3s -> new K3sContainer(
234-
config.apiVersion.map(version -> findOrElseThrow(clusterType, version, K3sContainerVersion.class))
235-
.orElseGet(() -> latest(K3sContainerVersion.class)));
236-
case kind -> new KindContainer(
237-
config.apiVersion
238-
.map(version -> findOrElseThrow(clusterType, version, KindContainerVersion.class))
239-
.orElseGet(() -> latest(KindContainerVersion.class)));
231+
KubernetesContainer container = switch (clusterType) {
232+
case api_only -> createContainer(ApiServerContainer::new, ApiServerContainerVersion.class, config, clusterType);
233+
234+
case k3s -> createContainer(K3sContainer::new, K3sContainerVersion.class, config, clusterType);
235+
236+
case kind -> createContainer(KindContainer::new, KindContainerVersion.class, config, clusterType);
240237
};
241238

242239
if (useSharedNetwork) {
@@ -270,7 +267,21 @@ private RunningDevService startKubernetes(DockerStatusBuildItem dockerStatusBuil
270267
.orElseGet(defaultKubernetesClusterSupplier);
271268
}
272269

273-
<T extends KubernetesVersionEnum<T>> T findOrElseThrow(final Flavor flavor, final String version,
270+
@SuppressWarnings("rawtypes")
271+
private <T extends KubernetesVersionEnum<T>, C extends KubernetesContainer> C createContainer(
272+
Function<KubernetesImageSpec<T>, C> constructor,
273+
Class<T> versionClass,
274+
KubernetesDevServiceCfg config,
275+
Flavor flavor) {
276+
T version = config.apiVersion
277+
.map(v -> findOrElseThrow(flavor, v, versionClass))
278+
.orElseGet(() -> latest(versionClass));
279+
280+
KubernetesImageSpec<T> imageSpec = version.withImage(config.imageName);
281+
return constructor.apply(imageSpec);
282+
}
283+
284+
private <T extends KubernetesVersionEnum<T>> T findOrElseThrow(final Flavor flavor, final String version,
274285
final Class<T> versions) {
275286
final String versionWithPrefix = !version.startsWith("v") ? "v" + version : version;
276287
return KubernetesVersionEnum.ascending(versions)
@@ -315,6 +326,7 @@ private KubernetesDevServiceCfg getConfiguration(KubernetesClientBuildConfig cfg
315326
private static final class KubernetesDevServiceCfg {
316327

317328
public boolean devServicesEnabled;
329+
public String imageName;
318330
public Optional<Flavor> flavor;
319331
public Optional<String> apiVersion;
320332
public boolean overrideKubeconfig;
@@ -324,6 +336,8 @@ private static final class KubernetesDevServiceCfg {
324336

325337
public KubernetesDevServiceCfg(KubernetesDevServicesBuildTimeConfig config) {
326338
this.devServicesEnabled = config.enabled();
339+
this.imageName = config.imageName()
340+
.orElse(null);
327341
this.serviceName = config.serviceName();
328342
this.apiVersion = config.apiVersion();
329343
this.overrideKubeconfig = config.overrideKubeconfig();
@@ -334,7 +348,8 @@ public KubernetesDevServiceCfg(KubernetesDevServicesBuildTimeConfig config) {
334348

335349
@Override
336350
public int hashCode() {
337-
return Objects.hash(devServicesEnabled, flavor, apiVersion, overrideKubeconfig, shared, serviceName, containerEnv);
351+
return Objects.hash(devServicesEnabled, imageName, flavor, apiVersion, overrideKubeconfig, shared, serviceName,
352+
containerEnv);
338353
}
339354

340355
@Override

extensions/kubernetes-client/runtime-internal/src/main/java/io/quarkus/kubernetes/client/runtime/internal/KubernetesDevServicesBuildTimeConfig.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ public interface KubernetesDevServicesBuildTimeConfig {
2626
*/
2727
Optional<String> apiVersion();
2828

29+
/**
30+
* The kubernetes image to use.
31+
* <p>
32+
* If not set, Dev Services for Kubernetes will use default image for the specified {@link #apiVersion()} for the given
33+
* {@link #flavor()}.
34+
*/
35+
36+
Optional<String> imageName();
37+
2938
/**
3039
* The flavor to use (kind, k3s or api-only).
3140
* <p>

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

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

3-
import java.util.Collections;
43
import java.util.Map;
54

65
import io.quarkus.test.junit.QuarkusTestProfile;
@@ -12,6 +11,8 @@ public class DevServiceKubernetes implements QuarkusTestProfile {
1211
@Override
1312
public Map<String, String> getConfigOverrides() {
1413

15-
return Collections.singletonMap("quarkus.kubernetes-client.devservices.api-version", API_VERSION);
14+
return Map.of("quarkus.kubernetes-client.devservices.api-version", API_VERSION,
15+
"quarkus.kubernetes-client.devservices.image-name",
16+
"quay.io/giantswarm/kube-apiserver:v%s".formatted(API_VERSION));
1617
}
1718
}

0 commit comments

Comments
 (0)