Add REST accessor for cluster message/state constraints - #217
Add REST accessor for cluster message/state constraints#217LZD-PratyushBhatt wants to merge 1 commit into
Conversation
Operators previously had to hand-edit the CONSTRAINT ZNodes
(/{cluster}/CONFIGS/CONSTRAINT/{type}) to add a message constraint,
which is error prone during an incident. This adds a ConstraintAccessor
exposing CRUD over individual constraint items:
GET /clusters/{clusterId}/constraints/{constraintType}
GET /clusters/{clusterId}/constraints/{constraintType}/{constraintId}
PUT /clusters/{clusterId}/constraints/{constraintType}/{constraintId}
DELETE /clusters/{clusterId}/constraints/{constraintType}/{constraintId}
PUT accepts a flat JSON map of constraint attributes and delegates to
HelixAdmin.setConstraint, validating the constraint type, attribute keys,
and CONSTRAINT_VALUE the same way the core loader does. The accessor is
auto-registered via the existing package scan. Adds TestConstraintAccessor
covering create/get/delete, multiple items of the same type, and the
invalid-type, missing-cluster, and invalid-body error paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| @Path("{constraintType}/{constraintId}") | ||
| @ApiOperation(value = "Create or overwrite a constraint item", | ||
| notes = "Helix REST Constraints Put API") | ||
| public Response setConstraint(@PathParam("clusterId") String clusterId, |
There was a problem hiding this comment.
we should add validation here on the MESSAGE_TYPE, STATE_TRANSITION and CONSTRAINT_VALUE fields to prevent users from adding wrong values.... for instance constraint_value >=0... basically the idea is the body of the message to be set on ZNode should be prevalidated
| public Response setConstraint(@PathParam("clusterId") String clusterId, | ||
| @PathParam("constraintType") String constraintTypeStr, | ||
| @PathParam("constraintId") String constraintId, String content) { | ||
| if (!doesClusterExist(clusterId)) { |
There was a problem hiding this comment.
One more thing we should think about is if we can also provide support for a batch API, suppose I want to add constraints for multiple instances in one go, with the current structure, I'll have to run the API again and again, can we do an atomic batch operation instead and support batch API instead?
|
Add a sample helix-rest curl in the PR description for this operation and lets update the helix-rest documentation as well for this... i'll moving it to helix-docs soon |
There was a problem hiding this comment.
Attribute values are regex-matched, not literal — this affects the prevalidation you asked for. Constraint matching uses messageValue.matches(constraintValue) in ConstraintItem.match, i.e. the stored attribute value is used as the regular expression argument of String.matches. So INSTANCE/RESOURCE/TRANSITION/STATE values are treated as patterns, not literals — a literal instance name like lva2-app58760.prod.linkedin.com_15088 happens to match itself, but the .s are wildcards and any regex metacharacter in a name would misbehave. Any value validation should therefore either escape/anchor these, or explicitly document that values are patterns.
| } | ||
|
|
||
| ConstraintItemBuilder builder = new ConstraintItemBuilder(); | ||
| builder.addConstraintAttributes(attributes); |
There was a problem hiding this comment.
This call (and builder.build() below) is outside any try/catch, so a syntactically-valid body with a null value returns HTTP 500 instead of 400.
Example: {"MESSAGE_TYPE":"STATE_TRANSITION","CONSTRAINT_VALUE":null} → Jackson yields a non-empty map {…, CONSTRAINT_VALUE=null} (so it passes the isEmpty() guard above) → ConstraintValue.valueOf(null) throws NullPointerException. That's not an IllegalArgumentException, so ConstraintItemBuilder's internal catch doesn't cover it, the NPE escapes this unguarded call, and Jersey maps it to 500. Verified end-to-end.
Suggest guarding it so bad input is a clean 400:
ConstraintItem item;
try {
builder.addConstraintAttributes(attributes);
item = builder.build();
} catch (RuntimeException e) {
return badRequest("Invalid constraint attributes: " + e.getMessage());
}This also avoids writing null-valued attributes into the ZNode (a non-CONSTRAINT_VALUE key with a null value doesn't throw, but currently gets stored). Please add a regression test (CONSTRAINT_VALUE:null → 400) to lock it in.
Operators previously had to hand-edit the CONSTRAINT ZNodes
(/{cluster}/CONFIGS/CONSTRAINT/{type})to add a message constraint, which is error prone during an incident. This adds a ConstraintAccessor exposing CRUD over individual constraint items:PUT accepts a flat JSON map of constraint attributes and delegates to HelixAdmin.setConstraint, validating the constraint type, attribute keys, and CONSTRAINT_VALUE the same way the core loader does. The accessor is auto-registered via the existing package scan. Adds TestConstraintAccessor covering create/get/delete, multiple items of the same type, and the invalid-type, missing-cluster, and invalid-body error paths.
Issues
(#200 - Link your issue number here: You can write "Fixes #XXX". Please use the proper keyword so that the issue gets closed automatically. See https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue
Any of the following keywords can be used: close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved)
Description
(Write a concise description including what, why, how)
Tests
(List the names of added unit/integration tests)
(If CI test fails due to known issue, please specify the issue and test PR locally. Then copy & paste the result of "mvn test" to here.)
Changes that Break Backward Compatibility (Optional)
(Consider including all behavior changes for public methods or API. Also include these changes in merge description so that other developers are aware of these changes. This allows them to make relevant code changes in feature branches accounting for the new method/API behavior.)
Documentation (Optional)
(Link the GitHub wiki you added)
Commits
Code Quality
(helix-style-intellij.xml if IntelliJ IDE is used)