fix(opnsense-config): ensure load balancer service configuration is idempotent #129

Merged
letian merged 8 commits from idempotent-load-balancer into master 2025-10-20 19:18:50 +00:00
Owner

The previous implementation blindly added HAProxy components without checking for existing configurations on the same port, which caused duplicate entries and errors when a service was updated.

This commit refactors the logic to a robust "remove-then-add" strategy. The configure_service method now finds and removes any existing frontend and its dependent components (backend, servers, health check) before adding the new, complete service definition.

This change makes the process fully idempotent, preventing configuration drift and ensuring a predictable state.

The previous implementation blindly added HAProxy components without checking for existing configurations on the same port, which caused duplicate entries and errors when a service was updated. This commit refactors the logic to a robust "remove-then-add" strategy. The configure_service method now finds and removes any existing frontend and its dependent components (backend, servers, health check) before adding the new, complete service definition. This change makes the process fully idempotent, preventing configuration drift and ensuring a predictable state.
letian added 1 commit 2025-09-01 11:42:56 +00:00
fix: merge existing services in load balancer config
All checks were successful
Run Check Script / check (pull_request) Successful in 1m13s
e9a1aa4831
letian reviewed 2025-09-01 11:44:09 +00:00
@ -35,3 +31,1 @@
if !self.list_services().await.contains(service) {
self.add_service(service).await?;
}
self.add_service(service).await?;
Author
Owner

we might want to rename this add_or_update_service instead of just add_service

we might want to rename this `add_or_update_service` instead of just `add_service`
letian marked this conversation as resolved
letian reviewed 2025-09-01 11:49:05 +00:00
@ -46,0 +53,4 @@
if let Some(existing_backend) = existing_backend {
backend.uuid = existing_backend.uuid.clone(); // This breaks the `frontend` config
// as it is now relying on a stale uuid
Author
Owner

As of now, the first time you add a load balancer service, everything will work as expected.

Though if you run it again, as we update in place the existing backend and we reuse its uuid, the frontend will be referring to a non-existent uuid for the default_backend (see harmony/src/infra/opnsense/load_balancer.rs on line 323). So we need to fix this before merging.

As of now, the first time you add a load balancer service, everything will work as expected. Though if you run it again, as we update in place the existing backend and we reuse its uuid, the frontend will be referring to a non-existent uuid for the `default_backend` (see `harmony/src/infra/opnsense/load_balancer.rs` on line 323). So we need to fix this before merging.
letian marked this conversation as resolved
letian reviewed 2025-09-01 11:49:46 +00:00
@ -28,3 +27,2 @@
"TODO : the current implementation does not check / cleanup / merge with existing haproxy services properly. Make sure to manually verify that the configuration is correct after executing any operation here"
);
let mut config = self.opnsense_config.write().await;
let mut load_balancer = config.load_balancer();
Author
Owner

For now we only merge the existing config with the service (if it already exists). Should we also remove stale services?

For now we only merge the existing config with the service (if it already exists). Should we also remove stale services?
letian marked this conversation as resolved
Owner

Ouais j'y repense un peu et c'est pas evident. Il y a beaucoup de cas.

Il faut simplifier. Probablement que la seule logique qui est raisonnable d'implementer maintenant c'est de prendre controle des ports et reset la config pour ces ports la chaque fois. Donc la logique serait probablement quelque chose comme :

configure_service(service, servers, health_check)
  current_config.destroy_service_components_on_port(service.port) // deletes services AND related backend pool AND servers that this service points to
  current_config.add_service(service, servers)
Ouais j'y repense un peu et c'est pas evident. Il y a beaucoup de cas. Il faut simplifier. Probablement que la seule logique qui est raisonnable d'implementer maintenant c'est de prendre controle des ports et reset la config pour ces ports la chaque fois. Donc la logique serait probablement quelque chose comme : ``` configure_service(service, servers, health_check) current_config.destroy_service_components_on_port(service.port) // deletes services AND related backend pool AND servers that this service points to current_config.add_service(service, servers) ```
letian added 1 commit 2025-09-03 19:58:32 +00:00
letian changed title from WIP: fix: merge existing services in load balancer config to fix: remove existing services (by bind address) in load balancer config 2025-09-03 19:59:47 +00:00
letian changed title from fix: remove existing services (by bind address) in load balancer config to fix(opnsense-config): ensure load balancer service configuration is idempotent 2025-09-03 20:03:07 +00:00
letian added 1 commit 2025-09-03 21:18:29 +00:00
de-duplicate stuff
All checks were successful
Run Check Script / check (pull_request) Successful in 1m11s
01206f5db1
letian added 2 commits 2025-09-04 01:55:39 +00:00
fix: de-duplicate backend servers list mapped from topology
All checks were successful
Run Check Script / check (pull_request) Successful in 1m11s
3ecda84530
letian force-pushed idempotent-load-balancer from 3ecda84530 to a31b459f33 2025-09-04 02:00:38 +00:00 Compare
letian added 1 commit 2025-09-04 17:05:28 +00:00
test: add load_balancer::configure_service tests
All checks were successful
Run Check Script / check (pull_request) Successful in 1m9s
9b892dc882
letian added 1 commit 2025-09-08 19:31:08 +00:00
Merge branch 'master' into idempotent-load-balancer
All checks were successful
Run Check Script / check (pull_request) Successful in 1m3s
9a205b0eb1
letian added 1 commit 2025-10-20 19:18:10 +00:00
Merge branch 'master' into idempotent-load-balancer
Some checks failed
Run Check Script / check (pull_request) Has been cancelled
7e70ece201
letian merged commit ed7f81aa1f into master 2025-10-20 19:18:50 +00:00
letian deleted branch idempotent-load-balancer 2025-10-20 19:18:50 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: NationTech/harmony#129
No description provided.