diff --git a/core/src/main/java/cz/xtf/core/bm/BinaryBuild.java b/core/src/main/java/cz/xtf/core/bm/BinaryBuild.java index e513d9c9..099e44a6 100755 --- a/core/src/main/java/cz/xtf/core/bm/BinaryBuild.java +++ b/core/src/main/java/cz/xtf/core/bm/BinaryBuild.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import cz.xtf.core.config.BuildManagerConfig; import cz.xtf.core.config.WaitingConfig; import cz.xtf.core.openshift.OpenShift; import cz.xtf.core.waiting.SimpleWaiter; @@ -16,6 +17,8 @@ import io.fabric8.kubernetes.api.model.EnvVarBuilder; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.Quantity; +import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder; import io.fabric8.openshift.api.model.Build; import io.fabric8.openshift.api.model.BuildConfig; import io.fabric8.openshift.api.model.BuildConfigBuilder; @@ -36,6 +39,8 @@ public abstract class BinaryBuild implements ManagedBuild { private final String builderImage; private final Map envProperties; + private final String memoryRequest; + private final String memoryLimit; protected final ImageStream is; protected final BuildConfig bc; @@ -43,10 +48,17 @@ public abstract class BinaryBuild implements ManagedBuild { protected String contentHash = null; public BinaryBuild(String builderImage, Path path, Map envProperties, String id) { + this(builderImage, path, envProperties, id, BuildManagerConfig.memoryRequest(), BuildManagerConfig.memoryLimit()); + } + + public BinaryBuild(String builderImage, Path path, Map envProperties, String id, String memoryRequest, + String memoryLimit) { this.builderImage = builderImage; this.path = path; this.envProperties = envProperties; this.id = id; + this.memoryRequest = memoryRequest; + this.memoryLimit = memoryLimit; this.is = this.createIsDefinition(); this.bc = this.createBcDefinition(); @@ -197,6 +209,18 @@ private BuildConfig createBcDefinition() { configureBuildStrategy(bcBuilder, builderImage, envVarList); + // Add resource requirements if specified + if (memoryRequest != null || memoryLimit != null) { + ResourceRequirementsBuilder rrb = new ResourceRequirementsBuilder(); + if (memoryRequest != null) { + rrb.withRequests(Collections.singletonMap("memory", new Quantity(memoryRequest))); + } + if (memoryLimit != null) { + rrb.withLimits(Collections.singletonMap("memory", new Quantity(memoryLimit))); + } + bcBuilder.withResources(rrb.build()); + } + return new BuildConfigBuilder().withMetadata(metadata).withSpec(bcBuilder.build()).build(); } } diff --git a/core/src/main/java/cz/xtf/core/bm/BinaryBuildFromSources.java b/core/src/main/java/cz/xtf/core/bm/BinaryBuildFromSources.java index 63a8b262..b92b88f3 100644 --- a/core/src/main/java/cz/xtf/core/bm/BinaryBuildFromSources.java +++ b/core/src/main/java/cz/xtf/core/bm/BinaryBuildFromSources.java @@ -61,6 +61,11 @@ public BinaryBuildFromSources(String builderImage, Path path, Map envProperties, String id, + String memoryRequest, String memoryLimit) { + super(builderImage, path, envProperties, id, memoryRequest, memoryLimit); + } + @Override public void build(OpenShift openShift) { openShift.imageStreams().create(is); diff --git a/core/src/main/java/cz/xtf/core/config/BuildManagerConfig.java b/core/src/main/java/cz/xtf/core/config/BuildManagerConfig.java index 61fb4355..9e8aa20e 100755 --- a/core/src/main/java/cz/xtf/core/config/BuildManagerConfig.java +++ b/core/src/main/java/cz/xtf/core/config/BuildManagerConfig.java @@ -5,6 +5,8 @@ public class BuildManagerConfig { public static final String FORCE_REBUILD = "xtf.bm.force_rebuild"; public static final String SKIP_REBUILD = "xtf.bm.skip_rebuild"; public static final String MAX_RUNNING_BUILDS = "xtf.bm.max_running_builds"; + public static final String MEMORY_REQUEST = "xtf.bm.memory.request"; + public static final String MEMORY_LIMIT = "xtf.bm.memory.limit"; public static String namespace() { return XTFConfig.get(BUILD_NAMESPACE, "xtf-builds"); @@ -21,4 +23,22 @@ public static boolean skipRebuild() { public static int maxRunningBuilds() { return Integer.valueOf(XTFConfig.get(MAX_RUNNING_BUILDS, "5")); } + + /** + * Memory request for builds (e.g., "2Gi", "512Mi") + * + * @return memory request string or null if not configured + */ + public static String memoryRequest() { + return XTFConfig.get(MEMORY_REQUEST); + } + + /** + * Memory limit for builds (e.g., "4Gi", "1Gi") + * + * @return memory limit string or null if not configured + */ + public static String memoryLimit() { + return XTFConfig.get(MEMORY_LIMIT); + } } diff --git a/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java b/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java index f82c5039..70536f44 100644 --- a/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java +++ b/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java @@ -9,6 +9,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import cz.xtf.core.config.BuildManagerConfig; +import cz.xtf.core.config.XTFConfig; import cz.xtf.core.openshift.OpenShift; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.openshift.api.model.Build; @@ -153,6 +155,150 @@ public void testNeedsUpdate_WhenBuildIsNull_ShouldReturnTrue() { "Missing Build should trigger needsUpdate=true"); } + @Test + public void testBuildConfig_WhenMemoryLimitsSet_ShouldContainResourceRequirements() throws IOException { + // Given: System properties configured for memory request and limit + String memoryRequest = "512Mi"; + String memoryLimit = "2Gi"; + System.setProperty(BuildManagerConfig.MEMORY_REQUEST, memoryRequest); + System.setProperty(BuildManagerConfig.MEMORY_LIMIT, memoryLimit); + XTFConfig.loadConfig(); + + try { + Path tempFileWithLimits = Files.createTempFile("test-with-limits", ".war"); + Files.write(tempFileWithLimits, "test content".getBytes()); + + BinaryBuildFromFile buildWithLimits = new BinaryBuildFromFile( + TEST_BUILDER_IMAGE, + tempFileWithLimits, + null, + TEST_BUILD_ID + "-with-limits"); + + // When: Getting the BuildConfig + BuildConfig buildConfig = buildWithLimits.bc; + + // Then: BuildConfig should contain resource requirements + Assertions.assertNotNull(buildConfig.getSpec().getResources(), + "BuildConfig should have resources set when memory limits are configured"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getRequests(), + "BuildConfig should have resource requests"); + Assertions.assertEquals(memoryRequest, + buildConfig.getSpec().getResources().getRequests().get("memory").toString(), + "Memory request should match configured value"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getLimits(), + "BuildConfig should have resource limits"); + Assertions.assertEquals(memoryLimit, + buildConfig.getSpec().getResources().getLimits().get("memory").toString(), + "Memory limit should match configured value"); + + // Cleanup + Files.delete(tempFileWithLimits); + } finally { + System.clearProperty(BuildManagerConfig.MEMORY_REQUEST); + System.clearProperty(BuildManagerConfig.MEMORY_LIMIT); + XTFConfig.loadConfig(); + } + } + + @Test + public void testBuildConfig_WhenOnlyMemoryRequestSet_ShouldContainOnlyRequest() throws IOException { + // Given: System property configured for memory request only + String memoryRequest = "256Mi"; + System.setProperty(BuildManagerConfig.MEMORY_REQUEST, memoryRequest); + XTFConfig.loadConfig(); + + try { + Path tempFileWithRequest = Files.createTempFile("test-with-request", ".war"); + Files.write(tempFileWithRequest, "test content".getBytes()); + + BinaryBuildFromFile buildWithRequest = new BinaryBuildFromFile( + TEST_BUILDER_IMAGE, + tempFileWithRequest, + null, + TEST_BUILD_ID + "-with-request"); + + // When: Getting the BuildConfig + BuildConfig buildConfig = buildWithRequest.bc; + + // Then: BuildConfig should contain only resource requests + Assertions.assertNotNull(buildConfig.getSpec().getResources(), + "BuildConfig should have resources set when memory request is configured"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getRequests(), + "BuildConfig should have resource requests"); + Assertions.assertEquals(memoryRequest, + buildConfig.getSpec().getResources().getRequests().get("memory").toString(), + "Memory request should match configured value"); + + Assertions.assertTrue(buildConfig.getSpec().getResources().getLimits() == null + || buildConfig.getSpec().getResources().getLimits().isEmpty(), + "BuildConfig should not have limits when only request is set"); + + // Cleanup + Files.delete(tempFileWithRequest); + } finally { + System.clearProperty(BuildManagerConfig.MEMORY_REQUEST); + XTFConfig.loadConfig(); + } + } + + @Test + public void testBuildConfig_WhenOnlyMemoryLimitSet_ShouldContainOnlyLimit() throws IOException { + // Given: System property configured for memory limit only + String memoryLimit = "1Gi"; + + System.setProperty(BuildManagerConfig.MEMORY_LIMIT, memoryLimit); + XTFConfig.loadConfig(); + + try { + Path tempFileWithLimit = Files.createTempFile("test-with-limit", ".war"); + Files.write(tempFileWithLimit, "test content".getBytes()); + + BinaryBuildFromFile buildWithLimit = new BinaryBuildFromFile( + TEST_BUILDER_IMAGE, + tempFileWithLimit, + null, + TEST_BUILD_ID + "-with-limit"); + + // When: Getting the BuildConfig + BuildConfig buildConfig = buildWithLimit.bc; + + // Then: BuildConfig should contain only resource limits + Assertions.assertNotNull(buildConfig.getSpec().getResources(), + "BuildConfig should have resources set when memory limit is configured"); + + Assertions.assertTrue(buildConfig.getSpec().getResources().getRequests() == null + || buildConfig.getSpec().getResources().getRequests().isEmpty(), + "BuildConfig should not have requests when only limit is set"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getLimits(), + "BuildConfig should have resource limits"); + Assertions.assertEquals(memoryLimit, + buildConfig.getSpec().getResources().getLimits().get("memory").toString(), + "Memory limit should match configured value"); + + // Cleanup + Files.delete(tempFileWithLimit); + } finally { + System.clearProperty(BuildManagerConfig.MEMORY_LIMIT); + XTFConfig.loadConfig(); + } + } + + @Test + public void testBuildConfig_WhenMemoryLimitsNotSet_ShouldNotContainResources() { + // Given: BinaryBuild without memory configuration (current setup in @BeforeEach) + + // When: Getting the BuildConfig + BuildConfig buildConfig = binaryBuild.bc; + + // Then: BuildConfig should not contain resource requirements + Assertions.assertNull(buildConfig.getSpec().getResources(), + "BuildConfig should not have resources when memory limits are not configured"); + } + // Helper methods to create test resources private ImageStream createImageStream(String name) {