Publish release-5edbadb22804 filtered source

Private source: 5edbadb2280419cf2cb42b5237835304ad7040e0

Public tree identity: sha256:e0d4d5e1c8693959e69448684a71582edfc13e9b18d7f1e733034fea5ce62cda
This commit is contained in:
Clusterflux Release 2026-07-29 18:13:15 +02:00
parent b93aef4f25
commit a9f6f8b7a9
12 changed files with 430 additions and 166 deletions

View file

@ -2435,7 +2435,7 @@ fn authenticated_api_calls_are_metered_per_tenant_and_project_before_dispatch()
}
#[test]
fn signed_node_log_ingestion_checks_scoped_quota_before_accepting_bytes() {
fn signed_node_log_ingestion_truncates_at_scoped_quota_without_failing_reports() {
let mut limits = ResourceLimits::unlimited();
limits.limits.insert(LimitKind::LogBytes, 4);
let quota = CoordinatorQuotaConfiguration::new(limits, [(LimitKind::LogBytes, 60)]).unwrap();
@ -2494,7 +2494,7 @@ fn signed_node_log_ingestion_checks_scoped_quota_before_accepting_bytes() {
backpressured: false,
})
.unwrap();
let denied = service
let truncated = service
.handle_signed_node_request_auto(CoordinatorRequest::ReportTaskLog {
tenant: "tenant".to_owned(),
project: "project".to_owned(),
@ -2509,8 +2509,22 @@ fn signed_node_log_ingestion_checks_scoped_quota_before_accepting_bytes() {
stderr_truncated: false,
backpressured: true,
})
.unwrap_err();
assert!(denied.to_string().contains("LogBytes"));
.unwrap();
let CoordinatorResponse::TaskLogRecorded {
stdout_tail,
stdout_bytes: 4,
..
} = truncated
else {
panic!("expected a successful truncated task-log report");
};
assert_eq!(stdout_tail, "[log output truncated at project log quota]");
assert!(service
.recent_logs
.get(&(TenantId::from("tenant"), ProjectId::from("project")))
.unwrap()
.iter()
.any(|entry| entry.text.contains("project log quota") && entry.truncated));
assert_eq!(
service
.quota
@ -2519,6 +2533,140 @@ fn signed_node_log_ingestion_checks_scoped_quota_before_accepting_bytes() {
);
}
#[test]
fn log_quota_exhaustion_cannot_strand_task_completion_or_artifact_publication() {
let mut limits = ResourceLimits::unlimited();
limits.limits.insert(LimitKind::LogBytes, 4);
let quota = CoordinatorQuotaConfiguration::new(limits, [(LimitKind::LogBytes, 60)]).unwrap();
let mut service = CoordinatorService::new_with_admin_token_database_url_and_quota(
7,
"test-admin-token",
None,
quota,
)
.unwrap();
service.set_server_time(30);
service
.handle_request(CoordinatorRequest::AttachNode {
tenant: "tenant".to_owned(),
project: "project".to_owned(),
node: "node".to_owned(),
public_key: test_node_public_key("node"),
})
.unwrap();
service
.handle_request(CoordinatorRequest::StartProcess {
launch_attempt: None,
tenant: "tenant".to_owned(),
project: "project".to_owned(),
actor_user: None,
actor_agent: None,
agent_public_key_fingerprint: None,
agent_signature: None,
process: "process".to_owned(),
restart: false,
})
.unwrap();
service
.handle_signed_node_request_auto(CoordinatorRequest::ReconnectNode {
tenant: "tenant".to_owned(),
project: "project".to_owned(),
node: "node".to_owned(),
process: "process".to_owned(),
epoch: 7,
})
.unwrap();
register_test_task_assignment(
&mut service,
"tenant",
"project",
"process",
"node",
"compile",
"compile-one",
7,
);
service.record_task_completion_event(TaskCompletionEvent {
tenant: TenantId::from("tenant"),
project: ProjectId::from("project"),
process: ProcessId::from("process"),
node: NodeId::from("coordinator-main"),
executor: TaskExecutor::CoordinatorMain,
task_definition: TaskDefinitionId::from("build"),
task: TaskInstanceId::from("main"),
attempt_id: None,
placement: None,
terminal_state: TaskTerminalState::Completed,
status_code: Some(0),
stdout_bytes: 0,
stderr_bytes: 0,
stdout_tail: String::new(),
stderr_tail: String::new(),
stdout_truncated: false,
stderr_truncated: false,
artifact_path: None,
artifact_digest: None,
artifact_size_bytes: None,
result: None,
});
service
.handle_signed_node_request_auto(CoordinatorRequest::TaskCompleted {
tenant: "tenant".to_owned(),
project: "project".to_owned(),
process: "process".to_owned(),
node: "node".to_owned(),
task: "compile-one".to_owned(),
terminal_state: Some(TaskTerminalState::Completed),
status_code: Some(0),
stdout_bytes: 64,
stderr_bytes: 0,
stdout_tail: "the-real-final-tail".to_owned(),
stderr_tail: String::new(),
stdout_truncated: true,
stderr_truncated: false,
artifact_path: Some("/vfs/artifacts/result.bin".to_owned()),
artifact_digest: Some(Digest::sha256("artifact bytes")),
artifact_size_bytes: Some(14),
result: None,
})
.unwrap();
let event = service
.task_events
.iter()
.find(|event| event.task == TaskInstanceId::from("compile-one"))
.unwrap();
assert_eq!(
event.stdout_tail,
"[log output truncated at project log quota]"
);
assert!(event.stdout_truncated);
assert!(!service
.active_tasks
.iter()
.any(|key| key.4 == TaskInstanceId::from("compile-one")));
assert!(service
.coordinator
.active_process(
&TenantId::from("tenant"),
&ProjectId::from("project"),
&ProcessId::from("process"),
)
.is_none());
assert!(matches!(
service
.handle_request(CoordinatorRequest::GetArtifact {
tenant: "tenant".to_owned(),
project: "project".to_owned(),
actor_user: "user".to_owned(),
artifact: "result.bin".to_owned(),
})
.unwrap(),
CoordinatorResponse::Artifact { .. }
));
}
#[test]
fn service_attaches_node_starts_process_and_records_scoped_task_event() {
let mut service = CoordinatorService::new(7);