From 1698009062a8a853ceb036b0a7f9d5d76109e550 Mon Sep 17 00:00:00 2001 From: centra Date: Tue, 31 Mar 2026 11:14:18 +0900 Subject: [PATCH] Simplify DNS publication state and FlashDNS storage --- deployer/crates/deployer-types/src/lib.rs | 53 -------------- .../crates/fleet-scheduler/src/publish.rs | 53 +++++++++----- .../crates/flashdns-server/src/metadata.rs | 69 ++----------------- nix/test-cluster/run-cluster.sh | 10 +-- 4 files changed, 45 insertions(+), 140 deletions(-) diff --git a/deployer/crates/deployer-types/src/lib.rs b/deployer/crates/deployer-types/src/lib.rs index ae5c125..3fafaa8 100644 --- a/deployer/crates/deployer-types/src/lib.rs +++ b/deployer/crates/deployer-types/src/lib.rs @@ -896,14 +896,8 @@ pub struct PublishedLoadBalancerState { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct PublishedDnsRecordState { pub zone_id: String, - #[serde(default)] - pub record_id: String, - #[serde(default)] pub record_ids: Vec, pub fqdn: String, - #[serde(default)] - pub value: String, - #[serde(default)] pub values: Vec, } @@ -921,51 +915,6 @@ pub struct ServicePublicationState { pub observed_at: Option>, } -/// Publication record stored under photoncloud/clusters/{cluster_id}/publications/{service}. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct ServicePublicationRecord { - #[serde(default)] - pub service: String, - #[serde(default)] - pub org_id: Option, - #[serde(default)] - pub project_id: Option, - #[serde(default)] - pub lb_id: Option, - #[serde(default)] - pub pool_id: Option, - #[serde(default)] - pub listener_id: Option, - #[serde(default)] - pub vip: Option, - #[serde(default)] - pub listener_port: Option, - #[serde(default)] - pub listener_protocol: Option, - #[serde(default)] - pub pool_protocol: Option, - #[serde(default)] - pub backend_ids: HashMap, - #[serde(default)] - pub backend_targets: HashMap, - #[serde(default)] - pub dns_zone: Option, - #[serde(default)] - pub dns_name: Option, - #[serde(default)] - pub dns_mode: Option, - #[serde(default)] - pub dns_ttl: Option, - #[serde(default)] - pub zone_id: Option, - #[serde(default)] - pub record_id: Option, - #[serde(default)] - pub fqdn: Option, - #[serde(default)] - pub updated_at: Option>, -} - /// mTLS policy definition. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct MtlsPolicySpec { @@ -1222,10 +1171,8 @@ mod tests { }), dns: Some(PublishedDnsRecordState { zone_id: "zone-1".to_string(), - record_id: "record-1".to_string(), record_ids: vec!["record-1".to_string()], fqdn: "api.test.cluster.local".to_string(), - value: "10.0.0.50".to_string(), values: vec!["10.0.0.50".to_string()], }), observed_at: None, diff --git a/deployer/crates/fleet-scheduler/src/publish.rs b/deployer/crates/fleet-scheduler/src/publish.rs index 9012585..bdec3e5 100644 --- a/deployer/crates/fleet-scheduler/src/publish.rs +++ b/deployer/crates/fleet-scheduler/src/publish.rs @@ -365,7 +365,6 @@ impl PublicationController { let zone_name = normalize_zone_name(&spec.zone); let record_name = record_name_for_service(spec, service); let fqdn = format!("{}.{}", record_name, zone_name); - let primary_value = desired_values.first().cloned().unwrap_or_default(); if self.config.dry_run { info!( @@ -376,10 +375,8 @@ impl PublicationController { ); return Ok(existing.cloned().or(Some(PublishedDnsRecordState { zone_id: String::new(), - record_id: String::new(), record_ids: Vec::new(), fqdn, - value: primary_value, values: desired_values, }))); } @@ -389,10 +386,11 @@ impl PublicationController { let zone = ensure_zone(&mut zone_client, auth_token, &zone_name, org_id, project_id).await?; + let replacing_existing = + existing.filter(|state| !dns_state_matches_target(state, &zone.id, &fqdn)); let records = ensure_records( &mut record_client, auth_token, - existing, &zone.id, &record_name, spec.ttl, @@ -403,13 +401,14 @@ impl PublicationController { .iter() .map(|record| record.id.clone()) .collect::>(); + if let Some(previous) = replacing_existing { + self.cleanup_dns(auth_token, previous).await?; + } Ok(Some(PublishedDnsRecordState { zone_id: zone.id, - record_id: record_ids.first().cloned().unwrap_or_default(), record_ids, fqdn, - value: primary_value, values: desired_values, })) } @@ -424,9 +423,6 @@ impl PublicationController { }; let mut record_client = RecordServiceClient::connect(endpoint.clone()).await?; let mut record_ids = dns_state.record_ids.clone(); - if record_ids.is_empty() && !dns_state.record_id.is_empty() { - record_ids.push(dns_state.record_id.clone()); - } record_ids.sort(); record_ids.dedup(); @@ -837,7 +833,6 @@ async fn ensure_zone( async fn ensure_records( client: &mut RecordServiceClient, auth_token: &str, - existing: Option<&PublishedDnsRecordState>, zone_id: &str, name: &str, ttl: u32, @@ -860,13 +855,7 @@ async fn ensure_records( let mut matching = records .iter() - .filter(|record| { - record.name == name - || existing.map(|state| state.record_id.as_str()) == Some(record.id.as_str()) - || existing - .map(|state| state.record_ids.iter().any(|id| id == &record.id)) - .unwrap_or(false) - }) + .filter(|record| record.name == name) .cloned() .collect::>(); matching.sort_by(|lhs, rhs| { @@ -1007,6 +996,10 @@ fn normalize_dns_values(values: impl IntoIterator) -> Vec values } +fn dns_state_matches_target(state: &PublishedDnsRecordState, zone_id: &str, fqdn: &str) -> bool { + state.zone_id == zone_id && state.fqdn == fqdn +} + fn desired_dns_values( spec: &DnsPublicationSpec, healthy_instances: &[ServiceInstanceSpec], @@ -1324,6 +1317,32 @@ mod tests { ); } + #[test] + fn test_dns_state_match_requires_same_zone_and_fqdn() { + let state = PublishedDnsRecordState { + zone_id: "zone-1".to_string(), + record_ids: vec!["record-1".to_string()], + fqdn: "api.native.cluster.test".to_string(), + values: vec!["10.0.0.11".to_string()], + }; + + assert!(dns_state_matches_target( + &state, + "zone-1", + "api.native.cluster.test" + )); + assert!(!dns_state_matches_target( + &state, + "zone-2", + "api.native.cluster.test" + )); + assert!(!dns_state_matches_target( + &state, + "zone-1", + "web.native.cluster.test" + )); + } + #[test] fn test_publishable_instance_requires_fresh_heartbeat() { let now = Utc::now(); diff --git a/flashdns/crates/flashdns-server/src/metadata.rs b/flashdns/crates/flashdns-server/src/metadata.rs index fb9eff3..d96405e 100644 --- a/flashdns/crates/flashdns-server/src/metadata.rs +++ b/flashdns/crates/flashdns-server/src/metadata.rs @@ -413,20 +413,6 @@ impl DnsMetadataStore { format!("/flashdns/records/{}/", zone_id) } - fn record_type_prefix(zone_id: &ZoneId, record_name: &str, record_type: RecordType) -> String { - format!( - "/flashdns/records/{}/{}/{}/", - zone_id, record_name, record_type - ) - } - - fn legacy_record_key(zone_id: &ZoneId, record_name: &str, record_type: RecordType) -> String { - format!( - "/flashdns/records/{}/{}/{}", - zone_id, record_name, record_type - ) - } - fn record_id_key(record_id: &RecordId) -> String { format!("/flashdns/record_ids/{}", record_id) } @@ -566,37 +552,6 @@ impl DnsMetadataStore { Ok(()) } - /// Load the first record by name and type, preserving compatibility with - /// older single-record keys. - pub async fn load_record( - &self, - zone_id: &ZoneId, - record_name: &str, - record_type: RecordType, - ) -> Result> { - let prefix = Self::record_type_prefix(zone_id, record_name, record_type); - let mut records = self - .get_prefix(&prefix) - .await? - .into_iter() - .filter_map(|(_, value)| serde_json::from_str::(&value).ok()) - .collect::>(); - - if records.is_empty() { - let legacy_key = Self::legacy_record_key(zone_id, record_name, record_type); - if let Some(value) = self.get(&legacy_key).await? { - let record: Record = serde_json::from_str(&value).map_err(|e| { - MetadataError::Serialization(format!("Failed to deserialize record: {}", e)) - })?; - return Ok(Some(record)); - } - return Ok(None); - } - - records.sort_by(|lhs, rhs| lhs.id.to_string().cmp(&rhs.id.to_string())); - Ok(records.into_iter().next()) - } - /// Load record by ID pub async fn load_record_by_id(&self, record_id: &RecordId) -> Result> { let id_key = Self::record_id_key(record_id); @@ -628,12 +583,6 @@ impl DnsMetadataStore { &record.id, )) .await?; - self.delete_key(&Self::legacy_record_key( - &record.zone_id, - &record.name, - record.record_type, - )) - .await?; } self.delete_key(&id_key).await?; @@ -867,25 +816,15 @@ mod tests { // Save store.save_record(&record).await.unwrap(); - // Load - let loaded = store - .load_record(&zone.id, "www", RecordType::A) - .await - .unwrap() - .unwrap(); - assert_eq!(loaded.id, record.id); - // List - let records = store.list_records(&zone.id).await.unwrap(); + let records = store.list_records_by_name(&zone.id, "www").await.unwrap(); assert_eq!(records.len(), 1); + assert_eq!(records[0].id, record.id); // Delete store.delete_record(&record).await.unwrap(); - let deleted = store - .load_record(&zone.id, "www", RecordType::A) - .await - .unwrap(); - assert!(deleted.is_none()); + let deleted = store.list_records_by_name(&zone.id, "www").await.unwrap(); + assert!(deleted.is_empty()); } #[tokio::test] diff --git a/nix/test-cluster/run-cluster.sh b/nix/test-cluster/run-cluster.sh index 4bbe6b2..2fbc53c 100755 --- a/nix/test-cluster/run-cluster.sh +++ b/nix/test-cluster/run-cluster.sh @@ -5079,7 +5079,7 @@ validate_native_runtime_flow() { local daemon_publication_value daemon_publication_fqdn publication_value="$(native_publication_state "native-web")" publication_fqdn="$(printf '%s' "${publication_value}" | jq -r '.dns.fqdn')" - publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.value')" + publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.values[0]')" publication_pool_id="$(printf '%s' "${publication_value}" | jq -r '.load_balancer.pool_id')" daemon_publication_value="$(native_publication_state "native-daemon")" daemon_publication_fqdn="$(printf '%s' "${daemon_publication_value}" | jq -r '.dns.fqdn')" @@ -5143,7 +5143,7 @@ validate_native_runtime_flow() { wait_for_http node01 "http://127.0.0.1:18191/" 240 publication_value="$(native_publication_state "native-web")" publication_pool_id="$(printf '%s' "${publication_value}" | jq -r '.load_balancer.pool_id')" - publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.value')" + publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.values[0]')" daemon_publication_value="$(native_publication_state "native-daemon")" wait_for_native_lb_backends "${publication_pool_id}" "1" 180 10.100.0.22 wait_for_native_dns_record "${publication_fqdn}" "${publication_ip}" 180 @@ -5191,7 +5191,7 @@ validate_native_runtime_flow() { [[ "${restored_container_node}" == "node05" ]] || die "native-container unexpectedly moved after node04 returned to service" publication_value="$(native_publication_state "native-web")" publication_pool_id="$(printf '%s' "${publication_value}" | jq -r '.load_balancer.pool_id')" - publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.value')" + publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.values[0]')" daemon_publication_value="$(native_publication_state "native-daemon")" wait_for_native_lb_backends "${publication_pool_id}" "2" 180 10.100.0.21 10.100.0.22 wait_for_native_dns_record "${publication_fqdn}" "${publication_ip}" 180 @@ -5228,7 +5228,7 @@ validate_native_runtime_flow() { wait_for_native_instance_node "native-daemon" "node04" 240 >/dev/null publication_value="$(native_publication_state "native-web")" publication_pool_id="$(printf '%s' "${publication_value}" | jq -r '.load_balancer.pool_id')" - publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.value')" + publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.values[0]')" daemon_publication_value="$(native_publication_state "native-daemon")" wait_for_native_lb_backends "${publication_pool_id}" "1" 240 10.100.0.21 wait_for_native_dns_record "${publication_fqdn}" "${publication_ip}" 180 @@ -5284,7 +5284,7 @@ validate_native_runtime_flow() { [[ "${recovered_container_node}" == "node04" ]] || die "native-container unexpectedly churned after node05 recovered" publication_value="$(native_publication_state "native-web")" publication_pool_id="$(printf '%s' "${publication_value}" | jq -r '.load_balancer.pool_id')" - publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.value')" + publication_ip="$(printf '%s' "${publication_value}" | jq -r '.dns.values[0]')" daemon_publication_value="$(native_publication_state "native-daemon")" wait_for_native_lb_backends "${publication_pool_id}" "2" 180 10.100.0.21 10.100.0.22 wait_for_native_dns_record "${publication_fqdn}" "${publication_ip}" 180