This is an automated email from the ASF dual-hosted git repository. czy006 pushed a commit to branch 0.9.x in repository https://gitbox.apache.org/repos/asf/amoro.git
commit 52616a2add93e7f7b60c6b66b1b418b8e65f47d3 Author: Sivakumar Mahalingam <[email protected]> AuthorDate: Thu Oct 8 09:36:52 2026 +0300 fix(ams): reject non-positive optimizer parallelism (#4398) Co-authored-by: Xu Bai <[email protected]> (cherry picked from commit 13364ef2f2aab6e2b897ce848bdaeca0ee5b0de4) --- .../dashboard/controller/OptimizerController.java | 4 ++ .../controller/OptimizerGroupController.java | 4 ++ .../amoro/server/TestOptimizerController.java | 65 ++++++++++++++++++++++ .../amoro/server/TestOptimizerGroupController.java | 19 +++++++ 4 files changed, 92 insertions(+) diff --git a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerController.java b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerController.java index 071f7c58b..4476fe175 100644 --- a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerController.java +++ b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerController.java @@ -18,6 +18,7 @@ package org.apache.amoro.server.dashboard.controller; +import io.javalin.http.BadRequestResponse; import io.javalin.http.Context; import org.apache.amoro.resource.Resource; import org.apache.amoro.resource.ResourceContainer; @@ -78,6 +79,9 @@ public class OptimizerController { public void createOptimizer(Context ctx) { Map<String, Object> map = ctx.bodyAsClass(Map.class); int parallelism = Integer.parseInt(map.get("parallelism").toString()); + if (parallelism <= 0) { + throw new BadRequestResponse("Parallelism must be greater than 0."); + } String optimizerGroup = map.get("optimizerGroup").toString(); ResourceGroup resourceGroup = optimizerManager.getResourceGroup(optimizerGroup); Resource resource = diff --git a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java index 586891d27..4e005f86c 100644 --- a/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java +++ b/amoro-ams/src/main/java/org/apache/amoro/server/dashboard/controller/OptimizerGroupController.java @@ -18,6 +18,7 @@ package org.apache.amoro.server.dashboard.controller; +import io.javalin.http.BadRequestResponse; import io.javalin.http.Context; import org.apache.amoro.resource.Resource; import org.apache.amoro.resource.ResourceContainer; @@ -208,6 +209,9 @@ public class OptimizerGroupController { String optimizerGroup = ctx.pathParam("optimizerGroup"); Map<String, Integer> map = ctx.bodyAsClass(Map.class); int parallelism = map.get("parallelism"); + if (parallelism <= 0) { + throw new BadRequestResponse("Parallelism must be greater than 0."); + } ResourceGroup resourceGroup = optimizerManager.getResourceGroup(optimizerGroup); Resource resource = diff --git a/amoro-ams/src/test/java/org/apache/amoro/server/TestOptimizerController.java b/amoro-ams/src/test/java/org/apache/amoro/server/TestOptimizerController.java new file mode 100644 index 000000000..81f9a504c --- /dev/null +++ b/amoro-ams/src/test/java/org/apache/amoro/server/TestOptimizerController.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.amoro.server; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import io.javalin.http.BadRequestResponse; +import io.javalin.http.Context; +import org.apache.amoro.server.dashboard.controller.OptimizerController; +import org.apache.amoro.server.resource.OptimizerManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.HashMap; +import java.util.Map; + +public class TestOptimizerController { + + private OptimizerManager optimizerManager; + private OptimizerController controller; + private Context ctx; + + @BeforeEach + void setUp() { + optimizerManager = mock(OptimizerManager.class); + controller = new OptimizerController(optimizerManager); + ctx = mock(Context.class); + } + + @ParameterizedTest + @ValueSource(ints = {0, -1}) + void createOptimizerWithInvalidParallelism(int parallelism) { + Map<String, Object> requestBody = new HashMap<>(); + requestBody.put("parallelism", parallelism); + requestBody.put("optimizerGroup", "group1"); + + when(ctx.bodyAsClass(Map.class)).thenReturn(requestBody); + + BadRequestResponse exception = + assertThrows(BadRequestResponse.class, () -> controller.createOptimizer(ctx)); + assertEquals(400, exception.getStatus()); + verifyNoInteractions(optimizerManager); + } +} diff --git a/amoro-ams/src/test/java/org/apache/amoro/server/TestOptimizerGroupController.java b/amoro-ams/src/test/java/org/apache/amoro/server/TestOptimizerGroupController.java index a4dfa4290..7fddb1ea2 100644 --- a/amoro-ams/src/test/java/org/apache/amoro/server/TestOptimizerGroupController.java +++ b/amoro-ams/src/test/java/org/apache/amoro/server/TestOptimizerGroupController.java @@ -18,12 +18,15 @@ package org.apache.amoro.server; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import io.javalin.http.BadRequestResponse; import io.javalin.http.Context; import org.apache.amoro.OptimizerProperties; import org.apache.amoro.resource.ResourceGroup; @@ -34,6 +37,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import javax.ws.rs.BadRequestException; @@ -83,6 +87,21 @@ public class TestOptimizerGroupController { assertThrows(BadRequestException.class, () -> controller.createResourceGroup(ctx)); } + @ParameterizedTest + @ValueSource(ints = {0, -1}) + void scaleOutOptimizerWithInvalidParallelism(int parallelism) { + Map<String, Integer> requestBody = new HashMap<>(); + requestBody.put("parallelism", parallelism); + + when(ctx.pathParam("optimizerGroup")).thenReturn("group1"); + when(ctx.bodyAsClass(Map.class)).thenReturn(requestBody); + + BadRequestResponse exception = + assertThrows(BadRequestResponse.class, () -> controller.scaleOutOptimizer(ctx)); + assertEquals(400, exception.getStatus()); + verifyNoInteractions(optimizerManager); + } + private Map<String, Object> groupRequest(String name, Map<String, String> properties) { Map<String, Object> requestBody = new HashMap<>(); requestBody.put("name", name);
