Skip to content

fix(admin): initialise V1Scale spec before setting replicas (#7698) - #7710

Merged
Aias00 merged 1 commit into
apache:masterfrom
konlue:fix/kubernetes-scaler-scale-spec
Oct 17, 2026
Merged

Aias00 merged 1 commit into
apache:masterfrom
konlue:fix/kubernetes-scaler-scale-spec

Conversation

@konlue

@konlue konlue commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Fixes #7698.

Summary

KubernetesScaler#scaleByNum constructs a bare V1Scale and dereferences its spec with Objects.requireNonNull. A fresh V1Scale has a null spec, so every replica update throws NPE before reaching the Kubernetes API — the fixed and time-window scaling policies and the dynamic scaleByAction are all dead on arrival. The NPE also escapes the surrounding catch (ApiException).

Modifications

  • Initialise the spec explicitly: scale.setSpec(new V1ScaleSpec().replicas(replicaCount)) instead of requireNonNull(scale.getSpec()).setReplicas(...). V1ScaleSpec was already imported in the file.
  • Add KubernetesScalerTest, the scale module's first unit test, with a Mockito-mocked AppsV1Api:
    • testScaleByNumSetsSpecBeforeReplace captures the scale passed to replaceNamespacedDeploymentScale and asserts the spec is non-null with the requested replicas.
    • testScaleByActionReadsCurrentAndAppliesDelta mocks readNamespacedDeploymentScale (replicas=2) and asserts scaleByAction(SCALE_UP) replaces with replicas=3.

Verifying this change

  • Red-green: with the original requireNonNull line restored, both new tests fail with NullPointerException at KubernetesScaler.scaleByNum (matching the stack trace in the issue); with the fix they pass (2/2).

  • Targeted tests pass:

    ./mvnw -B -ntp -pl shenyu-admin test -Dtest=KubernetesScalerTest

    2/2 green.

  • Checkstyle passes:

    ./mvnw -B -ntp -pl shenyu-admin checkstyle:check

    clean.

Notes

The replace call's other arguments (pretty / dryRun / fieldManager / fieldValidation) stay null, unchanged. No behaviour change beyond making the call actually reach the API server.

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Fixes #7698. KubernetesScaler#scaleByNum constructed a bare new V1Scale() and immediately dereferenced its spec via Objects.requireNonNull(scale.getSpec()).setReplicas(...). Because a freshly built V1Scale has a null spec, every replica update threw an NPE before the Kubernetes API call — the fixed / time-window scaling policies and the dynamic scaleByAction path were all dead on arrival. The NPE also escaped the surrounding catch (ApiException).

What the change does

  • shenyu-admin/.../scale/scaler/KubernetesScaler.java:73: replaces Objects.requireNonNull(scale.getSpec()).setReplicas(replicaCount) with scale.setSpec(new V1ScaleSpec().replicas(replicaCount)), so a non-null spec carrying the requested replica count reaches replaceNamespacedDeploymentScale. V1ScaleSpec was already imported, and Objects remains used in scaleByAction.
  • Adds the scale module's first unit test KubernetesScalerTest.java with a Mockito-mocked AppsV1Api: testScaleByNumSetsSpecBeforeReplace asserts the captured V1Scale passed to the replace call has a non-null spec with the requested replicas; testScaleByActionReadsCurrentAndAppliesDelta mocks readNamespacedDeploymentScale (replicas=2) and asserts scaleByAction(SCALE_UP) replaces with replicas=3.

Verification

  • Master comparison: master's scaleByNum used the requireNonNull(scale.getSpec()) form (as shown in the diff); head (56c0892f97c35722e5bdc99eca64339da6fe6895) uses setSpec(new V1ScaleSpec()...). V1ScaleSpec import confirmed present in the current head file; the constructor signature KubernetesScaler(Optional<AppsV1Api>, DeploymentProperties) matches the test's new KubernetesScaler(Optional.of(appsV1Api), deploymentProperties).
  • Red-green is credible from the code: new V1Scale().getSpec() is null by construction, so the old line throws before the API call; the new line is a straightforward, faithful replacement with no behavioural change beyond reaching the API server.
  • CI: all relevant checks pass on this head — pr_build, Analyze (java), check-license-header, rat_check, scope, sql-matrix, registry_dependencies, all build (shenyu-integrated-test-*), agent_gateway_migrations, and the e2e aggregator. No reds to attribute.

Notes

  • Non-blocking: the scale module previously had no tests; adding a Mockito-based unit test here is a welcome first step. Test class uses @MockitoSettings(strictness = Strictness.LENIENT) and isNull() matchers for the trailing pretty/dryRun/fieldManager/fieldValidation args, which are correct given they are passed as null.
  • No change to the other replace-call arguments; no impact on unrelated modules.

Conclusion

APPROVE. Correct, minimal fix for a real NPE that disabled the Kubernetes scaling policy paths, backed by a targeted unit test and fully green CI.

@Aias00
Aias00 merged commit d867e8d into apache:master Oct 17, 2026
54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] KubernetesScaler dereferences an uninitialized V1Scale spec before every replica update

2 participants