Skip to content

Commit fe03b26

Browse files
committed
Fix #34843: improve parser of k8s resources and limits
1 parent 143ea9c commit fe03b26

3 files changed

Lines changed: 75 additions & 7 deletions

File tree

src/main/java/eu/openanalytics/containerproxy/backend/kubernetes/KubernetesBackend.java

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,26 @@ public Proxy startContainer(Authentication user, Container initialContainer, Con
266266
.build();
267267

268268
ResourceRequirementsBuilder resourceRequirementsBuilder = new ResourceRequirementsBuilder();
269-
resourceRequirementsBuilder.addToRequests("cpu", spec.getCpuRequest().mapOrNull(Quantity::new));
270-
resourceRequirementsBuilder.addToLimits("cpu", spec.getCpuLimit().mapOrNull(Quantity::new));
271-
resourceRequirementsBuilder.addToRequests("memory", spec.getMemoryRequest().mapOrNull(Quantity::new));
272-
resourceRequirementsBuilder.addToLimits("memory", spec.getMemoryLimit().mapOrNull(Quantity::new));
269+
try {
270+
resourceRequirementsBuilder.addToRequests("cpu", parseCpuQuantity(spec.getCpuRequest().getValueOrNull()));
271+
} catch (IllegalArgumentException ex) {
272+
throw new IllegalArgumentException("Invalid container-cpu-request: " + ex.getMessage());
273+
}
274+
try {
275+
resourceRequirementsBuilder.addToLimits("cpu", parseCpuQuantity(spec.getCpuLimit().getValueOrNull()));
276+
} catch (IllegalArgumentException ex) {
277+
throw new IllegalArgumentException("Invalid container-cpu-limit: " + ex.getMessage());
278+
}
279+
try {
280+
resourceRequirementsBuilder.addToRequests("memory", parseMemoryQuantity(spec.getMemoryRequest().getValueOrNull()));
281+
} catch (IllegalArgumentException ex) {
282+
throw new IllegalArgumentException("Invalid container-memory-request: " + ex.getMessage());
283+
}
284+
try {
285+
resourceRequirementsBuilder.addToLimits("memory", parseMemoryQuantity(spec.getMemoryLimit().getValueOrNull()));
286+
} catch (IllegalArgumentException ex) {
287+
throw new IllegalArgumentException("Invalid container-memory-limit: " + ex.getMessage());
288+
}
273289

274290
List<ContainerPort> containerPorts = spec.getPortMapping().stream()
275291
.map(p -> new ContainerPortBuilder().withContainerPort(p.getPort()).build())
@@ -932,4 +948,42 @@ private Optional<Pod> getPod(BackendContainerName podInfo) {
932948
return Optional.ofNullable(kubeClient.pods().inNamespace(podInfo.getNamespace()).withName(podInfo.getName()).get());
933949
}
934950

951+
private Quantity parseCpuQuantity(String value) {
952+
if (value == null) {
953+
return null;
954+
}
955+
// convert upper case suffix automatically to a value accepted by k8s
956+
if (value.endsWith("M")) {
957+
value = value.substring(0, value.length() - 1) + "m";
958+
}
959+
Quantity quantity = new Quantity(value);
960+
quantity.getNumericalAmount(); // validates the quantity
961+
if (!quantity.getFormat().equals("m") && !quantity.getFormat().isEmpty()) {
962+
throw new IllegalArgumentException("Invalid format for CPU resources");
963+
}
964+
return quantity;
965+
}
966+
967+
private Quantity parseMemoryQuantity(String value) {
968+
if (value == null) {
969+
return null;
970+
}
971+
// convert lower case suffixes automatically to a value accepted by k8s
972+
for (String suffix : List.of("p", "t", "g", "m", "k")) {
973+
if (value.endsWith(suffix)) {
974+
value = value.substring(0, value.length() - 1) + suffix.toUpperCase();
975+
break;
976+
} else if (value.endsWith(suffix + "i")) {
977+
value = value.substring(0, value.length() - 2) + suffix.toUpperCase() + "i";
978+
break;
979+
}
980+
}
981+
Quantity quantity = new Quantity(value);
982+
quantity.getNumericalAmount(); // validates the quantity
983+
if (quantity.getFormat().equals("m") || quantity.getFormat().equals("mi") || quantity.getFormat() == "") {
984+
throw new IllegalArgumentException("Invalid format for memory resources");
985+
}
986+
return quantity;
987+
}
988+
935989
}

src/test/java/eu/openanalytics/containerproxy/test/proxy/TestIntegrationOnKube.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import org.junit.jupiter.api.AfterAll;
5353
import org.junit.jupiter.api.Assertions;
5454
import org.junit.jupiter.api.Test;
55+
import org.junit.jupiter.params.ParameterizedTest;
56+
import org.junit.jupiter.params.provider.ValueSource;
5557
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
5658
import org.springframework.security.core.Authentication;
5759
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -233,10 +235,11 @@ public void launchProxyWithSecretRef() {
233235
}
234236
}
235237

236-
@Test
237-
public void launchProxyWithResources() {
238+
@ValueSource(strings = {"01_hello_limits2", "01_hello_limits"})
239+
@ParameterizedTest
240+
public void launchProxyWithResources(String file) {
238241
try (ContainerSetup k8s = new ContainerSetup("kubernetes")) {
239-
String id = inst.client.startProxy("01_hello_limits");
242+
String id = inst.client.startProxy(file);
240243
Proxy proxy = inst.proxyService.getProxy(id);
241244

242245
PodList podList = k8s.client.pods().inNamespace(k8s.namespace).list();

src/test/resources/application-test-kubernetes.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ proxy:
7070
cpu-limit: 2
7171
memory-request: "1Gi"
7272
memory-limit: "2Gi"
73+
- id: 01_hello_limits2
74+
container-specs:
75+
- image: "openanalytics/shinyproxy-integration-test-app"
76+
cmd: [ "R", "-e", "shinyproxy::run_01_hello()" ]
77+
port-mapping:
78+
- name: default
79+
port: 3838
80+
cpu-request: 1000M
81+
cpu-limit: 2000m
82+
memory-request: "1024mi"
83+
memory-limit: "2gi"
7384
- id: 01_hello_priv
7485
container-specs:
7586
- image: "openanalytics/shinyproxy-integration-test-app"

0 commit comments

Comments
 (0)