forked from NationTech/harmony
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			master
			...
			idempotent
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9a205b0eb1 | |||
| 9b892dc882 | |||
| a31b459f33 | |||
| 3d8dd4d8e6 | |||
| 01206f5db1 | |||
| fc4c18ccea | |||
| e9a1aa4831 | 
							
								
								
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -429,6 +429,15 @@ dependencies = [
 | 
				
			|||||||
 "wait-timeout",
 | 
					 "wait-timeout",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "assertor"
 | 
				
			||||||
 | 
					version = "0.0.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "4ff24d87260733dc86d38a11c60d9400ce4a74a05d0dafa2a6f5ab249cd857cb"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "async-broadcast"
 | 
					name = "async-broadcast"
 | 
				
			||||||
version = "0.7.2"
 | 
					version = "0.7.2"
 | 
				
			||||||
@ -1881,6 +1890,8 @@ dependencies = [
 | 
				
			|||||||
 "env_logger",
 | 
					 "env_logger",
 | 
				
			||||||
 "harmony",
 | 
					 "harmony",
 | 
				
			||||||
 "harmony_macros",
 | 
					 "harmony_macros",
 | 
				
			||||||
 | 
					 "harmony_secret",
 | 
				
			||||||
 | 
					 "harmony_secret_derive",
 | 
				
			||||||
 "harmony_tui",
 | 
					 "harmony_tui",
 | 
				
			||||||
 "harmony_types",
 | 
					 "harmony_types",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
@ -3878,6 +3889,7 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
 | 
				
			|||||||
name = "opnsense-config"
 | 
					name = "opnsense-config"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "assertor",
 | 
				
			||||||
 "async-trait",
 | 
					 "async-trait",
 | 
				
			||||||
 "chrono",
 | 
					 "chrono",
 | 
				
			||||||
 "env_logger",
 | 
					 "env_logger",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								Cargo.toml
									
									
									
									
									
								
							@ -14,7 +14,8 @@ members = [
 | 
				
			|||||||
  "harmony_composer",
 | 
					  "harmony_composer",
 | 
				
			||||||
  "harmony_inventory_agent",
 | 
					  "harmony_inventory_agent",
 | 
				
			||||||
  "harmony_secret_derive",
 | 
					  "harmony_secret_derive",
 | 
				
			||||||
  "harmony_secret", "adr/agent_discovery/mdns",
 | 
					  "harmony_secret",
 | 
				
			||||||
 | 
					  "adr/agent_discovery/mdns",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[workspace.package]
 | 
					[workspace.package]
 | 
				
			||||||
@ -67,4 +68,11 @@ serde = { version = "1.0.209", features = ["derive", "rc"] }
 | 
				
			|||||||
serde_json = "1.0.127"
 | 
					serde_json = "1.0.127"
 | 
				
			||||||
askama = "0.14"
 | 
					askama = "0.14"
 | 
				
			||||||
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] }
 | 
					sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite"] }
 | 
				
			||||||
reqwest = { version = "0.12", features = ["blocking", "stream", "rustls-tls", "http2", "json"], default-features = false }
 | 
					reqwest = { version = "0.12", features = [
 | 
				
			||||||
 | 
					  "blocking",
 | 
				
			||||||
 | 
					  "stream",
 | 
				
			||||||
 | 
					  "rustls-tls",
 | 
				
			||||||
 | 
					  "http2",
 | 
				
			||||||
 | 
					  "json",
 | 
				
			||||||
 | 
					], default-features = false }
 | 
				
			||||||
 | 
					assertor = "0.0.4"
 | 
				
			||||||
 | 
				
			|||||||
@ -28,13 +28,7 @@ pub trait LoadBalancer: Send + Sync {
 | 
				
			|||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        service: &LoadBalancerService,
 | 
					        service: &LoadBalancerService,
 | 
				
			||||||
    ) -> Result<(), ExecutorError> {
 | 
					    ) -> Result<(), ExecutorError> {
 | 
				
			||||||
        debug!(
 | 
					 | 
				
			||||||
            "Listing LoadBalancer services {:?}",
 | 
					 | 
				
			||||||
            self.list_services().await
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        if !self.list_services().await.contains(service) {
 | 
					 | 
				
			||||||
        self.add_service(service).await?;
 | 
					        self.add_service(service).await?;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -26,19 +26,13 @@ impl LoadBalancer for OPNSenseFirewall {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn add_service(&self, service: &LoadBalancerService) -> Result<(), ExecutorError> {
 | 
					    async fn add_service(&self, service: &LoadBalancerService) -> Result<(), ExecutorError> {
 | 
				
			||||||
        warn!(
 | 
					 | 
				
			||||||
            "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 config = self.opnsense_config.write().await;
 | 
				
			||||||
 | 
					        let mut load_balancer = config.load_balancer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        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);
 | 
				
			||||||
        let mut load_balancer = config.load_balancer();
 | 
					
 | 
				
			||||||
        load_balancer.add_backend(backend);
 | 
					        load_balancer.configure_service(frontend, backend, servers, healthcheck);
 | 
				
			||||||
        load_balancer.add_frontend(frontend);
 | 
					 | 
				
			||||||
        load_balancer.add_servers(servers);
 | 
					 | 
				
			||||||
        if let Some(healthcheck) = healthcheck {
 | 
					 | 
				
			||||||
            load_balancer.add_healthcheck(healthcheck);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -106,7 +100,7 @@ pub(crate) fn haproxy_xml_config_to_harmony_loadbalancer(
 | 
				
			|||||||
                .backends
 | 
					                .backends
 | 
				
			||||||
                .backends
 | 
					                .backends
 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
                .find(|b| b.uuid == frontend.default_backend);
 | 
					                .find(|b| Some(b.uuid.clone()) == frontend.default_backend);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut health_check = None;
 | 
					            let mut health_check = None;
 | 
				
			||||||
            match matching_backend {
 | 
					            match matching_backend {
 | 
				
			||||||
@ -116,8 +110,7 @@ pub(crate) fn haproxy_xml_config_to_harmony_loadbalancer(
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                None => {
 | 
					                None => {
 | 
				
			||||||
                    warn!(
 | 
					                    warn!(
 | 
				
			||||||
                        "HAProxy config could not find a matching backend for frontend {:?}",
 | 
					                        "HAProxy config could not find a matching backend for frontend {frontend:?}"
 | 
				
			||||||
                        frontend
 | 
					 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -152,11 +145,11 @@ pub(crate) fn get_servers_for_backend(
 | 
				
			|||||||
        .servers
 | 
					        .servers
 | 
				
			||||||
        .iter()
 | 
					        .iter()
 | 
				
			||||||
        .filter_map(|server| {
 | 
					        .filter_map(|server| {
 | 
				
			||||||
 | 
					            let address = server.address.clone()?;
 | 
				
			||||||
 | 
					            let port = server.port?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if backend_servers.contains(&server.uuid.as_str()) {
 | 
					            if backend_servers.contains(&server.uuid.as_str()) {
 | 
				
			||||||
                return Some(BackendServer {
 | 
					                return Some(BackendServer { address, port });
 | 
				
			||||||
                    address: server.address.clone(),
 | 
					 | 
				
			||||||
                    port: server.port,
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            None
 | 
					            None
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
@ -347,7 +340,7 @@ pub(crate) fn harmony_load_balancer_service_to_haproxy_xml(
 | 
				
			|||||||
        name: format!("frontend_{}", service.listening_port),
 | 
					        name: format!("frontend_{}", service.listening_port),
 | 
				
			||||||
        bind: service.listening_port.to_string(),
 | 
					        bind: service.listening_port.to_string(),
 | 
				
			||||||
        mode: "tcp".to_string(), // TODO do not depend on health check here
 | 
					        mode: "tcp".to_string(), // TODO do not depend on health check here
 | 
				
			||||||
        default_backend: backend.uuid.clone(),
 | 
					        default_backend: Some(backend.uuid.clone()),
 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    info!("HAPRoxy frontend and backend mode currently hardcoded to tcp");
 | 
					    info!("HAPRoxy frontend and backend mode currently hardcoded to tcp");
 | 
				
			||||||
@ -361,8 +354,8 @@ fn server_to_haproxy_server(server: &BackendServer) -> HAProxyServer {
 | 
				
			|||||||
        uuid: Uuid::new_v4().to_string(),
 | 
					        uuid: Uuid::new_v4().to_string(),
 | 
				
			||||||
        name: format!("{}_{}", &server.address, &server.port),
 | 
					        name: format!("{}_{}", &server.address, &server.port),
 | 
				
			||||||
        enabled: 1,
 | 
					        enabled: 1,
 | 
				
			||||||
        address: server.address.clone(),
 | 
					        address: Some(server.address.clone()),
 | 
				
			||||||
        port: server.port,
 | 
					        port: Some(server.port),
 | 
				
			||||||
        mode: "active".to_string(),
 | 
					        mode: "active".to_string(),
 | 
				
			||||||
        server_type: "static".to_string(),
 | 
					        server_type: "static".to_string(),
 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
@ -385,8 +378,8 @@ mod tests {
 | 
				
			|||||||
        let mut haproxy = HAProxy::default();
 | 
					        let mut haproxy = HAProxy::default();
 | 
				
			||||||
        let server = HAProxyServer {
 | 
					        let server = HAProxyServer {
 | 
				
			||||||
            uuid: "server1".to_string(),
 | 
					            uuid: "server1".to_string(),
 | 
				
			||||||
            address: "192.168.1.1".to_string(),
 | 
					            address: Some("192.168.1.1".to_string()),
 | 
				
			||||||
            port: 80,
 | 
					            port: Some(80),
 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        haproxy.servers.servers.push(server);
 | 
					        haproxy.servers.servers.push(server);
 | 
				
			||||||
@ -411,8 +404,8 @@ mod tests {
 | 
				
			|||||||
        let mut haproxy = HAProxy::default();
 | 
					        let mut haproxy = HAProxy::default();
 | 
				
			||||||
        let server = HAProxyServer {
 | 
					        let server = HAProxyServer {
 | 
				
			||||||
            uuid: "server1".to_string(),
 | 
					            uuid: "server1".to_string(),
 | 
				
			||||||
            address: "192.168.1.1".to_string(),
 | 
					            address: Some("192.168.1.1".to_string()),
 | 
				
			||||||
            port: 80,
 | 
					            port: Some(80),
 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        haproxy.servers.servers.push(server);
 | 
					        haproxy.servers.servers.push(server);
 | 
				
			||||||
@ -431,8 +424,8 @@ mod tests {
 | 
				
			|||||||
        let mut haproxy = HAProxy::default();
 | 
					        let mut haproxy = HAProxy::default();
 | 
				
			||||||
        let server = HAProxyServer {
 | 
					        let server = HAProxyServer {
 | 
				
			||||||
            uuid: "server1".to_string(),
 | 
					            uuid: "server1".to_string(),
 | 
				
			||||||
            address: "192.168.1.1".to_string(),
 | 
					            address: Some("192.168.1.1".to_string()),
 | 
				
			||||||
            port: 80,
 | 
					            port: Some(80),
 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        haproxy.servers.servers.push(server);
 | 
					        haproxy.servers.servers.push(server);
 | 
				
			||||||
@ -453,16 +446,16 @@ mod tests {
 | 
				
			|||||||
        let mut haproxy = HAProxy::default();
 | 
					        let mut haproxy = HAProxy::default();
 | 
				
			||||||
        let server = HAProxyServer {
 | 
					        let server = HAProxyServer {
 | 
				
			||||||
            uuid: "server1".to_string(),
 | 
					            uuid: "server1".to_string(),
 | 
				
			||||||
            address: "some-hostname.test.mcd".to_string(),
 | 
					            address: Some("some-hostname.test.mcd".to_string()),
 | 
				
			||||||
            port: 80,
 | 
					            port: Some(80),
 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        haproxy.servers.servers.push(server);
 | 
					        haproxy.servers.servers.push(server);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let server = HAProxyServer {
 | 
					        let server = HAProxyServer {
 | 
				
			||||||
            uuid: "server2".to_string(),
 | 
					            uuid: "server2".to_string(),
 | 
				
			||||||
            address: "192.168.1.2".to_string(),
 | 
					            address: Some("192.168.1.2".to_string()),
 | 
				
			||||||
            port: 8080,
 | 
					            port: Some(8080),
 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        haproxy.servers.servers.push(server);
 | 
					        haproxy.servers.servers.push(server);
 | 
				
			||||||
 | 
				
			|||||||
@ -77,6 +77,8 @@ impl OKDBootstrapLoadBalancerScore {
 | 
				
			|||||||
            address: topology.bootstrap_host.ip.to_string(),
 | 
					            address: topology.bootstrap_host.ip.to_string(),
 | 
				
			||||||
            port,
 | 
					            port,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        backend.dedup();
 | 
				
			||||||
        backend
 | 
					        backend
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -77,7 +77,7 @@ impl YaSerializeTrait for HAProxyId {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Debug)]
 | 
					#[derive(PartialEq, Debug, Clone)]
 | 
				
			||||||
pub struct HAProxyId(String);
 | 
					pub struct HAProxyId(String);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for HAProxyId {
 | 
					impl Default for HAProxyId {
 | 
				
			||||||
@ -297,7 +297,7 @@ pub struct HAProxyFrontends {
 | 
				
			|||||||
    pub frontend: Vec<Frontend>,
 | 
					    pub frontend: Vec<Frontend>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
					#[derive(Clone, Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
				
			||||||
pub struct Frontend {
 | 
					pub struct Frontend {
 | 
				
			||||||
    #[yaserde(attribute = true)]
 | 
					    #[yaserde(attribute = true)]
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
@ -310,7 +310,7 @@ pub struct Frontend {
 | 
				
			|||||||
    pub bind_options: MaybeString,
 | 
					    pub bind_options: MaybeString,
 | 
				
			||||||
    pub mode: String,
 | 
					    pub mode: String,
 | 
				
			||||||
    #[yaserde(rename = "defaultBackend")]
 | 
					    #[yaserde(rename = "defaultBackend")]
 | 
				
			||||||
    pub default_backend: String,
 | 
					    pub default_backend: Option<String>,
 | 
				
			||||||
    pub ssl_enabled: i32,
 | 
					    pub ssl_enabled: i32,
 | 
				
			||||||
    pub ssl_certificates: MaybeString,
 | 
					    pub ssl_certificates: MaybeString,
 | 
				
			||||||
    pub ssl_default_certificate: MaybeString,
 | 
					    pub ssl_default_certificate: MaybeString,
 | 
				
			||||||
@ -416,7 +416,7 @@ pub struct HAProxyBackends {
 | 
				
			|||||||
    pub backends: Vec<HAProxyBackend>,
 | 
					    pub backends: Vec<HAProxyBackend>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
					#[derive(Clone, Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
				
			||||||
pub struct HAProxyBackend {
 | 
					pub struct HAProxyBackend {
 | 
				
			||||||
    #[yaserde(attribute = true, rename = "uuid")]
 | 
					    #[yaserde(attribute = true, rename = "uuid")]
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
@ -535,7 +535,7 @@ pub struct HAProxyServers {
 | 
				
			|||||||
    pub servers: Vec<HAProxyServer>,
 | 
					    pub servers: Vec<HAProxyServer>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
					#[derive(Clone, Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
				
			||||||
pub struct HAProxyServer {
 | 
					pub struct HAProxyServer {
 | 
				
			||||||
    #[yaserde(attribute = true, rename = "uuid")]
 | 
					    #[yaserde(attribute = true, rename = "uuid")]
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
@ -543,8 +543,8 @@ pub struct HAProxyServer {
 | 
				
			|||||||
    pub enabled: u8,
 | 
					    pub enabled: u8,
 | 
				
			||||||
    pub name: String,
 | 
					    pub name: String,
 | 
				
			||||||
    pub description: MaybeString,
 | 
					    pub description: MaybeString,
 | 
				
			||||||
    pub address: String,
 | 
					    pub address: Option<String>,
 | 
				
			||||||
    pub port: u16,
 | 
					    pub port: Option<u16>,
 | 
				
			||||||
    pub checkport: MaybeString,
 | 
					    pub checkport: MaybeString,
 | 
				
			||||||
    pub mode: String,
 | 
					    pub mode: String,
 | 
				
			||||||
    pub multiplexer_protocol: MaybeString,
 | 
					    pub multiplexer_protocol: MaybeString,
 | 
				
			||||||
@ -589,7 +589,7 @@ pub struct HAProxyHealthChecks {
 | 
				
			|||||||
    pub healthchecks: Vec<HAProxyHealthCheck>,
 | 
					    pub healthchecks: Vec<HAProxyHealthCheck>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
					#[derive(Clone, Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
 | 
				
			||||||
pub struct HAProxyHealthCheck {
 | 
					pub struct HAProxyHealthCheck {
 | 
				
			||||||
    #[yaserde(attribute = true)]
 | 
					    #[yaserde(attribute = true)]
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ sha2 = "0.10.9"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
pretty_assertions.workspace = true
 | 
					pretty_assertions.workspace = true
 | 
				
			||||||
 | 
					assertor.workspace = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[lints.rust]
 | 
					[lints.rust]
 | 
				
			||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(e2e_test)'] }
 | 
					unexpected_cfgs = { level = "warn", check-cfg = ['cfg(e2e_test)'] }
 | 
				
			||||||
 | 
				
			|||||||
@ -30,8 +30,7 @@ impl SshConfigManager {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.opnsense_shell
 | 
					        self.opnsense_shell
 | 
				
			||||||
            .exec(&format!(
 | 
					            .exec(&format!(
 | 
				
			||||||
                "cp /conf/config.xml /conf/backup/{}",
 | 
					                "cp /conf/config.xml /conf/backup/{backup_filename}"
 | 
				
			||||||
                backup_filename
 | 
					 | 
				
			||||||
            ))
 | 
					            ))
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,7 @@
 | 
				
			|||||||
mod ssh;
 | 
					mod ssh;
 | 
				
			||||||
pub use ssh::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use async_trait::async_trait;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::Error;
 | 
					use crate::Error;
 | 
				
			||||||
 | 
					use async_trait::async_trait;
 | 
				
			||||||
 | 
					pub use ssh::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[async_trait]
 | 
					#[async_trait]
 | 
				
			||||||
pub trait OPNsenseShell: std::fmt::Debug + Send + Sync {
 | 
					pub trait OPNsenseShell: std::fmt::Debug + Send + Sync {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,8 @@
 | 
				
			|||||||
use std::sync::Arc;
 | 
					use crate::{config::OPNsenseShell, Error};
 | 
				
			||||||
 | 
					 | 
				
			||||||
use log::warn;
 | 
					 | 
				
			||||||
use opnsense_config_xml::{
 | 
					use opnsense_config_xml::{
 | 
				
			||||||
    Frontend, HAProxy, HAProxyBackend, HAProxyHealthCheck, HAProxyServer, OPNsense,
 | 
					    Frontend, HAProxy, HAProxyBackend, HAProxyHealthCheck, HAProxyServer, OPNsense,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					use std::{collections::HashSet, sync::Arc};
 | 
				
			||||||
use crate::{config::OPNsenseShell, Error};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct LoadBalancerConfig<'a> {
 | 
					pub struct LoadBalancerConfig<'a> {
 | 
				
			||||||
    opnsense: &'a mut OPNsense,
 | 
					    opnsense: &'a mut OPNsense,
 | 
				
			||||||
@ -31,7 +28,7 @@ impl<'a> LoadBalancerConfig<'a> {
 | 
				
			|||||||
        match &mut self.opnsense.opnsense.haproxy.as_mut() {
 | 
					        match &mut self.opnsense.opnsense.haproxy.as_mut() {
 | 
				
			||||||
            Some(haproxy) => f(haproxy),
 | 
					            Some(haproxy) => f(haproxy),
 | 
				
			||||||
            None => unimplemented!(
 | 
					            None => unimplemented!(
 | 
				
			||||||
                "Adding a backend is not supported when haproxy config does not exist yet"
 | 
					                "Cannot configure load balancer when haproxy config does not exist yet"
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -40,21 +37,67 @@ impl<'a> LoadBalancerConfig<'a> {
 | 
				
			|||||||
        self.with_haproxy(|haproxy| haproxy.general.enabled = enabled as i32);
 | 
					        self.with_haproxy(|haproxy| haproxy.general.enabled = enabled as i32);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn add_backend(&mut self, backend: HAProxyBackend) {
 | 
					    /// Configures a service by removing any existing service on the same port
 | 
				
			||||||
        warn!("TODO make sure this new backend does not refer non-existing entities like servers or health checks");
 | 
					    /// and then adding the new definition. This ensures idempotency.
 | 
				
			||||||
        self.with_haproxy(|haproxy| haproxy.backends.backends.push(backend));
 | 
					    pub fn configure_service(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        frontend: Frontend,
 | 
				
			||||||
 | 
					        backend: HAProxyBackend,
 | 
				
			||||||
 | 
					        servers: Vec<HAProxyServer>,
 | 
				
			||||||
 | 
					        healthcheck: Option<HAProxyHealthCheck>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        self.remove_service_by_bind_address(&frontend.bind);
 | 
				
			||||||
 | 
					        self.remove_servers(&servers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.add_new_service(frontend, backend, servers, healthcheck);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn add_frontend(&mut self, frontend: Frontend) {
 | 
					    // Remove the corresponding real servers based on their name if they already exist.
 | 
				
			||||||
        self.with_haproxy(|haproxy| haproxy.frontends.frontend.push(frontend));
 | 
					    fn remove_servers(&mut self, servers: &[HAProxyServer]) {
 | 
				
			||||||
 | 
					        let server_names: HashSet<_> = servers.iter().map(|s| s.name.clone()).collect();
 | 
				
			||||||
 | 
					        self.with_haproxy(|haproxy| {
 | 
				
			||||||
 | 
					            haproxy
 | 
				
			||||||
 | 
					                .servers
 | 
				
			||||||
 | 
					                .servers
 | 
				
			||||||
 | 
					                .retain(|s| !server_names.contains(&s.name));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn add_healthcheck(&mut self, healthcheck: HAProxyHealthCheck) {
 | 
					    /// Removes a service and its dependent components based on the frontend's bind address.
 | 
				
			||||||
        self.with_haproxy(|haproxy| haproxy.healthchecks.healthchecks.push(healthcheck));
 | 
					    /// This performs a cascading delete of the frontend, backend, servers, and health check.
 | 
				
			||||||
 | 
					    fn remove_service_by_bind_address(&mut self, bind_address: &str) {
 | 
				
			||||||
 | 
					        self.with_haproxy(|haproxy| {
 | 
				
			||||||
 | 
					            let Some(old_frontend) = remove_frontend_by_bind_address(haproxy, bind_address) else {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let Some(old_backend) = remove_backend(haproxy, old_frontend) else {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            remove_healthcheck(haproxy, &old_backend);
 | 
				
			||||||
 | 
					            remove_linked_servers(haproxy, &old_backend);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn add_servers(&mut self, mut servers: Vec<HAProxyServer>) {
 | 
					    /// Adds the components of a new service to the HAProxy configuration.
 | 
				
			||||||
        self.with_haproxy(|haproxy| haproxy.servers.servers.append(&mut servers));
 | 
					    /// This function de-duplicates servers by name to prevent configuration errors.
 | 
				
			||||||
 | 
					    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.frontends.frontend.push(frontend);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn reload_restart(&self) -> Result<(), Error> {
 | 
					    pub async fn reload_restart(&self) -> Result<(), Error> {
 | 
				
			||||||
@ -82,3 +125,262 @@ 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 default_backend = old_frontend.default_backend?;
 | 
				
			||||||
 | 
					    let pos = haproxy
 | 
				
			||||||
 | 
					        .backends
 | 
				
			||||||
 | 
					        .backends
 | 
				
			||||||
 | 
					        .iter()
 | 
				
			||||||
 | 
					        .position(|b| b.uuid == 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_linked_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()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use crate::config::DummyOPNSenseShell;
 | 
				
			||||||
 | 
					    use assertor::*;
 | 
				
			||||||
 | 
					    use opnsense_config_xml::{
 | 
				
			||||||
 | 
					        Frontend, HAProxy, HAProxyBackend, HAProxyBackends, HAProxyFrontends, HAProxyHealthCheck,
 | 
				
			||||||
 | 
					        HAProxyHealthChecks, HAProxyId, HAProxyServer, HAProxyServers, MaybeString, OPNsense,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use super::LoadBalancerConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static SERVICE_BIND_ADDRESS: &str = "192.168.1.1:80";
 | 
				
			||||||
 | 
					    static OTHER_SERVICE_BIND_ADDRESS: &str = "192.168.1.1:443";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static SERVER_ADDRESS: &str = "1.1.1.1:80";
 | 
				
			||||||
 | 
					    static OTHER_SERVER_ADDRESS: &str = "1.1.1.1:443";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn configure_service_should_add_all_service_components_to_haproxy() {
 | 
				
			||||||
 | 
					        let mut opnsense = given_opnsense();
 | 
				
			||||||
 | 
					        let mut load_balancer = given_load_balancer(&mut opnsense);
 | 
				
			||||||
 | 
					        let (healthcheck, servers, backend, frontend) =
 | 
				
			||||||
 | 
					            given_service(SERVICE_BIND_ADDRESS, SERVER_ADDRESS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        load_balancer.configure_service(
 | 
				
			||||||
 | 
					            frontend.clone(),
 | 
				
			||||||
 | 
					            backend.clone(),
 | 
				
			||||||
 | 
					            servers.clone(),
 | 
				
			||||||
 | 
					            Some(healthcheck.clone()),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_haproxy_configured_with(
 | 
				
			||||||
 | 
					            opnsense,
 | 
				
			||||||
 | 
					            vec![frontend],
 | 
				
			||||||
 | 
					            vec![backend],
 | 
				
			||||||
 | 
					            servers,
 | 
				
			||||||
 | 
					            vec![healthcheck],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn configure_service_should_replace_service_on_same_bind_address() {
 | 
				
			||||||
 | 
					        let (healthcheck, servers, backend, frontend) =
 | 
				
			||||||
 | 
					            given_service(SERVICE_BIND_ADDRESS, SERVER_ADDRESS);
 | 
				
			||||||
 | 
					        let mut opnsense = given_opnsense_with(given_haproxy(
 | 
				
			||||||
 | 
					            vec![frontend.clone()],
 | 
				
			||||||
 | 
					            vec![backend.clone()],
 | 
				
			||||||
 | 
					            servers.clone(),
 | 
				
			||||||
 | 
					            vec![healthcheck.clone()],
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					        let mut load_balancer = given_load_balancer(&mut opnsense);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (updated_healthcheck, updated_servers, updated_backend, updated_frontend) =
 | 
				
			||||||
 | 
					            given_service(SERVICE_BIND_ADDRESS, OTHER_SERVER_ADDRESS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        load_balancer.configure_service(
 | 
				
			||||||
 | 
					            updated_frontend.clone(),
 | 
				
			||||||
 | 
					            updated_backend.clone(),
 | 
				
			||||||
 | 
					            updated_servers.clone(),
 | 
				
			||||||
 | 
					            Some(updated_healthcheck.clone()),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_haproxy_configured_with(
 | 
				
			||||||
 | 
					            opnsense,
 | 
				
			||||||
 | 
					            vec![updated_frontend],
 | 
				
			||||||
 | 
					            vec![updated_backend],
 | 
				
			||||||
 | 
					            updated_servers,
 | 
				
			||||||
 | 
					            vec![updated_healthcheck],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn configure_service_should_keep_existing_service_on_different_bind_addresses() {
 | 
				
			||||||
 | 
					        let (healthcheck, servers, backend, frontend) =
 | 
				
			||||||
 | 
					            given_service(SERVICE_BIND_ADDRESS, SERVER_ADDRESS);
 | 
				
			||||||
 | 
					        let (other_healthcheck, other_servers, other_backend, other_frontend) =
 | 
				
			||||||
 | 
					            given_service(OTHER_SERVICE_BIND_ADDRESS, OTHER_SERVER_ADDRESS);
 | 
				
			||||||
 | 
					        let mut opnsense = given_opnsense_with(given_haproxy(
 | 
				
			||||||
 | 
					            vec![frontend.clone()],
 | 
				
			||||||
 | 
					            vec![backend.clone()],
 | 
				
			||||||
 | 
					            servers.clone(),
 | 
				
			||||||
 | 
					            vec![healthcheck.clone()],
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					        let mut load_balancer = given_load_balancer(&mut opnsense);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        load_balancer.configure_service(
 | 
				
			||||||
 | 
					            other_frontend.clone(),
 | 
				
			||||||
 | 
					            other_backend.clone(),
 | 
				
			||||||
 | 
					            other_servers.clone(),
 | 
				
			||||||
 | 
					            Some(other_healthcheck.clone()),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_haproxy_configured_with(
 | 
				
			||||||
 | 
					            opnsense,
 | 
				
			||||||
 | 
					            vec![frontend, other_frontend],
 | 
				
			||||||
 | 
					            vec![backend, other_backend],
 | 
				
			||||||
 | 
					            [servers, other_servers].concat(),
 | 
				
			||||||
 | 
					            vec![healthcheck, other_healthcheck],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn assert_haproxy_configured_with(
 | 
				
			||||||
 | 
					        opnsense: OPNsense,
 | 
				
			||||||
 | 
					        frontends: Vec<Frontend>,
 | 
				
			||||||
 | 
					        backends: Vec<HAProxyBackend>,
 | 
				
			||||||
 | 
					        servers: Vec<HAProxyServer>,
 | 
				
			||||||
 | 
					        healthchecks: Vec<HAProxyHealthCheck>,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let haproxy = opnsense.opnsense.haproxy.as_ref().unwrap();
 | 
				
			||||||
 | 
					        assert_that!(haproxy.frontends.frontend).contains_exactly(frontends);
 | 
				
			||||||
 | 
					        assert_that!(haproxy.backends.backends).contains_exactly(backends);
 | 
				
			||||||
 | 
					        assert_that!(haproxy.servers.servers).is_equal_to(servers);
 | 
				
			||||||
 | 
					        assert_that!(haproxy.healthchecks.healthchecks).contains_exactly(healthchecks);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_opnsense() -> OPNsense {
 | 
				
			||||||
 | 
					        OPNsense::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_opnsense_with(haproxy: HAProxy) -> OPNsense {
 | 
				
			||||||
 | 
					        let mut opnsense = OPNsense::default();
 | 
				
			||||||
 | 
					        opnsense.opnsense.haproxy = Some(haproxy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        opnsense
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_load_balancer<'a>(opnsense: &'a mut OPNsense) -> LoadBalancerConfig<'a> {
 | 
				
			||||||
 | 
					        let opnsense_shell = Arc::new(DummyOPNSenseShell {});
 | 
				
			||||||
 | 
					        if opnsense.opnsense.haproxy.is_none() {
 | 
				
			||||||
 | 
					            opnsense.opnsense.haproxy = Some(HAProxy::default());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        LoadBalancerConfig::new(opnsense, opnsense_shell)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_service(
 | 
				
			||||||
 | 
					        bind_address: &str,
 | 
				
			||||||
 | 
					        server_address: &str,
 | 
				
			||||||
 | 
					    ) -> (
 | 
				
			||||||
 | 
					        HAProxyHealthCheck,
 | 
				
			||||||
 | 
					        Vec<HAProxyServer>,
 | 
				
			||||||
 | 
					        HAProxyBackend,
 | 
				
			||||||
 | 
					        Frontend,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        let healthcheck = given_healthcheck();
 | 
				
			||||||
 | 
					        let servers = vec![given_server(server_address)];
 | 
				
			||||||
 | 
					        let backend = given_backend();
 | 
				
			||||||
 | 
					        let frontend = given_frontend(bind_address);
 | 
				
			||||||
 | 
					        (healthcheck, servers, backend, frontend)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_haproxy(
 | 
				
			||||||
 | 
					        frontends: Vec<Frontend>,
 | 
				
			||||||
 | 
					        backends: Vec<HAProxyBackend>,
 | 
				
			||||||
 | 
					        servers: Vec<HAProxyServer>,
 | 
				
			||||||
 | 
					        healthchecks: Vec<HAProxyHealthCheck>,
 | 
				
			||||||
 | 
					    ) -> HAProxy {
 | 
				
			||||||
 | 
					        HAProxy {
 | 
				
			||||||
 | 
					            frontends: HAProxyFrontends {
 | 
				
			||||||
 | 
					                frontend: frontends,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            backends: HAProxyBackends { backends },
 | 
				
			||||||
 | 
					            servers: HAProxyServers { servers },
 | 
				
			||||||
 | 
					            healthchecks: HAProxyHealthChecks { healthchecks },
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_frontend(bind_address: &str) -> Frontend {
 | 
				
			||||||
 | 
					        Frontend {
 | 
				
			||||||
 | 
					            uuid: "uuid".into(),
 | 
				
			||||||
 | 
					            id: HAProxyId::default(),
 | 
				
			||||||
 | 
					            enabled: 1,
 | 
				
			||||||
 | 
					            name: format!("frontend_{bind_address}"),
 | 
				
			||||||
 | 
					            bind: bind_address.into(),
 | 
				
			||||||
 | 
					            default_backend: Some("backend-uuid".into()),
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_backend() -> HAProxyBackend {
 | 
				
			||||||
 | 
					        HAProxyBackend {
 | 
				
			||||||
 | 
					            uuid: "backend-uuid".into(),
 | 
				
			||||||
 | 
					            id: HAProxyId::default(),
 | 
				
			||||||
 | 
					            enabled: 1,
 | 
				
			||||||
 | 
					            name: "backend_192.168.1.1:80".into(),
 | 
				
			||||||
 | 
					            linked_servers: MaybeString::from("server-uuid"),
 | 
				
			||||||
 | 
					            health_check_enabled: 1,
 | 
				
			||||||
 | 
					            health_check: MaybeString::from("healthcheck-uuid"),
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_server(address: &str) -> HAProxyServer {
 | 
				
			||||||
 | 
					        HAProxyServer {
 | 
				
			||||||
 | 
					            uuid: "server-uuid".into(),
 | 
				
			||||||
 | 
					            id: HAProxyId::default(),
 | 
				
			||||||
 | 
					            name: address.into(),
 | 
				
			||||||
 | 
					            address: Some(address.into()),
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn given_healthcheck() -> HAProxyHealthCheck {
 | 
				
			||||||
 | 
					        HAProxyHealthCheck {
 | 
				
			||||||
 | 
					            uuid: "healthcheck-uuid".into(),
 | 
				
			||||||
 | 
					            name: "healthcheck".into(),
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user