forked from NationTech/harmony
		
	remove old service components (frontend, backend, servers, healthcheck) with same bind address before adding new service
This commit is contained in:
		
							parent
							
								
									e9a1aa4831
								
							
						
					
					
						commit
						fc4c18ccea
					
				| @ -29,12 +29,8 @@ impl LoadBalancer for OPNSenseFirewall { | |||||||
| 
 | 
 | ||||||
|         let (frontend, backend, servers, healthcheck) = |         let (frontend, backend, servers, healthcheck) = | ||||||
|             harmony_load_balancer_service_to_haproxy_xml(service); |             harmony_load_balancer_service_to_haproxy_xml(service); | ||||||
|         load_balancer.add_backend(backend); | 
 | ||||||
|         load_balancer.add_frontend(frontend); |         load_balancer.configure_service(frontend, backend, servers, healthcheck); | ||||||
|         load_balancer.add_servers(servers); |  | ||||||
|         if let Some(healthcheck) = healthcheck { |  | ||||||
|             load_balancer.add_healthcheck(healthcheck); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| use std::sync::Arc; | use std::{collections::HashSet, sync::Arc}; | ||||||
| 
 | 
 | ||||||
| use log::warn; | use log::warn; | ||||||
| use opnsense_config_xml::{ | use opnsense_config_xml::{ | ||||||
| @ -40,86 +40,51 @@ impl<'a> LoadBalancerConfig<'a> { | |||||||
|         self.with_haproxy(|haproxy| haproxy.general.enabled = enabled as i32); |         self.with_haproxy(|haproxy| haproxy.general.enabled = enabled as i32); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Adds or updates a backend pool.
 |     /// Configures a service by removing any existing service on the same port
 | ||||||
|     /// If a backend with the same name exists, it is updated. Otherwise, it is added.
 |     /// and then adding the new definition. This ensures idempotency.
 | ||||||
|     pub fn add_backend(&mut self, mut backend: HAProxyBackend) { |     pub fn configure_service( | ||||||
|         warn!("TODO make sure this new backend does not refer non-existing entities like servers or health checks"); |         &mut self, | ||||||
|         self.with_haproxy(|haproxy| { |         frontend: Frontend, | ||||||
|             let existing_backend = haproxy |         backend: HAProxyBackend, | ||||||
|                 .backends |         servers: Vec<HAProxyServer>, | ||||||
|                 .backends |         healthcheck: Option<HAProxyHealthCheck>, | ||||||
|                 .iter_mut() |     ) { | ||||||
|                 .find(|b| b.name == backend.name); |         self.remove_service_by_bind_address(&frontend.bind); | ||||||
|  |         self.add_new_service(frontend, backend, servers, healthcheck); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|             if let Some(existing_backend) = existing_backend { |     /// Removes a service and its dependent components based on the frontend's bind address.
 | ||||||
|                 backend.uuid = existing_backend.uuid.clone(); // This breaks the `frontend` config
 |     /// This performs a cascading delete of the frontend, backend, servers, and health check.
 | ||||||
|                                                               // as it is now relying on a stale uuid
 |     fn remove_service_by_bind_address(&mut self, bind_address: &str) { | ||||||
|                 backend.id = existing_backend.id.clone(); |         self.with_haproxy(|haproxy| { | ||||||
|                 *existing_backend = backend; |             let Some(old_frontend) = remove_frontend_by_bind_address(haproxy, bind_address) else { | ||||||
|             } else { |                 return; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             let Some(old_backend) = remove_backend(haproxy, old_frontend) else { | ||||||
|  |                 return; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             remove_healthcheck(haproxy, &old_backend); | ||||||
|  |             remove_servers(haproxy, &old_backend); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Adds the components of a new service to the HAProxy configuration.
 | ||||||
|  |     fn add_new_service( | ||||||
|  |         &mut self, | ||||||
|  |         frontend: Frontend, | ||||||
|  |         backend: HAProxyBackend, | ||||||
|  |         servers: Vec<HAProxyServer>, | ||||||
|  |         healthcheck: Option<HAProxyHealthCheck>, | ||||||
|  |     ) { | ||||||
|  |         self.with_haproxy(|haproxy| { | ||||||
|  |             if let Some(check) = healthcheck { | ||||||
|  |                 haproxy.healthchecks.healthchecks.push(check); | ||||||
|  |             } | ||||||
|  |             haproxy.servers.servers.extend(servers); | ||||||
|             haproxy.backends.backends.push(backend); |             haproxy.backends.backends.push(backend); | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Adds or updates a frontend.
 |  | ||||||
|     /// If a frontend with the same name exists, it is updated. Otherwise, it is added.
 |  | ||||||
|     pub fn add_frontend(&mut self, mut frontend: Frontend) { |  | ||||||
|         self.with_haproxy(|haproxy| { |  | ||||||
|             let existing_frontend = haproxy |  | ||||||
|                 .frontends |  | ||||||
|                 .frontend |  | ||||||
|                 .iter_mut() |  | ||||||
|                 .find(|f| f.name == frontend.name); |  | ||||||
| 
 |  | ||||||
|             if let Some(existing_frontend) = existing_frontend { |  | ||||||
|                 frontend.uuid = existing_frontend.uuid.clone(); |  | ||||||
|                 frontend.id = existing_frontend.id.clone(); |  | ||||||
|                 *existing_frontend = frontend; |  | ||||||
|             } else { |  | ||||||
|             haproxy.frontends.frontend.push(frontend); |             haproxy.frontends.frontend.push(frontend); | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Adds or updates a health check.
 |  | ||||||
|     /// If a health check with the same name exists, it is updated. Otherwise, it is added.
 |  | ||||||
|     pub fn add_healthcheck(&mut self, mut healthcheck: HAProxyHealthCheck) { |  | ||||||
|         self.with_haproxy(|haproxy| { |  | ||||||
|             let existing_healthcheck = haproxy |  | ||||||
|                 .healthchecks |  | ||||||
|                 .healthchecks |  | ||||||
|                 .iter_mut() |  | ||||||
|                 .find(|h| h.name == healthcheck.name); |  | ||||||
| 
 |  | ||||||
|             if let Some(existing_check) = existing_healthcheck { |  | ||||||
|                 healthcheck.uuid = existing_check.uuid.clone(); |  | ||||||
|                 *existing_check = healthcheck; |  | ||||||
|             } else { |  | ||||||
|                 haproxy.healthchecks.healthchecks.push(healthcheck); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Adds or updates a list of servers to the HAProxy configuration.
 |  | ||||||
|     /// If a server with the same name already exists, it is updated. Otherwise, it is added.
 |  | ||||||
|     pub fn add_servers(&mut self, servers: Vec<HAProxyServer>) { |  | ||||||
|         self.with_haproxy(|haproxy| { |  | ||||||
|             for server in servers { |  | ||||||
|                 let existing_server = haproxy |  | ||||||
|                     .servers |  | ||||||
|                     .servers |  | ||||||
|                     .iter_mut() |  | ||||||
|                     .find(|s| s.name == server.name); |  | ||||||
| 
 |  | ||||||
|                 if let Some(existing_server) = existing_server { |  | ||||||
|                     existing_server.address = server.address; |  | ||||||
|                     existing_server.port = server.port; |  | ||||||
|                     existing_server.enabled = server.enabled; |  | ||||||
|                 } else { |  | ||||||
|                     haproxy.servers.servers.push(server); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -148,3 +113,49 @@ impl<'a> LoadBalancerConfig<'a> { | |||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | fn remove_frontend_by_bind_address(haproxy: &mut HAProxy, bind_address: &str) -> Option<Frontend> { | ||||||
|  |     let pos = haproxy | ||||||
|  |         .frontends | ||||||
|  |         .frontend | ||||||
|  |         .iter() | ||||||
|  |         .position(|f| f.bind == bind_address); | ||||||
|  | 
 | ||||||
|  |     match pos { | ||||||
|  |         Some(pos) => Some(haproxy.frontends.frontend.remove(pos)), | ||||||
|  |         None => None, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn remove_backend(haproxy: &mut HAProxy, old_frontend: Frontend) -> Option<HAProxyBackend> { | ||||||
|  |     let pos = haproxy | ||||||
|  |         .backends | ||||||
|  |         .backends | ||||||
|  |         .iter() | ||||||
|  |         .position(|b| b.uuid == old_frontend.default_backend); | ||||||
|  | 
 | ||||||
|  |     match pos { | ||||||
|  |         Some(pos) => Some(haproxy.backends.backends.remove(pos)), | ||||||
|  |         None => None, // orphaned frontend, shouldn't happen
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn remove_healthcheck(haproxy: &mut HAProxy, backend: &HAProxyBackend) { | ||||||
|  |     if let Some(uuid) = &backend.health_check.content { | ||||||
|  |         haproxy | ||||||
|  |             .healthchecks | ||||||
|  |             .healthchecks | ||||||
|  |             .retain(|h| h.uuid != *uuid); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Remove the backend's servers. This assumes servers are not shared between services.
 | ||||||
|  | fn remove_servers(haproxy: &mut HAProxy, backend: &HAProxyBackend) { | ||||||
|  |     if let Some(server_uuids_str) = &backend.linked_servers.content { | ||||||
|  |         let server_uuids_to_remove: HashSet<_> = server_uuids_str.split(',').collect(); | ||||||
|  |         haproxy | ||||||
|  |             .servers | ||||||
|  |             .servers | ||||||
|  |             .retain(|s| !server_uuids_to_remove.contains(s.uuid.as_str())); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user