diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-07-25 20:01:46 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-07-25 20:01:46 -0400 |
| commit | 1754b64db14a1ea409779738a84d020fbb1ac79b (patch) | |
| tree | 8b41783dd178884492529b76c1976658dac497b5 | |
| parent | a2e2df7c0f97633bba3a91981c4c1598a062aaac (diff) | |
Unify dns_name and dns_ttl into a "target" type, pass that around.
| -rw-r--r-- | src/autoscaling.rs | 9 | ||||
| -rw-r--r-- | src/cli.rs | 16 | ||||
| -rw-r--r-- | src/converge.rs | 12 | ||||
| -rw-r--r-- | src/ec2.rs | 44 | ||||
| -rw-r--r-- | src/route53.rs | 51 |
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) } @@ -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); @@ -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)) + } + } +} |
