diff options
| -rw-r--r-- | src/apply.rs | 8 | ||||
| -rw-r--r-- | src/autoscaling.rs | 6 | ||||
| -rw-r--r-- | src/converge.rs | 23 | ||||
| -rw-r--r-- | src/ec2.rs | 9 | ||||
| -rw-r--r-- | src/hashable.rs | 37 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/route53.rs | 47 |
7 files changed, 58 insertions, 73 deletions
diff --git a/src/apply.rs b/src/apply.rs index ee1824e..e0f3ff2 100644 --- a/src/apply.rs +++ b/src/apply.rs @@ -1,11 +1,11 @@ use std::fmt::Debug; use anyhow::Result; -use aws_sdk_route53::types::{Change, ChangeAction, ChangeBatch, ResourceRecordSet}; +use aws_sdk_route53::types::{Change, ChangeAction, ChangeBatch}; // Needed until try_collect is stable, see <https://github.com/rust-lang/rust/issues/94047> use itertools::Itertools; -use crate::route53::Route53; +use crate::route53::{ResourceRecordSet, Route53}; pub enum ApplyMode { DryRun, @@ -55,13 +55,13 @@ where let remove_records = remove_records.into_iter().map(|record| { Change::builder() .action(ChangeAction::Delete) // <-- - .resource_record_set(record) + .resource_record_set(record.into()) .build() }); let insert_records = insert_records.into_iter().map(|record| { Change::builder() .action(ChangeAction::Create) // <-- - .resource_record_set(record) + .resource_record_set(record.into()) .build() }); diff --git a/src/autoscaling.rs b/src/autoscaling.rs index 79ac2b0..c43f116 100644 --- a/src/autoscaling.rs +++ b/src/autoscaling.rs @@ -3,11 +3,9 @@ use std::collections::HashSet; use anyhow::{anyhow, bail, Result}; use aws_sdk_autoscaling as autoscaling; use aws_sdk_autoscaling::types::{AutoScalingGroup, Instance, LifecycleState}; -use aws_sdk_route53::types::ResourceRecordSet; use crate::ec2::{asg_instances_proposal, Ec2}; -use crate::hashable::Hashable; -use crate::route53::Target; +use crate::route53::{ResourceRecordSet, Target}; pub trait AutoScaling { fn autoscaling(&self) -> &autoscaling::Client; @@ -17,7 +15,7 @@ pub async fn propose_asg_recordsets<C>( aws_context: &C, target: &Target, asg_name: &str, -) -> Result<HashSet<Hashable<ResourceRecordSet>>> +) -> Result<HashSet<ResourceRecordSet>> where C: AutoScaling + Ec2, { diff --git a/src/converge.rs b/src/converge.rs index f0746f9..29c4878 100644 --- a/src/converge.rs +++ b/src/converge.rs @@ -2,13 +2,11 @@ use std::collections::HashSet; use std::fmt::Debug; use anyhow::Result; -use aws_sdk_route53::types::ResourceRecordSet; use futures::try_join; use crate::autoscaling::{propose_asg_recordsets, AutoScaling}; use crate::ec2::Ec2; -use crate::hashable::Hashable; -use crate::route53::{zone_actual_recordsets, zone_for_name, Route53, Target}; +use crate::route53::{zone_actual_recordsets, zone_for_name, ResourceRecordSet, Route53, Target}; #[derive(Debug)] pub struct Changes<T> { @@ -38,23 +36,14 @@ where fn changes_for_records<T>( zone_id: &str, - intended: &HashSet<Hashable<T>>, - actual: &HashSet<Hashable<T>>, + intended: &HashSet<T>, + actual: &HashSet<T>, ) -> Changes<impl IntoIterator<Item = T> + Debug> where - Hashable<T>: Eq + std::hash::Hash, - T: Clone + Debug, + T: std::hash::Hash + Eq + Clone + Debug, { - let remove: Vec<_> = actual - .difference(intended) - .map(Hashable::as_ref) - .cloned() - .collect(); - let insert: Vec<_> = intended - .difference(actual) - .map(Hashable::as_ref) - .cloned() - .collect(); + let remove: Vec<_> = actual.difference(intended).cloned().collect(); + let insert: Vec<_> = intended.difference(actual).cloned().collect(); Changes { zone_id: zone_id.into(), @@ -3,10 +3,9 @@ use std::collections::HashSet; use anyhow::Result; use aws_sdk_ec2 as ec2; use aws_sdk_ec2::types::Filter; -use aws_sdk_route53::types::{ResourceRecordSet, RrType}; +use aws_sdk_route53::types::RrType; -use crate::hashable::Hashable; -use crate::route53::Target; +use crate::route53::{ResourceRecordSet, Target}; pub trait Ec2 { fn ec2(&self) -> &ec2::Client; @@ -17,7 +16,7 @@ pub async fn asg_instances_proposal<C>( target: &Target, asg_name: &str, live_instance_ids: &[String], -) -> Result<HashSet<Hashable<ResourceRecordSet>>> +) -> Result<HashSet<ResourceRecordSet>> where C: Ec2, { @@ -70,6 +69,6 @@ where Ok(ip4_proposal .into_iter() .chain(ip6_proposal.into_iter()) - .map(Hashable::from) + .map(Into::into) .collect()) } diff --git a/src/hashable.rs b/src/hashable.rs deleted file mode 100644 index 22bcdd3..0000000 --- a/src/hashable.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::fmt::Debug; -use std::hash::{Hash, Hasher}; - -use aws_sdk_route53::types::ResourceRecordSet; - -pub trait SimpleHash { - fn hash<H: Hasher>(&self, state: &mut H); -} - -#[derive(Debug, PartialEq, Clone)] -pub struct Hashable<T>(T); - -impl<T> AsRef<T> for Hashable<T> { - fn as_ref(&self) -> &T { - &self.0 - } -} - -impl<T> Eq for Hashable<T> where Hashable<T>: PartialEq {} - -impl<T: SimpleHash> Hash for Hashable<T> { - fn hash<H: Hasher>(&self, state: &mut H) { - self.0.hash(state) - } -} - -impl<T> From<T> for Hashable<T> { - fn from(value: T) -> Self { - Self(value) - } -} - -impl SimpleHash for ResourceRecordSet { - fn hash<H: Hasher>(&self, state: &mut H) { - self.name().hash(state) - } -} @@ -5,5 +5,4 @@ pub mod cli; mod converge; mod dns; mod ec2; -mod hashable; mod route53; diff --git a/src/route53.rs b/src/route53.rs index e63ce4c..74938bf 100644 --- a/src/route53.rs +++ b/src/route53.rs @@ -3,13 +3,12 @@ use std::str::FromStr; use anyhow::{anyhow, Result}; use aws_sdk_route53 as route53; -use aws_sdk_route53::types::{HostedZone, ResourceRecord, ResourceRecordSet, RrType}; +use aws_sdk_route53::types::{HostedZone, ResourceRecord, RrType}; // Needed until try_collect is stable, see <https://github.com/rust-lang/rust/issues/94047> use itertools::Itertools; use trust_dns_proto::rr::Name; use crate::dns; -use crate::hashable::Hashable; pub trait Route53 { fn route53(&self) -> &route53::Client; @@ -55,7 +54,7 @@ pub async fn zone_actual_recordsets<C>( aws_context: &C, zone_id: &str, dns_name: &Name, -) -> Result<HashSet<Hashable<ResourceRecordSet>>> +) -> Result<HashSet<ResourceRecordSet>> where C: Route53, { @@ -130,14 +129,52 @@ impl Target { .map(|address| ResourceRecord::builder().value(address).build()) .try_collect()?; - let record_set = ResourceRecordSet::builder() + let record_set = route53::types::ResourceRecordSet::builder() .name(self.name().to_ascii()) .r#type(rr_type) .ttl(self.ttl) .set_resource_records(Some(records)) - .build()?; + .build()? + .into(); Ok(Some(record_set)) } } } + +// ResourceRecordSet isn't hashable (reasonably enough), but we're going to do set difference on them +// later to identify which records to remove, and which to keep. This wrapper type adds a trivial - but +// probably correct - implementation of Hash and Eq so that ResourceRecordSet instances can be stored +// in hash sets. +#[derive(Clone, Debug, PartialEq)] +pub struct ResourceRecordSet(route53::types::ResourceRecordSet); + +// Equality is based on the observation that ResourceRecordSet already implements partialeq by derive, +// and that none of its fields have any actual partially-equal results. As a result, any two +// ResourceRecordSet instances that are fieldwise-equal are themselves equal under PartialEq. Promote +// this result to Eq. +impl Eq for ResourceRecordSet {} + +// Hash ResourceRecordSet instances by name and address(es). +impl std::hash::Hash for ResourceRecordSet { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.0.name().hash(state); + self.0 + .resource_records() + .iter() + .map(ResourceRecord::value) + .for_each(|val| val.hash(state)); + } +} + +impl From<route53::types::ResourceRecordSet> for ResourceRecordSet { + fn from(recordset: route53::types::ResourceRecordSet) -> Self { + Self(recordset) + } +} + +impl From<ResourceRecordSet> for route53::types::ResourceRecordSet { + fn from(recordset: ResourceRecordSet) -> Self { + recordset.0 + } +} |
