From 848e8af40d7cc6694c81fa06f082be51802c6e81 Mon Sep 17 00:00:00 2001 From: GWphua Date: Wed, 11 Feb 2026 18:36:52 +0800 Subject: [PATCH 01/10] Dynamic config retrieval logic. Clean up code --- ...aultKubernetesTaskRunnerDynamicConfig.java | 5 +- ...KubernetesTaskExecutionConfigResource.java | 15 ++++- ...rnetesTaskExecutionConfigResourceTest.java | 64 ++++++++++++++++--- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java index fde4dcc7b0bf..9db184258431 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java +++ b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java @@ -35,8 +35,8 @@ public class DefaultKubernetesTaskRunnerDynamicConfig implements KubernetesTaskR @JsonCreator public DefaultKubernetesTaskRunnerDynamicConfig( - @JsonProperty("podTemplateSelectStrategy") PodTemplateSelectStrategy podTemplateSelectStrategy, - @JsonProperty("capacity") Integer capacity + @Nullable @JsonProperty("podTemplateSelectStrategy") PodTemplateSelectStrategy podTemplateSelectStrategy, + @Nullable @JsonProperty("capacity") Integer capacity ) { this.podTemplateSelectStrategy = podTemplateSelectStrategy; @@ -52,6 +52,7 @@ public PodTemplateSelectStrategy getPodTemplateSelectStrategy() @Override @JsonProperty + @Nullable public Integer getCapacity() { return capacity; diff --git a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java index 432a41933ede..2a56774a0ccf 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java +++ b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java @@ -27,6 +27,7 @@ import org.apache.druid.common.config.JacksonConfigManager; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.logger.Logger; +import org.apache.druid.k8s.overlord.KubernetesTaskRunnerStaticConfig; import org.apache.druid.server.http.security.ConfigResourceFilter; import org.apache.druid.server.security.AuthorizationUtils; import org.joda.time.Interval; @@ -57,16 +58,19 @@ public class KubernetesTaskExecutionConfigResource private static final Logger log = new Logger(KubernetesTaskExecutionConfigResource.class); private final JacksonConfigManager configManager; private final AuditManager auditManager; + private final KubernetesTaskRunnerStaticConfig staticConfig; private AtomicReference dynamicConfigRef = null; @Inject public KubernetesTaskExecutionConfigResource( final JacksonConfigManager configManager, - final AuditManager auditManager + final AuditManager auditManager, + final KubernetesTaskRunnerStaticConfig staticConfig ) { this.configManager = configManager; this.auditManager = auditManager; + this.staticConfig = staticConfig; } /** @@ -160,7 +164,14 @@ public Response getExecutionConfig() private KubernetesTaskRunnerDynamicConfig getDynamicConfig() { if (dynamicConfigRef == null) { - dynamicConfigRef = configManager.watch(KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, KubernetesTaskRunnerDynamicConfig.class); + dynamicConfigRef = configManager.watch( + KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, + KubernetesTaskRunnerDynamicConfig.class, + new DefaultKubernetesTaskRunnerDynamicConfig( + KubernetesTaskRunnerDynamicConfig.DEFAULT_STRATEGY, + staticConfig.getCapacity() + ) + ); } return dynamicConfigRef.get(); } diff --git a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java index c06056f01133..2ff48d7d599f 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java +++ b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java @@ -22,6 +22,7 @@ import org.apache.druid.audit.AuditManager; import org.apache.druid.common.config.ConfigManager; import org.apache.druid.common.config.JacksonConfigManager; +import org.apache.druid.k8s.overlord.KubernetesTaskRunnerStaticConfig; import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.easymock.EasyMock; @@ -33,6 +34,7 @@ import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class KubernetesTaskExecutionConfigResourceTest { @@ -40,6 +42,13 @@ public class KubernetesTaskExecutionConfigResourceTest private AuditManager auditManager; private HttpServletRequest req; private KubernetesTaskRunnerDynamicConfig dynamicConfig; + private KubernetesTaskRunnerStaticConfig staticConfig; + + private static final KubernetesTaskRunnerDynamicConfig DEFAULT_DYNAMIC_CONFIG = + new DefaultKubernetesTaskRunnerDynamicConfig( + KubernetesTaskRunnerDynamicConfig.DEFAULT_STRATEGY, + Integer.MAX_VALUE + ); @Before public void setUp() @@ -48,6 +57,7 @@ public void setUp() auditManager = EasyMock.createMock(AuditManager.class); req = EasyMock.createMock(HttpServletRequest.class); dynamicConfig = EasyMock.createMock(KubernetesTaskRunnerDynamicConfig.class); + staticConfig = new KubernetesTaskRunnerStaticConfig(); } @Test @@ -55,11 +65,13 @@ public void setExecutionConfigSuccessfulUpdate() { EasyMock.expect(configManager.watch( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class + KubernetesTaskRunnerDynamicConfig.class, + DEFAULT_DYNAMIC_CONFIG )).andReturn(new AtomicReference<>(null)); KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, - auditManager + auditManager, + staticConfig ); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_AUTHOR)).andReturn(null).anyTimes(); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_COMMENT)).andReturn(null).anyTimes(); @@ -82,11 +94,13 @@ public void setExecutionConfigFailedUpdate() { EasyMock.expect(configManager.watch( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class + KubernetesTaskRunnerDynamicConfig.class, + DEFAULT_DYNAMIC_CONFIG )).andReturn(new AtomicReference<>(null)); KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, - auditManager + auditManager, + staticConfig ); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_AUTHOR)).andReturn(null).anyTimes(); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_COMMENT)).andReturn(null).anyTimes(); @@ -109,14 +123,16 @@ public void setExecutionConfig_MergeUsesCurrentCapacityWhenRequestCapacityNull() { KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, - auditManager + auditManager, + staticConfig ); PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); KubernetesTaskRunnerDynamicConfig currentConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 5); EasyMock.expect(configManager.watch( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class + KubernetesTaskRunnerDynamicConfig.class, + DEFAULT_DYNAMIC_CONFIG )).andReturn(new AtomicReference<>(currentConfig)); PodTemplateSelectStrategy requestStrategy = new TaskTypePodTemplateSelectStrategy(); @@ -147,14 +163,16 @@ public void setExecutionConfig_MergeUsesCurrentStrategyWhenRequestStrategyNull() { KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, - auditManager + auditManager, + staticConfig ); PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); KubernetesTaskRunnerDynamicConfig currentConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 2); EasyMock.expect(configManager.watch( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class + KubernetesTaskRunnerDynamicConfig.class, + DEFAULT_DYNAMIC_CONFIG )).andReturn(new AtomicReference<>(currentConfig)); KubernetesTaskRunnerDynamicConfig requestConfig = new DefaultKubernetesTaskRunnerDynamicConfig(null, 7); @@ -184,14 +202,16 @@ public void setExecutionConfig_MergeUsesCurrentWhenBothRequestFieldsNull() { KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, - auditManager + auditManager, + staticConfig ); PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); KubernetesTaskRunnerDynamicConfig currentConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 9); EasyMock.expect(configManager.watch( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class + KubernetesTaskRunnerDynamicConfig.class, + DEFAULT_DYNAMIC_CONFIG )).andReturn(new AtomicReference<>(currentConfig)); KubernetesTaskRunnerDynamicConfig requestConfig = new DefaultKubernetesTaskRunnerDynamicConfig(null, null); @@ -215,4 +235,28 @@ public void setExecutionConfig_MergeUsesCurrentWhenBothRequestFieldsNull() Response result = testedResource.setExecutionConfig(requestConfig, req); assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); } + + @Test + public void getExecutionConfig_ReturnsDefaultWhenNoConfigSet() + { + EasyMock.expect(configManager.watch( + KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, + KubernetesTaskRunnerDynamicConfig.class, + DEFAULT_DYNAMIC_CONFIG + )).andReturn(new AtomicReference<>(DEFAULT_DYNAMIC_CONFIG)); + EasyMock.replay(configManager, auditManager); + + KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( + configManager, + auditManager, + staticConfig + ); + + Response result = testedResource.getExecutionConfig(); + assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); + + KubernetesTaskRunnerDynamicConfig returnedConfig = (KubernetesTaskRunnerDynamicConfig) result.getEntity(); + assertNotNull(returnedConfig); + assertEquals(DEFAULT_DYNAMIC_CONFIG, returnedConfig); + } } From 54f6ed2ffbb70d85896a70431101217ea5bad2a6 Mon Sep 17 00:00:00 2001 From: GWphua Date: Thu, 12 Feb 2026 11:14:46 +0800 Subject: [PATCH 02/10] Effective strategy to be returned by API --- .../KubernetesTaskRunnerEffectiveConfig.java | 1 + ...KubernetesTaskExecutionConfigResource.java | 36 ++--- ...rnetesTaskExecutionConfigResourceTest.java | 123 +++++++++--------- 3 files changed, 76 insertions(+), 84 deletions(-) diff --git a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java index 8343ebe15879..21c5b674f152 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java +++ b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java @@ -19,6 +19,7 @@ package org.apache.druid.k8s.overlord; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Supplier; import org.apache.druid.k8s.overlord.execution.KubernetesTaskRunnerDynamicConfig; import org.apache.druid.k8s.overlord.execution.PodTemplateSelectStrategy; diff --git a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java index 2a56774a0ccf..d8e16d1dd2b0 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java +++ b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResource.java @@ -27,7 +27,7 @@ import org.apache.druid.common.config.JacksonConfigManager; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.java.util.common.logger.Logger; -import org.apache.druid.k8s.overlord.KubernetesTaskRunnerStaticConfig; +import org.apache.druid.k8s.overlord.KubernetesTaskRunnerEffectiveConfig; import org.apache.druid.server.http.security.ConfigResourceFilter; import org.apache.druid.server.security.AuthorizationUtils; import org.joda.time.Interval; @@ -44,7 +44,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; /** * Resource that manages Kubernetes-specific execution configurations for running tasks. @@ -58,19 +57,18 @@ public class KubernetesTaskExecutionConfigResource private static final Logger log = new Logger(KubernetesTaskExecutionConfigResource.class); private final JacksonConfigManager configManager; private final AuditManager auditManager; - private final KubernetesTaskRunnerStaticConfig staticConfig; - private AtomicReference dynamicConfigRef = null; + private final KubernetesTaskRunnerEffectiveConfig effectiveConfig; @Inject public KubernetesTaskExecutionConfigResource( final JacksonConfigManager configManager, final AuditManager auditManager, - final KubernetesTaskRunnerStaticConfig staticConfig + final KubernetesTaskRunnerEffectiveConfig effectiveConfig ) { this.configManager = configManager; this.auditManager = auditManager; - this.staticConfig = staticConfig; + this.effectiveConfig = effectiveConfig; } /** @@ -88,12 +86,9 @@ public Response setExecutionConfig( @Context final HttpServletRequest req ) { - KubernetesTaskRunnerDynamicConfig currentConfig = getDynamicConfig(); - KubernetesTaskRunnerDynamicConfig mergedConfig = dynamicConfig; + KubernetesTaskRunnerDynamicConfig currentConfig = getCurrentConfiguration(); + KubernetesTaskRunnerDynamicConfig mergedConfig = currentConfig.merge(dynamicConfig); - if (currentConfig != null) { - mergedConfig = currentConfig.merge(dynamicConfig); - } final ConfigManager.SetResult setResult = configManager.set( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, mergedConfig, @@ -158,21 +153,14 @@ public Response getExecutionConfigHistory( @ResourceFilters(ConfigResourceFilter.class) public Response getExecutionConfig() { - return Response.ok(getDynamicConfig()).build(); + return Response.ok(getCurrentConfiguration()).build(); } - private KubernetesTaskRunnerDynamicConfig getDynamicConfig() + private KubernetesTaskRunnerDynamicConfig getCurrentConfiguration() { - if (dynamicConfigRef == null) { - dynamicConfigRef = configManager.watch( - KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class, - new DefaultKubernetesTaskRunnerDynamicConfig( - KubernetesTaskRunnerDynamicConfig.DEFAULT_STRATEGY, - staticConfig.getCapacity() - ) - ); - } - return dynamicConfigRef.get(); + return new DefaultKubernetesTaskRunnerDynamicConfig( + effectiveConfig.getPodTemplateSelectStrategy(), + effectiveConfig.getCapacity() + ); } } diff --git a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java index 2ff48d7d599f..cc5bc3a9a7d2 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java +++ b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java @@ -19,9 +19,11 @@ package org.apache.druid.k8s.overlord.execution; +import com.google.common.base.Suppliers; import org.apache.druid.audit.AuditManager; import org.apache.druid.common.config.ConfigManager; import org.apache.druid.common.config.JacksonConfigManager; +import org.apache.druid.k8s.overlord.KubernetesTaskRunnerEffectiveConfig; import org.apache.druid.k8s.overlord.KubernetesTaskRunnerStaticConfig; import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; @@ -31,7 +33,6 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; -import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -41,13 +42,14 @@ public class KubernetesTaskExecutionConfigResourceTest private JacksonConfigManager configManager; private AuditManager auditManager; private HttpServletRequest req; - private KubernetesTaskRunnerDynamicConfig dynamicConfig; - private KubernetesTaskRunnerStaticConfig staticConfig; - + private static final KubernetesTaskRunnerEffectiveConfig DEFAULT_CONFIG = new KubernetesTaskRunnerEffectiveConfig( + new KubernetesTaskRunnerStaticConfig(), + null + ); private static final KubernetesTaskRunnerDynamicConfig DEFAULT_DYNAMIC_CONFIG = new DefaultKubernetesTaskRunnerDynamicConfig( - KubernetesTaskRunnerDynamicConfig.DEFAULT_STRATEGY, - Integer.MAX_VALUE + DEFAULT_CONFIG.getPodTemplateSelectStrategy(), + DEFAULT_CONFIG.getCapacity() ); @Before @@ -56,23 +58,25 @@ public void setUp() configManager = EasyMock.createMock(JacksonConfigManager.class); auditManager = EasyMock.createMock(AuditManager.class); req = EasyMock.createMock(HttpServletRequest.class); - dynamicConfig = EasyMock.createMock(KubernetesTaskRunnerDynamicConfig.class); - staticConfig = new KubernetesTaskRunnerStaticConfig(); } @Test public void setExecutionConfigSuccessfulUpdate() { - EasyMock.expect(configManager.watch( - KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class, - DEFAULT_DYNAMIC_CONFIG - )).andReturn(new AtomicReference<>(null)); KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, auditManager, - staticConfig + DEFAULT_CONFIG ); + + KubernetesTaskRunnerDynamicConfig inputConfig = new DefaultKubernetesTaskRunnerDynamicConfig( + new TaskTypePodTemplateSelectStrategy(), 10 + ); + + KubernetesTaskRunnerDynamicConfig expectedMerged = new DefaultKubernetesTaskRunnerDynamicConfig( + new TaskTypePodTemplateSelectStrategy(), 10 + ); + EasyMock.expect(req.getHeader(AuditManager.X_DRUID_AUTHOR)).andReturn(null).anyTimes(); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_COMMENT)).andReturn(null).anyTimes(); EasyMock.expect(req.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)).andReturn(null).anyTimes(); @@ -80,28 +84,32 @@ public void setExecutionConfigSuccessfulUpdate() EasyMock.replay(req); EasyMock.expect(configManager.set( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - dynamicConfig, + expectedMerged, AuthorizationUtils.buildAuditInfo(req) )).andReturn(ConfigManager.SetResult.ok()); - EasyMock.replay(configManager, auditManager, dynamicConfig); + EasyMock.replay(configManager, auditManager); - Response result = testedResource.setExecutionConfig(dynamicConfig, req); + Response result = testedResource.setExecutionConfig(inputConfig, req); assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); } @Test public void setExecutionConfigFailedUpdate() { - EasyMock.expect(configManager.watch( - KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class, - DEFAULT_DYNAMIC_CONFIG - )).andReturn(new AtomicReference<>(null)); KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, auditManager, - staticConfig + DEFAULT_CONFIG ); + + KubernetesTaskRunnerDynamicConfig inputConfig = new DefaultKubernetesTaskRunnerDynamicConfig( + new TaskTypePodTemplateSelectStrategy(), 10 + ); + + KubernetesTaskRunnerDynamicConfig expectedMerged = new DefaultKubernetesTaskRunnerDynamicConfig( + new TaskTypePodTemplateSelectStrategy(), 10 + ); + EasyMock.expect(req.getHeader(AuditManager.X_DRUID_AUTHOR)).andReturn(null).anyTimes(); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_COMMENT)).andReturn(null).anyTimes(); EasyMock.expect(req.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)).andReturn(null).anyTimes(); @@ -109,35 +117,35 @@ public void setExecutionConfigFailedUpdate() EasyMock.replay(req); EasyMock.expect(configManager.set( KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - dynamicConfig, + expectedMerged, AuthorizationUtils.buildAuditInfo(req) - )).andReturn(ConfigManager.SetResult.failure(new RuntimeException())); - EasyMock.replay(configManager, auditManager, dynamicConfig); + )).andReturn(ConfigManager.SetResult.fail(new RuntimeException(), false)); + EasyMock.replay(configManager, auditManager); - Response result = testedResource.setExecutionConfig(dynamicConfig, req); + Response result = testedResource.setExecutionConfig(inputConfig, req); assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), result.getStatus()); } @Test public void setExecutionConfig_MergeUsesCurrentCapacityWhenRequestCapacityNull() { + PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); + KubernetesTaskRunnerDynamicConfig currentDynamic = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 5); + KubernetesTaskRunnerEffectiveConfig effectiveConfig = new KubernetesTaskRunnerEffectiveConfig( + new KubernetesTaskRunnerStaticConfig(), + Suppliers.ofInstance(currentDynamic) + ); + KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, auditManager, - staticConfig + effectiveConfig ); - PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); - KubernetesTaskRunnerDynamicConfig currentConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 5); - EasyMock.expect(configManager.watch( - KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class, - DEFAULT_DYNAMIC_CONFIG - )).andReturn(new AtomicReference<>(currentConfig)); - PodTemplateSelectStrategy requestStrategy = new TaskTypePodTemplateSelectStrategy(); KubernetesTaskRunnerDynamicConfig requestConfig = new DefaultKubernetesTaskRunnerDynamicConfig(requestStrategy, null); + // Effective current: (TaskType, 5). Request: (TaskType, null). Merged: (TaskType, 5). KubernetesTaskRunnerDynamicConfig expectedMergedConfig = new DefaultKubernetesTaskRunnerDynamicConfig(requestStrategy, 5); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_AUTHOR)).andReturn(null).anyTimes(); @@ -161,22 +169,22 @@ public void setExecutionConfig_MergeUsesCurrentCapacityWhenRequestCapacityNull() @Test public void setExecutionConfig_MergeUsesCurrentStrategyWhenRequestStrategyNull() { + PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); + KubernetesTaskRunnerDynamicConfig currentDynamic = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 2); + KubernetesTaskRunnerEffectiveConfig effectiveConfig = new KubernetesTaskRunnerEffectiveConfig( + new KubernetesTaskRunnerStaticConfig(), + Suppliers.ofInstance(currentDynamic) + ); + KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, auditManager, - staticConfig + effectiveConfig ); - PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); - KubernetesTaskRunnerDynamicConfig currentConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 2); - EasyMock.expect(configManager.watch( - KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class, - DEFAULT_DYNAMIC_CONFIG - )).andReturn(new AtomicReference<>(currentConfig)); - KubernetesTaskRunnerDynamicConfig requestConfig = new DefaultKubernetesTaskRunnerDynamicConfig(null, 7); + // Effective current: (TaskType, 2). Request: (null, 7). Merged: (TaskType, 7). KubernetesTaskRunnerDynamicConfig expectedMergedConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 7); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_AUTHOR)).andReturn(null).anyTimes(); @@ -200,22 +208,22 @@ public void setExecutionConfig_MergeUsesCurrentStrategyWhenRequestStrategyNull() @Test public void setExecutionConfig_MergeUsesCurrentWhenBothRequestFieldsNull() { + PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); + KubernetesTaskRunnerDynamicConfig currentDynamic = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 9); + KubernetesTaskRunnerEffectiveConfig effectiveConfig = new KubernetesTaskRunnerEffectiveConfig( + new KubernetesTaskRunnerStaticConfig(), + Suppliers.ofInstance(currentDynamic) + ); + KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, auditManager, - staticConfig + effectiveConfig ); - PodTemplateSelectStrategy currentStrategy = new TaskTypePodTemplateSelectStrategy(); - KubernetesTaskRunnerDynamicConfig currentConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 9); - EasyMock.expect(configManager.watch( - KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class, - DEFAULT_DYNAMIC_CONFIG - )).andReturn(new AtomicReference<>(currentConfig)); - KubernetesTaskRunnerDynamicConfig requestConfig = new DefaultKubernetesTaskRunnerDynamicConfig(null, null); + // Effective current: (TaskType, 9). Request: (null, null). Merged: (TaskType, 9). KubernetesTaskRunnerDynamicConfig expectedMergedConfig = new DefaultKubernetesTaskRunnerDynamicConfig(currentStrategy, 9); EasyMock.expect(req.getHeader(AuditManager.X_DRUID_AUTHOR)).andReturn(null).anyTimes(); @@ -239,17 +247,12 @@ public void setExecutionConfig_MergeUsesCurrentWhenBothRequestFieldsNull() @Test public void getExecutionConfig_ReturnsDefaultWhenNoConfigSet() { - EasyMock.expect(configManager.watch( - KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, - KubernetesTaskRunnerDynamicConfig.class, - DEFAULT_DYNAMIC_CONFIG - )).andReturn(new AtomicReference<>(DEFAULT_DYNAMIC_CONFIG)); EasyMock.replay(configManager, auditManager); KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( configManager, auditManager, - staticConfig + DEFAULT_CONFIG ); Response result = testedResource.getExecutionConfig(); From 3ec764823b1d63d01761f3724d3491c802894332 Mon Sep 17 00:00:00 2001 From: GWphua Date: Thu, 12 Feb 2026 11:29:11 +0800 Subject: [PATCH 03/10] Test for effective resource --- .../KubernetesTaskRunnerEffectiveConfig.java | 1 - ...rnetesTaskExecutionConfigResourceTest.java | 114 ++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java index 21c5b674f152..8343ebe15879 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java +++ b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java @@ -19,7 +19,6 @@ package org.apache.druid.k8s.overlord; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Supplier; import org.apache.druid.k8s.overlord.execution.KubernetesTaskRunnerDynamicConfig; import org.apache.druid.k8s.overlord.execution.PodTemplateSelectStrategy; diff --git a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java index cc5bc3a9a7d2..6ce95e5f056c 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java +++ b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java @@ -20,19 +20,27 @@ package org.apache.druid.k8s.overlord.execution; import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import org.apache.druid.audit.AuditEntry; +import org.apache.druid.audit.AuditInfo; import org.apache.druid.audit.AuditManager; import org.apache.druid.common.config.ConfigManager; import org.apache.druid.common.config.JacksonConfigManager; +import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.java.util.common.Intervals; import org.apache.druid.k8s.overlord.KubernetesTaskRunnerEffectiveConfig; import org.apache.druid.k8s.overlord.KubernetesTaskRunnerStaticConfig; import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.easymock.EasyMock; +import org.joda.time.Interval; import org.junit.Before; import org.junit.Test; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; +import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -262,4 +270,110 @@ public void getExecutionConfig_ReturnsDefaultWhenNoConfigSet() assertNotNull(returnedConfig); assertEquals(DEFAULT_DYNAMIC_CONFIG, returnedConfig); } + + @Test + @SuppressWarnings("unchecked") + public void getExecutionConfigHistory_SanityCheck() + { + AuditInfo admin = new AuditInfo("admin", "initial setup", "10.0.0.1"); + AuditInfo operator = new AuditInfo("operator", "scaled up capacity", "10.0.0.2"); + AuditInfo paranoidUser = new AuditInfo("paranoid-user", "rollback to safe config", "10.0.0.3"); + + String configKey = KubernetesTaskRunnerDynamicConfig.CONFIG_KEY; + + AuditEntry entry1 = new AuditEntry( + configKey, configKey, admin, + "{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":5}", + DateTimes.of("2024-06-01T10:00:00Z") + ); + AuditEntry entry2 = new AuditEntry( + configKey, configKey, operator, + "{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":20}", + DateTimes.of("2024-09-15T14:30:00Z") + ); + AuditEntry entry3 = new AuditEntry( + configKey, configKey, paranoidUser, + "{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":10}", + DateTimes.of("2024-11-20T08:00:00Z") + ); + + List fullHistory = ImmutableList.of(entry3, entry2, entry1); + List lastTwo = ImmutableList.of(entry3, entry2); + String intervalStr = "2024-06-01/2024-10-01"; + Interval interval = Intervals.of(intervalStr); + List intervalFiltered = ImmutableList.of(entry2, entry1); + + // Query by count: returns the last 2 entries + auditManager = EasyMock.createMock(AuditManager.class); + EasyMock.expect(auditManager.fetchAuditHistory(configKey, configKey, 2)).andReturn(lastTwo); + EasyMock.replay(configManager, auditManager); + + KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( + configManager, auditManager, DEFAULT_CONFIG + ); + Response result = testedResource.getExecutionConfigHistory(null, 2); + assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); + List resultEntries = (List) result.getEntity(); + assertEquals(2, resultEntries.size()); + assertEquals(lastTwo, resultEntries); + EasyMock.verify(auditManager); + + // Query by interval: returns entries within the interval + EasyMock.reset(configManager, auditManager); + EasyMock.expect(auditManager.fetchAuditHistory(configKey, configKey, interval)).andReturn(intervalFiltered); + EasyMock.replay(configManager, auditManager); + + testedResource = new KubernetesTaskExecutionConfigResource( + configManager, auditManager, DEFAULT_CONFIG + ); + result = testedResource.getExecutionConfigHistory(intervalStr, null); + assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); + resultEntries = (List) result.getEntity(); + assertEquals(2, resultEntries.size()); + assertEquals(intervalFiltered, resultEntries); + EasyMock.verify(auditManager); + + // Both interval and count provided: interval takes precedence + EasyMock.reset(configManager, auditManager); + EasyMock.expect(auditManager.fetchAuditHistory(configKey, configKey, interval)).andReturn(intervalFiltered); + EasyMock.replay(configManager, auditManager); + + testedResource = new KubernetesTaskExecutionConfigResource( + configManager, auditManager, DEFAULT_CONFIG + ); + result = testedResource.getExecutionConfigHistory(intervalStr, 99); + assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); + assertEquals(intervalFiltered, result.getEntity()); + EasyMock.verify(auditManager); + + // Neither interval nor count: falls through to interval-based fetch with null + EasyMock.reset(configManager, auditManager); + EasyMock.expect(auditManager.fetchAuditHistory(configKey, configKey, null)).andReturn(fullHistory); + EasyMock.replay(configManager, auditManager); + + testedResource = new KubernetesTaskExecutionConfigResource( + configManager, auditManager, DEFAULT_CONFIG + ); + result = testedResource.getExecutionConfigHistory(null, null); + assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); + resultEntries = (List) result.getEntity(); + assertEquals(3, resultEntries.size()); + assertEquals(fullHistory, resultEntries); + EasyMock.verify(auditManager); + + // Invalid count: returns BAD_REQUEST with an error message + EasyMock.reset(configManager, auditManager); + EasyMock.expect(auditManager.fetchAuditHistory(configKey, configKey, -1)) + .andThrow(new IllegalArgumentException("count must be positive")); + EasyMock.replay(configManager, auditManager); + + testedResource = new KubernetesTaskExecutionConfigResource( + configManager, auditManager, DEFAULT_CONFIG + ); + result = testedResource.getExecutionConfigHistory(null, -1); + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), result.getStatus()); + Map errorEntity = (Map) result.getEntity(); + assertEquals("count must be positive", errorEntity.get("error")); + EasyMock.verify(auditManager); + } } From ad04374f83aeac14959187d2c30960abc611b31d Mon Sep 17 00:00:00 2001 From: GWphua Date: Thu, 12 Feb 2026 11:44:03 +0800 Subject: [PATCH 04/10] Test for effective resource + cleanup --- ...aultKubernetesTaskRunnerDynamicConfig.java | 7 +- ...rnetesTaskExecutionConfigResourceTest.java | 64 ++++++++++++------- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java index 9db184258431..e804ddd06c35 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java +++ b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java @@ -35,14 +35,15 @@ public class DefaultKubernetesTaskRunnerDynamicConfig implements KubernetesTaskR @JsonCreator public DefaultKubernetesTaskRunnerDynamicConfig( - @Nullable @JsonProperty("podTemplateSelectStrategy") PodTemplateSelectStrategy podTemplateSelectStrategy, - @Nullable @JsonProperty("capacity") Integer capacity + @JsonProperty("podTemplateSelectStrategy") PodTemplateSelectStrategy podTemplateSelectStrategy, + @JsonProperty("capacity") Integer capacity ) { this.podTemplateSelectStrategy = podTemplateSelectStrategy; this.capacity = capacity; } + @Nullable @Override @JsonProperty public PodTemplateSelectStrategy getPodTemplateSelectStrategy() @@ -50,9 +51,9 @@ public PodTemplateSelectStrategy getPodTemplateSelectStrategy() return podTemplateSelectStrategy; } + @Nullable @Override @JsonProperty - @Nullable public Integer getCapacity() { return capacity; diff --git a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java index 6ce95e5f056c..6f572effa4ec 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java +++ b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/execution/KubernetesTaskExecutionConfigResourceTest.java @@ -127,7 +127,7 @@ public void setExecutionConfigFailedUpdate() KubernetesTaskRunnerDynamicConfig.CONFIG_KEY, expectedMerged, AuthorizationUtils.buildAuditInfo(req) - )).andReturn(ConfigManager.SetResult.fail(new RuntimeException(), false)); + )).andReturn(ConfigManager.SetResult.failure(new RuntimeException())); EasyMock.replay(configManager, auditManager); Response result = testedResource.setExecutionConfig(inputConfig, req); @@ -275,27 +275,33 @@ public void getExecutionConfig_ReturnsDefaultWhenNoConfigSet() @SuppressWarnings("unchecked") public void getExecutionConfigHistory_SanityCheck() { - AuditInfo admin = new AuditInfo("admin", "initial setup", "10.0.0.1"); - AuditInfo operator = new AuditInfo("operator", "scaled up capacity", "10.0.0.2"); - AuditInfo paranoidUser = new AuditInfo("paranoid-user", "rollback to safe config", "10.0.0.3"); + AuditInfo admin = new AuditInfo("admin", "crewmate", "initial setup", "10.0.0.1"); + AuditInfo operator = new AuditInfo("operator", "imposter", "scaled up capacity", "10.0.0.2"); + AuditInfo paranoidUser = new AuditInfo("paranoid-user", "crewmate", "rollback to safe config", "10.0.0.3"); String configKey = KubernetesTaskRunnerDynamicConfig.CONFIG_KEY; - AuditEntry entry1 = new AuditEntry( - configKey, configKey, admin, - "{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":5}", - DateTimes.of("2024-06-01T10:00:00Z") - ); - AuditEntry entry2 = new AuditEntry( - configKey, configKey, operator, - "{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":20}", - DateTimes.of("2024-09-15T14:30:00Z") - ); - AuditEntry entry3 = new AuditEntry( - configKey, configKey, paranoidUser, - "{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":10}", - DateTimes.of("2024-11-20T08:00:00Z") - ); + AuditEntry entry1 = AuditEntry.builder() + .key(configKey) + .type(configKey) + .auditInfo(admin) + .serializedPayload("{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":5}") + .auditTime(DateTimes.of("2024-06-01T10:00:00Z")) + .build(); + AuditEntry entry2 = AuditEntry.builder() + .key(configKey) + .type(configKey) + .auditInfo(operator) + .serializedPayload("{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":20}") + .auditTime(DateTimes.of("2024-09-15T14:30:00Z")) + .build(); + AuditEntry entry3 = AuditEntry.builder() + .key(configKey) + .type(configKey) + .auditInfo(paranoidUser) + .serializedPayload("{\"type\":\"default\",\"podTemplateSelectStrategy\":{\"type\":\"taskType\"},\"capacity\":10}") + .auditTime(DateTimes.of("2024-11-20T08:00:00Z")) + .build(); List fullHistory = ImmutableList.of(entry3, entry2, entry1); List lastTwo = ImmutableList.of(entry3, entry2); @@ -309,7 +315,9 @@ public void getExecutionConfigHistory_SanityCheck() EasyMock.replay(configManager, auditManager); KubernetesTaskExecutionConfigResource testedResource = new KubernetesTaskExecutionConfigResource( - configManager, auditManager, DEFAULT_CONFIG + configManager, + auditManager, + DEFAULT_CONFIG ); Response result = testedResource.getExecutionConfigHistory(null, 2); assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); @@ -324,7 +332,9 @@ public void getExecutionConfigHistory_SanityCheck() EasyMock.replay(configManager, auditManager); testedResource = new KubernetesTaskExecutionConfigResource( - configManager, auditManager, DEFAULT_CONFIG + configManager, + auditManager, + DEFAULT_CONFIG ); result = testedResource.getExecutionConfigHistory(intervalStr, null); assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); @@ -339,7 +349,9 @@ public void getExecutionConfigHistory_SanityCheck() EasyMock.replay(configManager, auditManager); testedResource = new KubernetesTaskExecutionConfigResource( - configManager, auditManager, DEFAULT_CONFIG + configManager, + auditManager, + DEFAULT_CONFIG ); result = testedResource.getExecutionConfigHistory(intervalStr, 99); assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); @@ -352,7 +364,9 @@ public void getExecutionConfigHistory_SanityCheck() EasyMock.replay(configManager, auditManager); testedResource = new KubernetesTaskExecutionConfigResource( - configManager, auditManager, DEFAULT_CONFIG + configManager, + auditManager, + DEFAULT_CONFIG ); result = testedResource.getExecutionConfigHistory(null, null); assertEquals(Response.Status.OK.getStatusCode(), result.getStatus()); @@ -368,7 +382,9 @@ public void getExecutionConfigHistory_SanityCheck() EasyMock.replay(configManager, auditManager); testedResource = new KubernetesTaskExecutionConfigResource( - configManager, auditManager, DEFAULT_CONFIG + configManager, + auditManager, + DEFAULT_CONFIG ); result = testedResource.getExecutionConfigHistory(null, -1); assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), result.getStatus()); From 5a41512bc7669e2401fcb34662be44047d1245a8 Mon Sep 17 00:00:00 2001 From: GWphua Date: Thu, 12 Feb 2026 11:49:07 +0800 Subject: [PATCH 05/10] Nullable parameters in jsoncreator --- .../execution/DefaultKubernetesTaskRunnerDynamicConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java index e804ddd06c35..cb6ea848fffb 100644 --- a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java +++ b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/execution/DefaultKubernetesTaskRunnerDynamicConfig.java @@ -35,7 +35,9 @@ public class DefaultKubernetesTaskRunnerDynamicConfig implements KubernetesTaskR @JsonCreator public DefaultKubernetesTaskRunnerDynamicConfig( + @Nullable @JsonProperty("podTemplateSelectStrategy") PodTemplateSelectStrategy podTemplateSelectStrategy, + @Nullable @JsonProperty("capacity") Integer capacity ) { From 0256a5e2e9a415c1c63f51f263ac9a90cf8da7a4 Mon Sep 17 00:00:00 2001 From: GWphua Date: Fri, 27 Feb 2026 16:53:39 +0800 Subject: [PATCH 06/10] Docs --- docs/development/extensions-core/k8s-jobs.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/development/extensions-core/k8s-jobs.md b/docs/development/extensions-core/k8s-jobs.md index a9c32370d609..88156d84ad2c 100644 --- a/docs/development/extensions-core/k8s-jobs.md +++ b/docs/development/extensions-core/k8s-jobs.md @@ -85,7 +85,7 @@ To use these APIs, ensure you have read and write permissions for the CONFIG res #### Get dynamic configuration -Retrieves the current dynamic execution config for the Kubernetes task runner. +Retrieves the current execution config for the Kubernetes task runner. The Overlord will be using the values here to create tasks. Returns a JSON object with the dynamic configuration properties. ##### URL @@ -98,7 +98,6 @@ Returns a JSON object with the dynamic configuration properties. - *Successfully retrieved dynamic configuration* From 49e013d790a797ad9aac4ad872d6215ab31ea2b6 Mon Sep 17 00:00:00 2001 From: GWphua Date: Fri, 27 Feb 2026 16:58:34 +0800 Subject: [PATCH 07/10] Docs --- docs/development/extensions-core/k8s-jobs.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/development/extensions-core/k8s-jobs.md b/docs/development/extensions-core/k8s-jobs.md index 88156d84ad2c..916136bb23c1 100644 --- a/docs/development/extensions-core/k8s-jobs.md +++ b/docs/development/extensions-core/k8s-jobs.md @@ -85,7 +85,7 @@ To use these APIs, ensure you have read and write permissions for the CONFIG res #### Get dynamic configuration -Retrieves the current execution config for the Kubernetes task runner. The Overlord will be using the values here to create tasks. +Retrieves the current execution config used by the Kubernetes task runner. Returns a JSON object with the dynamic configuration properties. ##### URL @@ -156,6 +156,9 @@ Host: http://ROUTER_IP:ROUTER_PORT ``` +> [!NOTE] +> Prior to Druid v37, this API will return an empty value when the dynamic config has not been updated via the POST method below. This has since changed to always reflect the dynamic config that will be used by the task runner to create K8s jobs. + #### Update dynamic configuration Updates the dynamic configuration for the Kubernetes Task Runner From f2a0b3402ab013e83ccfc54191cea3e4405f923e Mon Sep 17 00:00:00 2001 From: GWphua Date: Fri, 27 Feb 2026 16:59:36 +0800 Subject: [PATCH 08/10] Docs --- docs/development/extensions-core/k8s-jobs.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/development/extensions-core/k8s-jobs.md b/docs/development/extensions-core/k8s-jobs.md index 916136bb23c1..bd268963b050 100644 --- a/docs/development/extensions-core/k8s-jobs.md +++ b/docs/development/extensions-core/k8s-jobs.md @@ -85,6 +85,9 @@ To use these APIs, ensure you have read and write permissions for the CONFIG res #### Get dynamic configuration +> [!NOTE] +> Prior to Druid v37, this API will return an empty value when the dynamic config has not been updated via the POST method below. This has since changed to always reflect the dynamic config that will be used by the task runner to create K8s jobs. + Retrieves the current execution config used by the Kubernetes task runner. Returns a JSON object with the dynamic configuration properties. @@ -98,6 +101,7 @@ Returns a JSON object with the dynamic configuration properties. + *Successfully retrieved dynamic configuration* @@ -156,9 +160,6 @@ Host: http://ROUTER_IP:ROUTER_PORT ``` -> [!NOTE] -> Prior to Druid v37, this API will return an empty value when the dynamic config has not been updated via the POST method below. This has since changed to always reflect the dynamic config that will be used by the task runner to create K8s jobs. - #### Update dynamic configuration Updates the dynamic configuration for the Kubernetes Task Runner From 043b252f5f1176596d205c4d746f66efbaeaf654 Mon Sep 17 00:00:00 2001 From: GWphua Date: Fri, 27 Feb 2026 17:04:37 +0800 Subject: [PATCH 09/10] Docs --- docs/development/extensions-core/k8s-jobs.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/development/extensions-core/k8s-jobs.md b/docs/development/extensions-core/k8s-jobs.md index bd268963b050..55a87dd742bb 100644 --- a/docs/development/extensions-core/k8s-jobs.md +++ b/docs/development/extensions-core/k8s-jobs.md @@ -85,10 +85,9 @@ To use these APIs, ensure you have read and write permissions for the CONFIG res #### Get dynamic configuration -> [!NOTE] > Prior to Druid v37, this API will return an empty value when the dynamic config has not been updated via the POST method below. This has since changed to always reflect the dynamic config that will be used by the task runner to create K8s jobs. -Retrieves the current execution config used by the Kubernetes task runner. +Retrieve the current execution config used by the Kubernetes task runner. Returns a JSON object with the dynamic configuration properties. ##### URL From 8eae02a3f6091cbce928ffca148aa9417c2022b9 Mon Sep 17 00:00:00 2001 From: GWphua Date: Fri, 27 Feb 2026 17:22:12 +0800 Subject: [PATCH 10/10] Spelling check --- docs/development/extensions-core/k8s-jobs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/extensions-core/k8s-jobs.md b/docs/development/extensions-core/k8s-jobs.md index 55a87dd742bb..d9c9b85ed579 100644 --- a/docs/development/extensions-core/k8s-jobs.md +++ b/docs/development/extensions-core/k8s-jobs.md @@ -85,7 +85,7 @@ To use these APIs, ensure you have read and write permissions for the CONFIG res #### Get dynamic configuration -> Prior to Druid v37, this API will return an empty value when the dynamic config has not been updated via the POST method below. This has since changed to always reflect the dynamic config that will be used by the task runner to create K8s jobs. +> Prior to Druid 37.0.0, this API will return an empty value when the dynamic config has not been updated via the POST method below. This has since changed to always reflect the dynamic config that will be used by the task runner to create K8s jobs. Retrieve the current execution config used by the Kubernetes task runner. Returns a JSON object with the dynamic configuration properties.