mirror of
https://github.com/mayanayza/netvisor.git
synced 2025-12-10 08:24:08 -06:00
fixed a bug with docker bridge subnet deduplication
This commit is contained in:
@@ -190,15 +190,21 @@ impl DiscoversNetworkedEntities for Discovery<DockerScanDiscovery> {
|
||||
.scan_interfaces(self.discovery_type(), daemon_id, network_id)
|
||||
.await?;
|
||||
|
||||
tracing::info!("Host subnets {:?}", host_subnets);
|
||||
|
||||
let docker_subnets = self
|
||||
.get_subnets_from_docker_networks(daemon_id, network_id)
|
||||
.await?;
|
||||
|
||||
let subnets = [host_subnets, docker_subnets].concat();
|
||||
tracing::info!("Docker subnets {:?}", docker_subnets);
|
||||
|
||||
let subnets: Vec<Subnet> = [host_subnets, docker_subnets].concat();
|
||||
|
||||
let subnet_futures = subnets.iter().map(|subnet| self.create_subnet(subnet));
|
||||
let subnets = try_join_all(subnet_futures).await?;
|
||||
|
||||
tracing::info!("Subnets {:?}", subnets);
|
||||
|
||||
Ok(subnets)
|
||||
}
|
||||
}
|
||||
@@ -217,6 +223,7 @@ impl Discovery<DockerScanDiscovery> {
|
||||
}
|
||||
|
||||
/// Create service which has all discovered containers in containers field
|
||||
/// Host will also have
|
||||
pub async fn create_docker_daemon_service(&self, services: Vec<Service>) -> Result<(), Error> {
|
||||
let daemon_id = self.as_ref().config_store.get_id().await?;
|
||||
let network_id = self
|
||||
|
||||
@@ -61,12 +61,7 @@ impl DiscoversNetworkedEntities for Discovery<NetworkScanDiscovery> {
|
||||
cancel: CancellationToken,
|
||||
) -> Result<(), Error> {
|
||||
// Ignore docker bridge subnets, they are discovered through Docker Discovery
|
||||
let subnets: Vec<Subnet> = self
|
||||
.discover_create_subnets()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|s| s.base.subnet_type != SubnetType::DockerBridge)
|
||||
.collect();
|
||||
let subnets: Vec<Subnet> = self.discover_create_subnets().await?;
|
||||
|
||||
let total_ips_across_subnets: usize = subnets
|
||||
.iter()
|
||||
@@ -106,6 +101,12 @@ impl DiscoversNetworkedEntities for Discovery<NetworkScanDiscovery> {
|
||||
.utils
|
||||
.scan_interfaces(self.discovery_type(), daemon_id, network_id)
|
||||
.await?;
|
||||
|
||||
let subnets: Vec<Subnet> = subnets
|
||||
.into_iter()
|
||||
.filter(|s| s.base.subnet_type != SubnetType::DockerBridge)
|
||||
.collect();
|
||||
|
||||
let subnet_futures = subnets.iter().map(|subnet| self.create_subnet(subnet));
|
||||
let subnets = try_join_all(subnet_futures).await?;
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ impl HostService {
|
||||
) -> Result<(Host, Vec<Service>)> {
|
||||
// Create host first (handles duplicates via upsert_host)
|
||||
|
||||
// Manually created and needs actual UUID
|
||||
let mut created_host = if host.id == Uuid::nil() {
|
||||
self.create_host(Host::new(host.base.clone()), &host.base.network_id)
|
||||
.await?
|
||||
@@ -97,12 +98,15 @@ impl HostService {
|
||||
|
||||
/// Create a new host
|
||||
async fn create_host(&self, host: Host, network_id: &Uuid) -> Result<Host> {
|
||||
let lock = self.get_host_lock(&host.id).await;
|
||||
let _guard = lock.lock().await;
|
||||
|
||||
tracing::debug!("Creating host {:?}", host);
|
||||
|
||||
let all_hosts = self.storage.get_all(network_id).await?;
|
||||
|
||||
let host_from_storage = match all_hosts.into_iter().find(|h| host.eq(h)) {
|
||||
// If both are from discovery, or if they have the same ID but for some reason the create route is being used, upsert data
|
||||
// If both are from discovery, or if they have the same ID, upsert data
|
||||
Some(existing_host)
|
||||
if (host.base.source.discriminant() == EntitySourceDiscriminants::Discovery
|
||||
&& existing_host.base.source.discriminant()
|
||||
@@ -288,6 +292,9 @@ impl HostService {
|
||||
return Err(anyhow!("Can't consolidate a host that has a daemon. Consolidate the other host into the daemon host."));
|
||||
}
|
||||
|
||||
let lock = self.get_host_lock(&destination_host.id).await;
|
||||
let _guard1 = lock.lock().await;
|
||||
|
||||
tracing::debug!(
|
||||
"Consolidating host {:?} into host {:?}",
|
||||
other_host,
|
||||
|
||||
@@ -102,15 +102,15 @@ impl ServiceService {
|
||||
) -> Result<Service> {
|
||||
let mut binding_updates = 0;
|
||||
|
||||
let lock = self.get_service_lock(&existing_service.id).await;
|
||||
let _guard = lock.lock().await;
|
||||
|
||||
tracing::debug!(
|
||||
"Upserting new service data {:?} into {:?}",
|
||||
new_service_data,
|
||||
existing_service
|
||||
);
|
||||
|
||||
let lock = self.get_service_lock(&existing_service.id).await;
|
||||
let _guard = lock.lock().await;
|
||||
|
||||
for new_service_binding in &new_service_data.base.bindings {
|
||||
if !existing_service.base.bindings.contains(new_service_binding) {
|
||||
binding_updates += 1;
|
||||
@@ -227,11 +227,11 @@ impl ServiceService {
|
||||
}
|
||||
|
||||
pub async fn update_service(&self, mut service: Service) -> Result<Service> {
|
||||
tracing::debug!("Updating service: {:?}", service);
|
||||
|
||||
let lock = self.get_service_lock(&service.id).await;
|
||||
let _guard = lock.lock().await;
|
||||
|
||||
tracing::debug!("Updating service: {:?}", service);
|
||||
|
||||
let current_service = self
|
||||
.get_service(&service.id)
|
||||
.await?
|
||||
@@ -409,6 +409,9 @@ impl ServiceService {
|
||||
}
|
||||
|
||||
pub async fn delete_service(&self, id: &Uuid) -> Result<()> {
|
||||
let lock = self.get_service_lock(id).await;
|
||||
let _guard = lock.lock().await;
|
||||
|
||||
let service = self
|
||||
.get_service(id)
|
||||
.await?
|
||||
@@ -416,9 +419,6 @@ impl ServiceService {
|
||||
|
||||
let mut all_services = self.get_all_services(&service.base.network_id).await?;
|
||||
|
||||
let lock = self.get_service_lock(&service.id).await;
|
||||
let _guard = lock.lock().await;
|
||||
|
||||
self.update_group_service_bindings(&service, None).await?;
|
||||
|
||||
let container_update_futures = all_services.iter_mut().filter_map(|s| {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::server::{
|
||||
discovery::types::base::{DiscoveryType, EntitySource},
|
||||
hosts::service::HostService,
|
||||
subnets::{storage::SubnetStorage, types::base::Subnet},
|
||||
};
|
||||
@@ -24,8 +25,56 @@ impl SubnetService {
|
||||
pub async fn create_subnet(&self, subnet: Subnet) -> Result<Subnet> {
|
||||
let all_subnets = self.storage.get_all(&subnet.base.network_id).await?;
|
||||
|
||||
tracing::debug!("Creating subnet {:?}", subnet);
|
||||
|
||||
let subnet_from_storage = match all_subnets.iter().find(|s| subnet.eq(s)) {
|
||||
Some(existing_subnet) => {
|
||||
// Docker will default to the same subnet range for bridge networks, so we need a way to distinguish docker bridge subnets
|
||||
// with the same CIDR but which originate from different hosts
|
||||
|
||||
// This branch returns the existing subnet for docker bridge subnets created from the same host
|
||||
// And the same subnet for all other sources provided CIDRs match
|
||||
Some(existing_subnet)
|
||||
if {
|
||||
let result = match (&existing_subnet.base.source, &subnet.base.source) {
|
||||
(
|
||||
EntitySource::Discovery {
|
||||
metadata: existing_metadata,
|
||||
},
|
||||
EntitySource::Discovery { metadata },
|
||||
) => {
|
||||
// Only one metadata entry will be present for subnet which is trying to be created bc it is brand new / just discovered
|
||||
if let Some(metadata) = metadata.first() {
|
||||
existing_metadata.iter().any(|other_m| {
|
||||
match (metadata.discovery_type, other_m.discovery_type) {
|
||||
// Only return existing if they originate from the same host
|
||||
(
|
||||
DiscoveryType::Docker { host_id },
|
||||
DiscoveryType::Docker {
|
||||
host_id: other_host_id,
|
||||
},
|
||||
) => host_id == other_host_id,
|
||||
// Always return existing for other types
|
||||
_ => true,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Error comparing discovered subnets during creation: subnet missing discovery metadata"));
|
||||
}
|
||||
}
|
||||
// Don't apply this to other cases - same CIDR means same subnet
|
||||
_ => true,
|
||||
};
|
||||
|
||||
tracing::info!(
|
||||
"Dedup check result: {}, existing: {:?}, new: {:?}",
|
||||
result,
|
||||
existing_subnet.base.source,
|
||||
subnet.base.source
|
||||
);
|
||||
|
||||
result
|
||||
} =>
|
||||
{
|
||||
tracing::warn!(
|
||||
"Duplicate subnet for {}: {} found, returning existing {}: {}",
|
||||
subnet.base.name,
|
||||
@@ -35,7 +84,8 @@ impl SubnetService {
|
||||
);
|
||||
existing_subnet.clone()
|
||||
}
|
||||
None => {
|
||||
// If there's no existing subnet, create a new one
|
||||
_ => {
|
||||
self.storage.create(&subnet).await?;
|
||||
tracing::info!("Created subnet {}: {}", subnet.base.name, subnet.id);
|
||||
subnet
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::server::discovery::types::base::{
|
||||
DiscoveryMetadata, DiscoveryType, EntitySource, EntitySourceDiscriminants,
|
||||
};
|
||||
use crate::server::discovery::types::base::{DiscoveryMetadata, DiscoveryType, EntitySource};
|
||||
use crate::server::services::types::definitions::ServiceDefinitionExt;
|
||||
use crate::server::shared::types::api::deserialize_empty_string_as_none;
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -10,7 +8,6 @@ use cidr::{IpCidr, Ipv4Cidr};
|
||||
use pnet::ipnetwork::IpNetwork;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::hash::Hash;
|
||||
use strum::IntoDiscriminant;
|
||||
use strum_macros::{Display, EnumDiscriminants, EnumIter, IntoStaticStr};
|
||||
use uuid::Uuid;
|
||||
use validator::Validate;
|
||||
@@ -146,18 +143,10 @@ impl Subnet {
|
||||
|
||||
impl PartialEq for Subnet {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.base.cidr == other.base.cidr
|
||||
&& self.base.network_id == other.base.network_id
|
||||
&& self.base.source.discriminant() != EntitySourceDiscriminants::System
|
||||
&& other.base.source.discriminant() != EntitySourceDiscriminants::System)
|
||||
|| self.id == other.id
|
||||
// let sources_match = match (&self.base.source, &other.base.source) {
|
||||
// (SubnetSource::Discovery(daemon_id), SubnetSource::Discovery(other_daemon_id)) => {
|
||||
// daemon_id == other_daemon_id
|
||||
// },
|
||||
// _ => false
|
||||
// };
|
||||
// cidr_match
|
||||
let network_match =
|
||||
self.base.cidr == other.base.cidr && self.base.network_id == other.base.network_id;
|
||||
|
||||
network_match || self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ impl EdgeBuilder {
|
||||
.collect();
|
||||
|
||||
if group_docker_bridges_by_host {
|
||||
// If subnets are grouped, pick an arbitrary
|
||||
// If subnets are grouped, pick an arbitrary subnet ID to use for grouping
|
||||
if let (Some(first_interface_id), Some(first_subnet_id)) = (
|
||||
container_subnet_interface_ids.first(),
|
||||
container_subnets.first(),
|
||||
|
||||
Reference in New Issue
Block a user