summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-07-25 20:01:46 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-07-25 20:01:46 -0400
commit1754b64db14a1ea409779738a84d020fbb1ac79b (patch)
tree8b41783dd178884492529b76c1976658dac497b5
parenta2e2df7c0f97633bba3a91981c4c1598a062aaac (diff)
Unify dns_name and dns_ttl into a "target" type, pass that around.
-rw-r--r--src/autoscaling.rs9
-rw-r--r--src/cli.rs16
-rw-r--r--src/converge.rs12
-rw-r--r--src/ec2.rs44
-rw-r--r--src/route53.rs51
5 files changed, 70 insertions, 62 deletions
diff --git a/src/autoscaling.rs b/src/autoscaling.rs
index 024b556..79ac2b0 100644
--- a/src/autoscaling.rs
+++ b/src/autoscaling.rs
@@ -4,10 +4,10 @@ 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 trust_dns_proto::rr::Name;
-use crate::ec2::{instance_proposal, Ec2};
+use crate::ec2::{asg_instances_proposal, Ec2};
use crate::hashable::Hashable;
+use crate::route53::Target;
pub trait AutoScaling {
fn autoscaling(&self) -> &autoscaling::Client;
@@ -15,9 +15,8 @@ pub trait AutoScaling {
pub async fn propose_asg_recordsets<C>(
aws_context: &C,
+ target: &Target,
asg_name: &str,
- dns_name: &Name,
- dns_ttl: i64,
) -> Result<HashSet<Hashable<ResourceRecordSet>>>
where
C: AutoScaling + Ec2,
@@ -28,7 +27,7 @@ where
.ok_or(anyhow!("Autoscaling group returned from AWS with no name"))?;
let live_instance_ids = live_instance_ids(asg.instances());
let proposed =
- instance_proposal(aws_context, asg_name, dns_name, dns_ttl, &live_instance_ids).await?;
+ asg_instances_proposal(aws_context, target, asg_name, &live_instance_ids).await?;
Ok(proposed)
}
diff --git a/src/cli.rs b/src/cli.rs
index 340d537..7a8d2c7 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -7,7 +7,7 @@ use trust_dns_proto::rr::Name;
use crate::apply::ApplyMode;
use crate::aws_context::AwsContext;
use crate::converge::named_asg_changes;
-use crate::dns::absolute;
+use crate::route53::Target;
/// Synchronize a DNS entry with an autoscaling group's running instances.
///
@@ -45,14 +45,9 @@ impl Args {
let args = Args::parse();
let aws_context = AwsContext::from_env().await;
+ let target = self.target()?;
- let changes = named_asg_changes(
- &aws_context,
- &args.autoscaling_group,
- &absolute(args.dns_name)?,
- args.dns_ttl,
- )
- .await?;
+ let changes = named_asg_changes(&aws_context, &target, &args.autoscaling_group).await?;
self.apply_mode()
.apply(
@@ -75,4 +70,9 @@ impl Args {
(true, true) => unreachable!("Cannot set --apply and --dry-run together"),
}
}
+
+ fn target(&self) -> Result<Target> {
+ let target = Target::new(&self.dns_name, self.dns_ttl)?;
+ Ok(target)
+ }
}
diff --git a/src/converge.rs b/src/converge.rs
index d25a82b..f0746f9 100644
--- a/src/converge.rs
+++ b/src/converge.rs
@@ -4,12 +4,11 @@ use std::fmt::Debug;
use anyhow::Result;
use aws_sdk_route53::types::ResourceRecordSet;
use futures::try_join;
-use trust_dns_proto::rr::Name;
use crate::autoscaling::{propose_asg_recordsets, AutoScaling};
use crate::ec2::Ec2;
use crate::hashable::Hashable;
-use crate::route53::{zone_actual_recordsets, zone_for_domain, Route53};
+use crate::route53::{zone_actual_recordsets, zone_for_name, Route53, Target};
#[derive(Debug)]
pub struct Changes<T> {
@@ -20,18 +19,17 @@ pub struct Changes<T> {
pub async fn named_asg_changes<C>(
aws_context: &C,
+ target: &Target,
asg_name: &str,
- dns_name: &Name,
- dns_ttl: i64,
) -> Result<Changes<impl IntoIterator<Item = ResourceRecordSet> + Debug>>
where
C: AutoScaling + Ec2 + Route53,
{
- let zone = zone_for_domain(aws_context, dns_name).await?;
+ let zone = zone_for_name(aws_context, target.name()).await?;
let (proposed, actual) = try_join!(
- propose_asg_recordsets(aws_context, asg_name, dns_name, dns_ttl),
- zone_actual_recordsets(aws_context, &zone.id, dns_name),
+ propose_asg_recordsets(aws_context, target, asg_name),
+ zone_actual_recordsets(aws_context, &zone.id, target.name()),
)?;
let changes = changes_for_records(&zone.id, &proposed, &actual);
diff --git a/src/ec2.rs b/src/ec2.rs
index 998b98b..0457643 100644
--- a/src/ec2.rs
+++ b/src/ec2.rs
@@ -3,29 +3,24 @@ use std::collections::HashSet;
use anyhow::Result;
use aws_sdk_ec2 as ec2;
use aws_sdk_ec2::types::Filter;
-use aws_sdk_route53::types::{ResourceRecord, ResourceRecordSet, 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 aws_sdk_route53::types::{ResourceRecordSet, RrType};
use crate::hashable::Hashable;
+use crate::route53::Target;
pub trait Ec2 {
fn ec2(&self) -> &ec2::Client;
}
-pub async fn instance_proposal<C>(
+pub async fn asg_instances_proposal<C>(
aws_context: &C,
+ target: &Target,
asg_name: &str,
- dns_name: &Name,
- dns_ttl: i64,
live_instance_ids: &[String],
) -> Result<HashSet<Hashable<ResourceRecordSet>>>
where
C: Ec2,
{
- assert!(dns_name.is_fqdn());
-
// If there's nothing running, then (a) we don't need to ask AWS about
// running instances, and (b) we can't anyways as the API call requires at
// least one instance ID. Abort here.
@@ -69,9 +64,8 @@ where
}
}
- let dns_name = dns_name.to_ascii();
- let ip4_proposal = host_proposal(&dns_name, dns_ttl, RrType::A, ip4)?;
- let ip6_proposal = host_proposal(&dns_name, dns_ttl, RrType::Aaaa, ip6)?;
+ let ip4_proposal = target.host_proposal(RrType::A, ip4)?;
+ let ip6_proposal = target.host_proposal(RrType::Aaaa, ip6)?;
Ok(ip4_proposal
.into_iter()
@@ -79,29 +73,3 @@ where
.map(Hashable::from)
.collect())
}
-
-fn host_proposal(
- dns_name: &str,
- dns_ttl: i64,
- rr_type: RrType,
- addresses: HashSet<impl Into<String>>,
-) -> Result<Option<ResourceRecordSet>> {
- if addresses.is_empty() {
- Ok(None)
- } else {
- let records = addresses
- .into_iter()
- .map(|address| address.into())
- .map(|address| ResourceRecord::builder().value(address).build())
- .try_collect()?;
-
- let record_set = ResourceRecordSet::builder()
- .name(dns_name)
- .r#type(rr_type)
- .ttl(dns_ttl)
- .set_resource_records(Some(records))
- .build()?;
-
- Ok(Some(record_set))
- }
-}
diff --git a/src/route53.rs b/src/route53.rs
index e4379af..e63ce4c 100644
--- a/src/route53.rs
+++ b/src/route53.rs
@@ -3,21 +3,23 @@ use std::str::FromStr;
use anyhow::{anyhow, Result};
use aws_sdk_route53 as route53;
-use aws_sdk_route53::types::{HostedZone, ResourceRecordSet, RrType};
+use aws_sdk_route53::types::{HostedZone, ResourceRecord, ResourceRecordSet, 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::suffixes;
+use crate::dns;
use crate::hashable::Hashable;
pub trait Route53 {
fn route53(&self) -> &route53::Client;
}
-pub async fn zone_for_domain<C>(aws_context: &C, name: &Name) -> Result<HostedZone>
+pub async fn zone_for_name<C>(aws_context: &C, name: &Name) -> Result<HostedZone>
where
C: Route53,
{
- let names = suffixes(name.clone());
+ let names = dns::suffixes(name.clone());
let mut zone = None;
let mut depth = None;
@@ -98,3 +100,44 @@ where
Ok(suffix_records)
}
+
+pub struct Target {
+ name: Name,
+ ttl: i64,
+}
+
+impl Target {
+ pub fn new(name: &Name, ttl: i64) -> Result<Self> {
+ let name = dns::absolute(name.to_owned())?;
+ Ok(Self { name, ttl })
+ }
+
+ pub fn name(&self) -> &Name {
+ &self.name
+ }
+
+ pub fn host_proposal(
+ &self,
+ rr_type: RrType,
+ addresses: HashSet<impl Into<String>>,
+ ) -> Result<Option<ResourceRecordSet>> {
+ if addresses.is_empty() {
+ Ok(None)
+ } else {
+ let records = addresses
+ .into_iter()
+ .map(|address| address.into())
+ .map(|address| ResourceRecord::builder().value(address).build())
+ .try_collect()?;
+
+ let record_set = ResourceRecordSet::builder()
+ .name(self.name().to_ascii())
+ .r#type(rr_type)
+ .ttl(self.ttl)
+ .set_resource_records(Some(records))
+ .build()?;
+
+ Ok(Some(record_set))
+ }
+ }
+}