fix(spur-cli): support ALL in node subcommands - #568
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #568 +/- ##
==========================================
+ Coverage 76.70% 76.79% +0.09%
==========================================
Files 168 168
Lines 68179 68266 +87
==========================================
+ Hits 52295 52423 +128
+ Misses 15884 15843 -41 🚀 New features to boost your workflow:
|
yansun1996
left a comment
There was a problem hiding this comment.
Thanks for this — it lines up cleanly with the scontrol update NodeName=ALL behavior and the empty-cluster handling is a nice touch. A few suggestions before merge:
- Shared resolver.
resolve_node_nameshere is essentially identical to the one inscontrol.rs. Would it be worth making that onepub(crate)and calling it from both, so the two can't drift? (nodelist.rslooked like a candidate home but it's a sync/file-based resolver, so probably not the right fit.) - Help text. The
nodearg help forlabel/drain/removestill lists only comma-lists and hostlist ranges — could we mentionALLthere so it's discoverable in--help? scontrol's docstring already calls it out. - Coverage of the new branch. The two added tests cover
is_all_node_patternand the (unchanged)expand_node_patternpath, but the actual new behavior — theALL->get_nodes-> empty-clusterbail!branch — isn't exercised yet. The in-processmock_controllercould drive this deterministically, thoughget_nodeswould need to be added to it first (it's currently unimplemented there). Worth a follow-up if not this PR. remove ALL. Minor:remove ALL --forcewill deregister every node and evict all jobs with no confirmation. It matches scontrol so this may be intentional — might be worth a one-line note in the PR description either way.
There was a problem hiding this comment.
Pull request overview
This PR updates spur node subcommands to accept the Slurm-compatible ALL keyword (case-insensitive) by resolving it to the set of registered node names via the controller’s GetNodes RPC, aligning behavior with the existing scontrol path.
Changes:
- Switch
spur node label/drain/removefrom pure hostlist expansion to a new async resolver that expands hostlists and resolvesALLviaGetNodes. - Add an explicit error for the empty-cluster case when
ALLis requested. - Add unit tests covering
ALLcase-insensitivity and ensuring hostlist expansion behavior is preserved.
Suppressed comments (3)
crates/spur-cli/src/node.rs:152
cmd_drainnow connects to the controller before validating/expanding non-ALLhostlist patterns. This can mask hostlist parse errors behind connection failures and adds an unnecessary network dependency to argument validation.
async fn cmd_drain(controller: &str, node_pattern: String, reason: Option<String>) -> Result<()> {
let mut client = spur_proto::controller_client(spur_client::connect_channel(controller).await?);
let nodes = resolve_node_names(&mut client, &node_pattern).await?;
crates/spur-cli/src/node.rs:202
cmd_removenow connects to the controller before validating/expanding non-ALLhostlist patterns, which can turn local hostlist errors into connection errors when the controller is unreachable and adds avoidable network work during argument validation.
async fn cmd_remove(
controller: &str,
node_pattern: String,
force: bool,
reason: Option<String>,
) -> Result<()> {
let mut client = spur_proto::controller_client(spur_client::connect_channel(controller).await?);
let nodes = resolve_node_names(&mut client, &node_pattern).await?;
crates/spur-cli/src/node.rs:274
- The new behavior that resolves case-insensitive
ALLviaGetNodes(including the empty-cluster error path) is not covered by tests here. The added unit tests only cover the string predicate and hostlist expansion, so regressions in the RPC-basedALLresolution would go unnoticed.
async fn resolve_node_names(
client: &mut SlurmControllerClient<tonic::transport::Channel>,
pattern: &str,
) -> Result<Vec<String>> {
if is_all_node_pattern(pattern) {
let response = client
.get_nodes(GetNodesRequest {
nodelist: String::new(),
..Default::default()
})
.await
.context("failed to get nodes")?;
let names: Vec<String> = response
.into_inner()
.nodes
.into_iter()
.map(|node| node.name)
.collect();
if names.is_empty() {
bail!("no nodes registered in the cluster");
}
return Ok(names);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f27cacc to
38d3cfa
Compare
|
Thanks for the review — addressed in
|
Reuse scontrol's node resolver so label, drain, and remove handle ALL consistently while malformed hostlists still fail before connecting. Document the expanded scope and cover the controller-backed paths. Signed-off-by: Phlimosx <jixiong@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
38d3cfa to
ad683b5
Compare
|
Hi @01xjw in this PR could you please check #568 (comment) ? we need the test coverage on the newly added |
Summary
spur node label,drain, andremoveresolve case-insensitiveALLto every registered node.scontrol's existing resolver while validating ordinary hostlists before connecting.ALLscope and add focused controller-backed regression coverage.Closes #566.
Behavior note
spur node remove ALL --forceremoves every registered node and may evict their jobs. This matches existingscontrolbehavior and is documented inspur node remove --help.Validation
cargo fmt --all -- --checkcargo clippy --workspace --exclude spur-ffi --all-targets --locked -- -D warningscargo test --workspace --exclude spurd --locked(352spur-clitests and 626 workspace tests passed)Disclosure
This change was prepared with assistance from the radeon-issue automation and independently checked by a separately configured validation model. Maintainer review is still required.