Skip to content

Commit

Permalink
Add nodeSelect capability to kubernetes extension.
Browse files Browse the repository at this point in the history
Fix quarkusio#44122

Signed-off-by: Vinche <[email protected]>
  • Loading branch information
Vinche59 committed Nov 9, 2024
1 parent 1e2140d commit e5a21e4
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 0 deletions.
21 changes: 21 additions & 0 deletions docs/src/main/asciidoc/deploying-to-kubernetes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,27 @@ spec:
ip: 10.0.0.0
----

=== Add nodeSelector
To add a nodeSelector in the generated `Deployment` (more information can be found in https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes[Kubernetes documentation]), just apply the following configuration:

[source,properties]
----
quarkus.kubernetes.node-selector.key=diskType
quarkus.kubernetes.node-selector.value=ssd
----

This would generate the following `nodeSelector` section in the `deployment` definition:

[source,yaml]
----
kind: Deployment
spec:
template:
spec:
nodeSelector:
diskType: ssd
----

=== Container Resources Management

CPU & Memory limits and requests can be applied to a `Container` (more info in https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/[Kubernetes documentation]) using the following configuration:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkus.kubernetes.deployment;

import io.dekorate.kubernetes.decorator.NamedResourceDecorator;
import io.dekorate.utils.Strings;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.PodSpecFluent;

public class AddNodeSelectorDecorator extends NamedResourceDecorator<PodSpecFluent<?>> {
private final String nodeSelectorKey;
private final String nodeSelectorValue;

public AddNodeSelectorDecorator(String deploymentName, String nodeSelectorKey, String nodeSelectorValue) {
super(deploymentName);
this.nodeSelectorKey = nodeSelectorKey;
this.nodeSelectorValue = nodeSelectorValue;
}

public void andThenVisit(PodSpecFluent<?> podSpec, ObjectMeta resourceMeta) {
if (Strings.isNotNullOrEmpty(nodeSelectorKey) && Strings.isNotNullOrEmpty(nodeSelectorValue)) {
podSpec.removeFromNodeSelector(nodeSelectorKey);
podSpec.addToNodeSelector(nodeSelectorKey, nodeSelectorValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ public List<DecoratorBuildItem> createDecorators(ApplicationInfoBuildItem applic
result.addAll(createAppConfigVolumeAndEnvDecorators(name, config));
config.hostAliases().entrySet().forEach(e -> result.add(new DecoratorBuildItem(KNATIVE,
new AddHostAliasesToRevisionDecorator(name, HostAliasConverter.convert(e)))));
config.nodeSelector().ifPresent(n -> result.add(new DecoratorBuildItem(KNATIVE,
new AddNodeSelectorDecorator(name, n.key(), n.value()))));
config.sidecars().entrySet().forEach(e -> result
.add(new DecoratorBuildItem(KNATIVE, new AddSidecarToRevisionDecorator(name, ContainerConverter.convert(e)))));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,10 @@ private static List<DecoratorBuildItem> createPodDecorators(String target, Strin
config.hostAliases().entrySet().forEach(e -> result
.add(new DecoratorBuildItem(target, new AddHostAliasesDecorator(name, HostAliasConverter.convert(e)))));

config.nodeSelector()
.ifPresent(n -> result.add(
new DecoratorBuildItem(target, new AddNodeSelectorDecorator(name, n.key(), n.value()))));

config.initContainers().entrySet().forEach(e -> result
.add(new DecoratorBuildItem(target, new AddInitContainerDecorator(name, ContainerConverter.convert(e)))));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.kubernetes.deployment;

public interface NodeSelectorConfig {
/**
* The key of the nodeSelector.
*/
String key();

/**
* The value of the nodeSelector.
*/
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ public interface PlatformConfiguration extends EnvVarHolder {
@WithName("hostaliases")
Map<String, HostAliasConfig> hostAliases();

/**
* The nodeSelector.
*/
Optional<NodeSelectorConfig> nodeSelector();

/**
* Resources requirements.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ void kubernetes() throws Exception {
assertEquals("konoha", hostAliases.get("ip"));
assertIterableEquals(List.of("dev", "qly", "prod"), (Iterable<String>) hostAliases.get("hostnames"));

Map<String, Object> nodeSelector = deployment().map("spec").map("template").map("spec").asMap("nodeSelector");
assertTrue(nodeSelector.containsKey("jutsu"));
assertEquals("katon", nodeSelector.get("jutsu"));

Map<String, Object> limits = container().map("resources").asMap("limits");
assertEquals("fuuton", limits.get("cpu"));
assertEquals("raiton", limits.get("memory"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ quarkus.kubernetes.deployment-target=kubernetes,openshift,knative,minikube
quarkus.kubernetes.hostaliases.konoha.ip=0.0.0.0
quarkus.kubernetes.hostaliases.konoha.hostnames=dev,qly,prod

quarkus.kubernetes.node-selector.key=jutsu
quarkus.kubernetes.node-selector.value=katon

quarkus.kubernetes.resources.limits.cpu=fuuton
quarkus.kubernetes.resources.limits.memory=raiton
quarkus.kubernetes.resources.requests.cpu=katon
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.quarkus.it.kubernetes;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.quarkus.test.ProdBuildResults;
import io.quarkus.test.ProdModeTestResults;
import io.quarkus.test.QuarkusProdModeTest;

public class KubernetesWithNodeSelectorTest {

@RegisterExtension
static final QuarkusProdModeTest config = new QuarkusProdModeTest()
.withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class))
.setApplicationName("nodeselector")
.setApplicationVersion("0.1-SNAPSHOT")
.withConfigurationResource("kubernetes-with-nodeselector.properties");

@ProdBuildResults
private ProdModeTestResults prodModeTestResults;

@Test
public void assertGeneratedResources() throws IOException {
Map<String, String> expectedNodeSelector = Map.of("diskType", "ssd");

Path kubernetesDir = prodModeTestResults.getBuildDir().resolve("kubernetes");
assertThat(kubernetesDir)
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json"))
.isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml"));
List<HasMetadata> kubernetesList = DeserializationUtil
.deserializeAsList(kubernetesDir.resolve("kubernetes.yml"));
assertThat(kubernetesList.get(0)).isInstanceOfSatisfying(Deployment.class, d -> {
assertThat(d.getMetadata()).satisfies(m -> {
assertThat(m.getName()).isEqualTo("nodeselector");
});

assertThat(d.getSpec()).satisfies(deploymentSpec -> {
assertThat(deploymentSpec.getTemplate()).satisfies(t -> {
assertThat(t.getSpec()).satisfies(podSpec -> {
assertThat(podSpec.getNodeSelector()).containsExactlyEntriesOf(expectedNodeSelector);
});
});
});
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
quarkus.kubernetes.node-selector.key=diskType
quarkus.kubernetes.node-selector.value=ssd

0 comments on commit e5a21e4

Please sign in to comment.