From 33a9369861a4711a6bcdfff13f75337a3b08930f Mon Sep 17 00:00:00 2001 From: Tom Gianos Date: Mon, 22 Feb 2021 21:10:16 -0800 Subject: [PATCH] Add container attributes as possible extension point of Titus Agent Launcher Titus allows certain special key value pairs to be added as container level attributes. This wasn't exposed and some of them are needed. Examples of these are the ability to have bursty network or cpu that exceed the defined resource limits for limited period of time. --- genie-docs/src/docs/asciidoc/_properties.adoc | 5 ++ .../impl/TitusAgentLauncherImpl.java | 8 ++++ .../genie/web/dtos/TitusBatchJobRequest.java | 1 + .../TitusAgentLauncherProperties.java | 11 +++++ .../impl/TitusAgentLauncherImplSpec.groovy | 48 +++++++++++++++++++ .../TitusAgentLauncherPropertiesSpec.groovy | 3 ++ 6 files changed, 76 insertions(+) diff --git a/genie-docs/src/docs/asciidoc/_properties.adoc b/genie-docs/src/docs/asciidoc/_properties.adoc index 21b5e5b9790..2e034695329 100644 --- a/genie-docs/src/docs/asciidoc/_properties.adoc +++ b/genie-docs/src/docs/asciidoc/_properties.adoc @@ -182,6 +182,11 @@ https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-featu |default |yes +|genie.agent.launcher.titus.container-attributes +|Map attributes to send to Titus specific to the container +|empty +|yes + |genie.agent.launcher.titus.enabled |Whether the Titus agent launcher is active or not |false diff --git a/genie-web/src/main/java/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImpl.java b/genie-web/src/main/java/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImpl.java index e150d001f2e..ce58910855d 100644 --- a/genie-web/src/main/java/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImpl.java +++ b/genie-web/src/main/java/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImpl.java @@ -307,11 +307,19 @@ private TitusBatchJobRequest createJobRequest(final ResolvedJob resolvedJob) { ) ), entryPoint, + // Environment Variables this.binder .bind( TitusAgentLauncherProperties.ADDITIONAL_ENVIRONMENT_PROPERTY, Bindable.mapOf(String.class, String.class) ) + .orElse(new HashMap<>()), + // Container Attributes + this.binder + .bind( + TitusAgentLauncherProperties.CONTAINER_ATTRIBUTES_PROPERTY, + Bindable.mapOf(String.class, String.class) + ) .orElse(new HashMap<>()) ), new TitusBatchJobRequest.Batch( diff --git a/genie-web/src/main/java/com/netflix/genie/web/dtos/TitusBatchJobRequest.java b/genie-web/src/main/java/com/netflix/genie/web/dtos/TitusBatchJobRequest.java index da8da52474d..6240af2bf91 100644 --- a/genie-web/src/main/java/com/netflix/genie/web/dtos/TitusBatchJobRequest.java +++ b/genie-web/src/main/java/com/netflix/genie/web/dtos/TitusBatchJobRequest.java @@ -73,6 +73,7 @@ public static class Container { private final Image image; private final List entryPoint; private final Map env; + private final Map attributes; } /** diff --git a/genie-web/src/main/java/com/netflix/genie/web/properties/TitusAgentLauncherProperties.java b/genie-web/src/main/java/com/netflix/genie/web/properties/TitusAgentLauncherProperties.java index 42052db538d..90d145e0f80 100644 --- a/genie-web/src/main/java/com/netflix/genie/web/properties/TitusAgentLauncherProperties.java +++ b/genie-web/src/main/java/com/netflix/genie/web/properties/TitusAgentLauncherProperties.java @@ -89,6 +89,11 @@ public class TitusAgentLauncherProperties { */ public static final String CAPACITY_GROUP_PROPERTY = PREFIX + ".capacityGroup"; + /** + * Any attributes that should be added to the request specifically for the container. + */ + public static final String CONTAINER_ATTRIBUTES_PROPERTY = PREFIX + ".container-attributes"; + /** * Name of the property that enables {@link TitusAgentLauncherImpl}. */ @@ -328,4 +333,10 @@ public class TitusAgentLauncherProperties { */ @NotNull private DataSize minimumMemory = DataSize.ofGigabytes(4); + + /** + * A map of container attributes. + */ + @NotNull + private Map containerAttributes = new HashMap<>(); } diff --git a/genie-web/src/test/groovy/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImplSpec.groovy b/genie-web/src/test/groovy/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImplSpec.groovy index 816d2b69d51..78494123549 100644 --- a/genie-web/src/test/groovy/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImplSpec.groovy +++ b/genie-web/src/test/groovy/com/netflix/genie/web/agent/launchers/impl/TitusAgentLauncherImplSpec.groovy @@ -150,6 +150,7 @@ class TitusAgentLauncherImplSpec extends Specification { "--server-port", launcherProperties.getGenieServerPort().toString() ] requestCapture.getContainer().getEnv() == launcherProperties.getAdditionalEnvironment() + requestCapture.getContainer().getAttributes() == this.launcherProperties.getContainerAttributes() requestCapture.getBatch().getSize() == 1 requestCapture.getBatch().getRetryPolicy().getImmediate().getRetries() == launcherProperties.getRetries() requestCapture.getBatch().getRuntimeLimitSec() == launcherProperties.getRuntimeLimit().getSeconds() @@ -539,4 +540,51 @@ class TitusAgentLauncherImplSpec extends Specification { requestCapture.getContainer().getEnv().get(prop1Key) == prop1Value requestCapture.getContainer().getEnv().get(prop2Key) == prop2Value } + + def "Check container attributes resolution"() { + TitusBatchJobResponse response = toTitusResponse("{ \"id\" : \"" + TITUS_JOB_ID + "\" }") + TitusBatchJobRequest requestCapture + + when: + Optional launcherExt = this.launcher.launchAgent(this.resolvedJob, null) + + then: + 1 * this.restTemplate.postForObject(TITUS_ENDPOINT, _ as TitusBatchJobRequest, TitusBatchJobResponse.class) >> { + args -> + requestCapture = args[1] as TitusBatchJobRequest + return response + } + 1 * this.cache.put(JOB_ID, TITUS_JOB_ID) + launcherExt.isPresent() + requestCapture != null + requestCapture.getContainer().getAttributes().isEmpty() + + when: + def prop1Key = "${UUID.randomUUID()}.${UUID.randomUUID()}.${UUID.randomUUID()}".toString() + def prop1Value = UUID.randomUUID().toString() + def prop2Key = "${UUID.randomUUID()}-${UUID.randomUUID()}".toString() + def prop2Value = UUID.randomUUID().toString() + this.environment.withProperty( + "${TitusAgentLauncherProperties.CONTAINER_ATTRIBUTES_PROPERTY}.${prop1Key}", + prop1Value + ) + this.environment.withProperty( + "${TitusAgentLauncherProperties.CONTAINER_ATTRIBUTES_PROPERTY}.${prop2Key}", + prop2Value + ) + launcherExt = this.launcher.launchAgent(this.resolvedJob, null) + + then: + 1 * this.restTemplate.postForObject(TITUS_ENDPOINT, _ as TitusBatchJobRequest, TitusBatchJobResponse.class) >> { + args -> + requestCapture = args[1] as TitusBatchJobRequest + return response + } + 1 * this.cache.put(JOB_ID, TITUS_JOB_ID) + launcherExt.isPresent() + requestCapture != null + requestCapture.getContainer().getAttributes().size() == 2 + requestCapture.getContainer().getAttributes().get(prop1Key) == prop1Value + requestCapture.getContainer().getAttributes().get(prop2Key) == prop2Value + } } diff --git a/genie-web/src/test/groovy/com/netflix/genie/web/properties/TitusAgentLauncherPropertiesSpec.groovy b/genie-web/src/test/groovy/com/netflix/genie/web/properties/TitusAgentLauncherPropertiesSpec.groovy index 51af2ca112f..fa7629f07ae 100644 --- a/genie-web/src/test/groovy/com/netflix/genie/web/properties/TitusAgentLauncherPropertiesSpec.groovy +++ b/genie-web/src/test/groovy/com/netflix/genie/web/properties/TitusAgentLauncherPropertiesSpec.groovy @@ -65,6 +65,7 @@ class TitusAgentLauncherPropertiesSpec extends Specification { p.getMinimumDiskSize().toGigabytes() == 10 p.getMinimumMemory().toGigabytes() == 4 p.getMinimumGPU() == 0 + p.getContainerAttributes() == [:] when: p.setEnabled(true) @@ -103,6 +104,7 @@ class TitusAgentLauncherPropertiesSpec extends Specification { p.setMinimumDiskSize(DataSize.ofGigabytes(20)) p.setMinimumMemory(DataSize.ofGigabytes(8)) p.setMinimumGPU(1) + p.setContainerAttributes(["hi": "bye"]) then: p.isEnabled() @@ -142,5 +144,6 @@ class TitusAgentLauncherPropertiesSpec extends Specification { p.getMinimumDiskSize().toGigabytes() == 20 p.getMinimumMemory().toGigabytes() == 8 p.getMinimumGPU() == 1 + p.getContainerAttributes() == ["hi": "bye"] } }