Public dry run dryrun-529dbe43b301
This commit is contained in:
parent
16cb76e6e2
commit
bef088c34c
3 changed files with 205 additions and 27 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"kind": "disasmer-filtered-public-tree",
|
"kind": "disasmer-filtered-public-tree",
|
||||||
"source_commit": "22f391eb394afc47797118ce3a8b98cc36ba2a45",
|
"source_commit": "529dbe43b301d0436dbb4739b399daf4709b89ea",
|
||||||
"release_name": "dryrun-22f391eb394a",
|
"release_name": "dryrun-529dbe43b301",
|
||||||
"filtered_out": [
|
"filtered_out": [
|
||||||
"private/**",
|
"private/**",
|
||||||
"experiments/**",
|
"experiments/**",
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ use anyhow::{Context, Result};
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use disasmer_core::{
|
use disasmer_core::{
|
||||||
BrowserLoginFlow, BundleIdentityInputs, BundleMetadata, Capability, CliLoginFlow, Digest,
|
BrowserLoginFlow, BundleIdentityInputs, BundleMetadata, Capability, CliLoginFlow, Digest,
|
||||||
NodeCapabilities, ProjectModel, SelectedInput, SourceProviderKind, SourceProviderManifest,
|
LimitKind, NodeCapabilities, ProjectModel, ResourceLimits, SelectedInput, SourceProviderKind,
|
||||||
|
SourceProviderManifest,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
@ -1290,18 +1291,30 @@ fn debug_attach_report(args: DebugAttachArgs) -> Result<Value> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quota_status_report(args: QuotaStatusArgs) -> Result<Value> {
|
fn quota_status_report(args: QuotaStatusArgs, cwd: PathBuf) -> Result<Value> {
|
||||||
|
let config = read_project_config(&cwd)?;
|
||||||
|
let effective_scope = effective_project_scope(&args.scope, config.as_ref());
|
||||||
|
let coordinator = effective_scope.coordinator.clone();
|
||||||
|
let attached_nodes =
|
||||||
|
list_attached_nodes_if_available(coordinator.as_deref(), &effective_scope)?;
|
||||||
|
let task_events =
|
||||||
|
list_task_events_if_available(coordinator.as_deref(), &effective_scope, None)?;
|
||||||
|
let current_usage = quota_current_usage(&attached_nodes, task_events.as_ref());
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"command": "quota status",
|
"command": "quota status",
|
||||||
"tenant": args.scope.tenant,
|
"tenant": effective_scope.tenant,
|
||||||
"project": args.scope.project,
|
"project": effective_scope.project,
|
||||||
"coordinator": args.scope.coordinator,
|
"user": effective_scope.user,
|
||||||
|
"coordinator": coordinator,
|
||||||
|
"project_config": config,
|
||||||
"policy_surface": "generic public quota categories; hosted tuning remains private policy",
|
"policy_surface": "generic public quota categories; hosted tuning remains private policy",
|
||||||
"limits": {
|
"limits": quota_limits_value(),
|
||||||
"artifact_download_bytes": "community-tier policy before dispatch",
|
"current_usage": current_usage,
|
||||||
"rendezvous_attempts": "community-tier policy before dispatch",
|
"attached_nodes": attached_nodes,
|
||||||
"hosted_wasm": "fuel/memory/wall-clock/state/logs/metadata/ui/api limits before start"
|
"task_events": task_events,
|
||||||
},
|
"next_blocked_action": quota_next_blocked_action(¤t_usage),
|
||||||
|
"community_tier_language": true,
|
||||||
|
"billing_required": false,
|
||||||
"private_abuse_heuristics_exposed": false,
|
"private_abuse_heuristics_exposed": false,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
@ -1469,6 +1482,17 @@ fn human_report(value: &Value) -> String {
|
||||||
if let Some(current_usage) = value.pointer("/quota_posture/current_usage") {
|
if let Some(current_usage) = value.pointer("/quota_posture/current_usage") {
|
||||||
lines.push(format!("quota usage: {}", compact_json(current_usage)));
|
lines.push(format!("quota usage: {}", compact_json(current_usage)));
|
||||||
}
|
}
|
||||||
|
if let Some(current_usage) = value.get("current_usage") {
|
||||||
|
lines.push(format!("quota usage: {}", compact_json(current_usage)));
|
||||||
|
}
|
||||||
|
if let Some(next_blocked_action) = value.get("next_blocked_action") {
|
||||||
|
if !next_blocked_action.is_null() {
|
||||||
|
lines.push(format!(
|
||||||
|
"next blocked action: {}",
|
||||||
|
compact_json(next_blocked_action)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(flow) = value.get("human_flow") {
|
if let Some(flow) = value.get("human_flow") {
|
||||||
if let Some(device) = flow.get("Device") {
|
if let Some(device) = flow.get("Device") {
|
||||||
lines.push("flow: device".to_owned());
|
lines.push("flow: device".to_owned());
|
||||||
|
|
@ -1839,7 +1863,10 @@ fn main() -> Result<()> {
|
||||||
command: QuotaCommands::Status(args),
|
command: QuotaCommands::Status(args),
|
||||||
} => {
|
} => {
|
||||||
let json_output = args.scope.json;
|
let json_output = args.scope.json;
|
||||||
emit_report("a_status_report(args)?, json_output)?;
|
emit_report(
|
||||||
|
"a_status_report(args, std::env::current_dir()?)?,
|
||||||
|
json_output,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Commands::Admin { command } => {
|
Commands::Admin { command } => {
|
||||||
let (report, json_output) = match command {
|
let (report, json_output) = match command {
|
||||||
|
|
@ -2131,6 +2158,19 @@ fn task_event_count(task_events: Option<&Value>) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_quota_posture(attached_nodes: &Value, task_events: Option<&Value>) -> Value {
|
fn project_quota_posture(attached_nodes: &Value, task_events: Option<&Value>) -> Value {
|
||||||
|
let current_usage = quota_current_usage(attached_nodes, task_events);
|
||||||
|
let next_blocked_action = quota_next_blocked_action(¤t_usage);
|
||||||
|
json!({
|
||||||
|
"source": "cli_project_status_summary",
|
||||||
|
"current_usage": current_usage,
|
||||||
|
"limits": quota_limits_value(),
|
||||||
|
"next_blocked_action": next_blocked_action,
|
||||||
|
"billing_required": false,
|
||||||
|
"private_abuse_heuristics_exposed": false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quota_current_usage(attached_nodes: &Value, task_events: Option<&Value>) -> Value {
|
||||||
let attached_node_count = attached_nodes
|
let attached_node_count = attached_nodes
|
||||||
.get("count")
|
.get("count")
|
||||||
.and_then(Value::as_u64)
|
.and_then(Value::as_u64)
|
||||||
|
|
@ -2140,23 +2180,51 @@ fn project_quota_posture(attached_nodes: &Value, task_events: Option<&Value>) ->
|
||||||
.and_then(Value::as_u64)
|
.and_then(Value::as_u64)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
json!({
|
json!({
|
||||||
"source": "cli_project_status_summary",
|
"attached_nodes": attached_node_count,
|
||||||
"current_usage": {
|
"online_nodes": online_node_count,
|
||||||
"attached_nodes": attached_node_count,
|
"observed_task_events": task_event_count(task_events),
|
||||||
"online_nodes": online_node_count,
|
"artifact_download_bytes": 0,
|
||||||
"observed_task_events": task_event_count(task_events),
|
"rendezvous_attempts": 0,
|
||||||
},
|
"hosted_wasm_processes": 0,
|
||||||
"limits": {
|
|
||||||
"artifact_download_bytes": "checked before download link creation",
|
|
||||||
"rendezvous_attempts": "checked before rendezvous plan creation",
|
|
||||||
"hosted_wasm": "checked before hosted coordinator-side work starts",
|
|
||||||
},
|
|
||||||
"next_blocked_action": null,
|
|
||||||
"billing_required": false,
|
|
||||||
"private_abuse_heuristics_exposed": false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn quota_limits_value() -> Value {
|
||||||
|
let limits = ResourceLimits::community_tier_defaults();
|
||||||
|
json!({
|
||||||
|
"api_calls": limits.limit(&LimitKind::ApiCall),
|
||||||
|
"spawns": limits.limit(&LimitKind::Spawn),
|
||||||
|
"log_bytes": limits.limit(&LimitKind::LogBytes),
|
||||||
|
"metadata_bytes": limits.limit(&LimitKind::MetadataBytes),
|
||||||
|
"debug_read_bytes": limits.limit(&LimitKind::DebugReadBytes),
|
||||||
|
"ui_events": limits.limit(&LimitKind::UiEvent),
|
||||||
|
"rendezvous_attempts": limits.limit(&LimitKind::RendezvousAttempt),
|
||||||
|
"artifact_download_bytes": limits.limit(&LimitKind::ArtifactDownloadBytes),
|
||||||
|
"hosted_fuel": limits.limit(&LimitKind::HostedFuel),
|
||||||
|
"hosted_memory_bytes": limits.limit(&LimitKind::HostedMemoryBytes),
|
||||||
|
"hosted_wall_clock_ms": limits.limit(&LimitKind::HostedWallClockMs),
|
||||||
|
"hosted_state_bytes": limits.limit(&LimitKind::HostedStateBytes),
|
||||||
|
"hosted_tuning_private": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quota_next_blocked_action(current_usage: &Value) -> Value {
|
||||||
|
if current_usage
|
||||||
|
.get("online_nodes")
|
||||||
|
.and_then(Value::as_u64)
|
||||||
|
.unwrap_or(0)
|
||||||
|
== 0
|
||||||
|
{
|
||||||
|
return json!({
|
||||||
|
"action": "node_work_requires_online_attached_node",
|
||||||
|
"category": "capability",
|
||||||
|
"quota_related": false,
|
||||||
|
"message": "no online attached node is visible for work that requires a user node"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Value::Null
|
||||||
|
}
|
||||||
|
|
||||||
fn login_plan(args: LoginArgs) -> LoginPlan {
|
fn login_plan(args: LoginArgs) -> LoginPlan {
|
||||||
login_plan_with_nonce(args, login_nonce())
|
login_plan_with_nonce(args, login_nonce())
|
||||||
}
|
}
|
||||||
|
|
@ -3822,6 +3890,114 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quota_status_uses_project_config_and_generic_public_limits() {
|
||||||
|
let temp = tempfile::tempdir().unwrap();
|
||||||
|
write_project_config(
|
||||||
|
temp.path(),
|
||||||
|
&ProjectConfig {
|
||||||
|
tenant: "tenant-quota".to_owned(),
|
||||||
|
project: "project-quota".to_owned(),
|
||||||
|
user: "user".to_owned(),
|
||||||
|
coordinator: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let status = quota_status_report(
|
||||||
|
QuotaStatusArgs {
|
||||||
|
scope: CliScopeArgs {
|
||||||
|
coordinator: None,
|
||||||
|
tenant: "tenant".to_owned(),
|
||||||
|
project: "project".to_owned(),
|
||||||
|
user: "user".to_owned(),
|
||||||
|
json: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
temp.path().to_path_buf(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(status["command"], "quota status");
|
||||||
|
assert_eq!(status["tenant"], "tenant-quota");
|
||||||
|
assert_eq!(status["project"], "project-quota");
|
||||||
|
assert_eq!(status["current_usage"]["attached_nodes"], 0);
|
||||||
|
assert_eq!(status["limits"]["artifact_download_bytes"], 268_435_456);
|
||||||
|
assert_eq!(status["community_tier_language"], true);
|
||||||
|
assert_eq!(status["billing_required"], false);
|
||||||
|
assert_eq!(
|
||||||
|
status["next_blocked_action"]["action"],
|
||||||
|
"node_work_requires_online_attached_node"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quota_status_queries_public_coordinator_usage() {
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
|
let addr = listener.local_addr().unwrap().to_string();
|
||||||
|
let server = std::thread::spawn(move || {
|
||||||
|
for _ in 0..2 {
|
||||||
|
let (mut stream, _) = listener.accept().unwrap();
|
||||||
|
let mut reader = BufReader::new(stream.try_clone().unwrap());
|
||||||
|
let mut line = String::new();
|
||||||
|
reader.read_line(&mut line).unwrap();
|
||||||
|
if line.contains("\"type\":\"list_node_descriptors\"") {
|
||||||
|
assert!(line.contains("\"tenant\":\"tenant-live\""));
|
||||||
|
stream
|
||||||
|
.write_all(
|
||||||
|
br#"{"type":"node_descriptors","descriptors":[{"id":"node-a","online":true},{"id":"node-b","online":false}],"actor":"user"}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
stream.write_all(b"\n").unwrap();
|
||||||
|
} else if line.contains("\"type\":\"list_task_events\"") {
|
||||||
|
assert!(line.contains("\"project\":\"project-live\""));
|
||||||
|
stream
|
||||||
|
.write_all(
|
||||||
|
br#"{"type":"task_events","events":[{"process":"vp-live","task":"task-a"},{"process":"vp-live","task":"task-b"}]}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
stream.write_all(b"\n").unwrap();
|
||||||
|
} else {
|
||||||
|
panic!("unexpected coordinator request: {line}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let temp = tempfile::tempdir().unwrap();
|
||||||
|
write_project_config(
|
||||||
|
temp.path(),
|
||||||
|
&ProjectConfig {
|
||||||
|
tenant: "tenant-live".to_owned(),
|
||||||
|
project: "project-live".to_owned(),
|
||||||
|
user: "user".to_owned(),
|
||||||
|
coordinator: Some(addr.clone()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let status = quota_status_report(
|
||||||
|
QuotaStatusArgs {
|
||||||
|
scope: CliScopeArgs {
|
||||||
|
coordinator: None,
|
||||||
|
tenant: "tenant".to_owned(),
|
||||||
|
project: "project".to_owned(),
|
||||||
|
user: "user".to_owned(),
|
||||||
|
json: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
temp.path().to_path_buf(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
server.join().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(status["coordinator"], addr);
|
||||||
|
assert_eq!(status["current_usage"]["attached_nodes"], 2);
|
||||||
|
assert_eq!(status["current_usage"]["online_nodes"], 1);
|
||||||
|
assert_eq!(status["current_usage"]["observed_task_events"], 2);
|
||||||
|
assert!(status["next_blocked_action"].is_null());
|
||||||
|
assert_eq!(status["task_events"]["response"]["type"], "task_events");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_command_reuses_bundle_inspection_without_full_repo_upload() {
|
fn build_command_reuses_bundle_inspection_without_full_repo_upload() {
|
||||||
let temp = tempfile::tempdir().unwrap();
|
let temp = tempfile::tempdir().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,8 @@ for (const [name, pattern] of [
|
||||||
["doctor ping reachability coverage", /fn doctor_pings_configured_coordinator\(\)/],
|
["doctor ping reachability coverage", /fn doctor_pings_configured_coordinator\(\)/],
|
||||||
["project local config coverage", /fn project_init_select_and_status_use_local_project_config\(\)/],
|
["project local config coverage", /fn project_init_select_and_status_use_local_project_config\(\)/],
|
||||||
["project coordinator status coverage", /fn project_status_queries_public_coordinator_state\(\)/],
|
["project coordinator status coverage", /fn project_status_queries_public_coordinator_state\(\)/],
|
||||||
|
["quota local status coverage", /fn quota_status_uses_project_config_and_generic_public_limits\(\)/],
|
||||||
|
["quota coordinator usage coverage", /fn quota_status_queries_public_coordinator_usage\(\)/],
|
||||||
["build no full repo upload coverage", /fn build_command_reuses_bundle_inspection_without_full_repo_upload\(\)/],
|
["build no full repo upload coverage", /fn build_command_reuses_bundle_inspection_without_full_repo_upload\(\)/],
|
||||||
["safe coordinator-required plans", /fn node_enroll_and_process_commands_have_safe_plan_without_coordinator\(\)/],
|
["safe coordinator-required plans", /fn node_enroll_and_process_commands_have_safe_plan_without_coordinator\(\)/],
|
||||||
]) {
|
]) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue