summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/apply.rs8
-rw-r--r--src/autoscaling.rs6
-rw-r--r--src/converge.rs23
-rw-r--r--src/ec2.rs9
-rw-r--r--src/hashable.rs37
-rw-r--r--src/lib.rs1
-rw-r--r--src/route53.rs47
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(),
diff --git a/src/ec2.rs b/src/ec2.rs
index 0457643..d4b7afd 100644
--- a/src/ec2.rs
+++ b/src/ec2.rs
@@ -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)
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 178033d..3e00064 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
+ }
+}