ACME: a renewal that isn't due yet is rescheduled, not failed for good
ci / fork-checks (pull_request) Successful in 14s
ci / build (pull_request) Successful in 16m46s

When a valid certificate already covered a domain's names (one stored
by hand before the domain was switched to automatic, for instance), the
renewal task ended with NotDue, which the task manager treats as a
permanent failure. Nothing rescheduled it, so the certificate expired
unrenewed. The renewal now returns a new AcmeRenewal task due when the
certificate falls due, the same way a successful renewal does, and logs
it as a backoff.

The ACME integration suite checks that renewing again right after
issuance hands back one AcmeRenewal for that domain, due at the
certificate's renewal point.
This commit is contained in:
2026-09-28 12:15:05 -07:00
parent 5a73a1183a
commit beb6c33e63
2 changed files with 46 additions and 5 deletions
+27
View File
@@ -1,7 +1,10 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 Coffey Labs
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*
* Modified by Coffey Labs in 2026 for INBUXA.
*/
use crate::utils::server::TestServer;
@@ -286,6 +289,30 @@ pub async fn test(test: &TestServer) {
not_valid_before + length / 2,
task.due_timestamp() as i64
);
// inbuxa: renewing while a valid certificate already covers the names
// (say, one stored by hand before the domain went automatic) schedules
// the renewal for when it falls due. It used to end the task for good.
let rescheduled = test
.server
.acme_renew(tls_domain_id)
.await
.ok()
.expect("a renewal that isn't due yet to be rescheduled, not to fail");
assert!(
matches!(
rescheduled.as_slice(),
[Task::AcmeRenewal(TaskDomainManagement { domain_id, .. })] if *domain_id == tls_domain_id
),
"Expected one rescheduled ACME renewal, found: {:?}",
rescheduled
);
assert_eq!(
rescheduled[0].due_timestamp() as i64,
not_valid_before + length / 2,
"The rescheduled renewal should fall due when the certificate does"
);
account.registry_destroy_all(ObjectType::Certificate).await;
account.registry_destroy_all(ObjectType::Task).await;