test: add load_balancer::configure_service tests
All checks were successful
Run Check Script / check (pull_request) Successful in 1m9s
All checks were successful
Run Check Script / check (pull_request) Successful in 1m9s
This commit is contained in:
parent
a31b459f33
commit
9b892dc882
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"
|
||||||
@ -1846,6 +1855,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",
|
||||||
@ -3830,6 +3841,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",
|
||||||
|
14
Cargo.toml
14
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]
|
||||||
@ -66,5 +67,12 @@ thiserror = "2.0.14"
|
|||||||
serde = { version = "1.0.209", features = ["derive", "rc"] }
|
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"
|
||||||
|
@ -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,
|
||||||
@ -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,
|
||||||
@ -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,
|
||||||
|
@ -24,6 +24,7 @@ uuid.workspace = true
|
|||||||
|
|
||||||
[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)'] }
|
||||||
|
@ -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 {
|
||||||
|
@ -28,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"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,3 +172,215 @@ fn remove_linked_servers(haproxy: &mut HAProxy, backend: &HAProxyBackend) {
|
|||||||
.retain(|s| !server_uuids_to_remove.contains(s.uuid.as_str()));
|
.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