summaryrefslogtreecommitdiff
path: root/src/cli.rs
blob: 340d537cdb03ceab54359bf367d6f579766dc567 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use std::fmt::Debug;

use anyhow::Result;
use clap::Parser;
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;

/// Synchronize a DNS entry with an autoscaling group's running instances.
///
/// The given DNS name's A and AAAA records in Route53 will be rewritten to exactly
/// match the list of pending and in-service EC2 instances in the specified
/// autoscaling group. Records of other types (including CNAMEs) will not be
/// modified, so this can be used alongside DNS ACME verification, SPF, and other
/// DNS applications.
#[derive(Parser, Debug)]
pub struct Args {
    /// The name of the autoscaling group to synchronize.
    #[arg(long)]
    autoscaling_group: String,

    /// The DNS domain name to synchronize. The most specific Route53 zone that
    /// contains this name will be modified.
    #[arg(long)]
    dns_name: Name,

    /// The TTL (in seconds) for newly-created records.
    #[arg(long, default_value_t = 60)]
    dns_ttl: i64,

    /// Print the affected zone ID and pending changes, without applying them (default).
    #[arg(long, conflicts_with = "apply")]
    dry_run: bool,

    /// Apply the changes to Route53.
    #[arg(long)]
    apply: bool,
}

impl Args {
    pub async fn run(self) -> Result<()> {
        let args = Args::parse();

        let aws_context = AwsContext::from_env().await;

        let changes = named_asg_changes(
            &aws_context,
            &args.autoscaling_group,
            &absolute(args.dns_name)?,
            args.dns_ttl,
        )
        .await?;

        self.apply_mode()
            .apply(
                &aws_context,
                &changes.zone_id,
                changes.remove,
                changes.insert,
            )
            .await?;

        Ok(())
    }

    fn apply_mode(&self) -> ApplyMode {
        use ApplyMode::*;
        match (self.dry_run, self.apply) {
            (true, false) => DryRun,
            (false, true) => Apply,
            (false, false) => DryRun,
            (true, true) => unreachable!("Cannot set --apply and --dry-run together"),
        }
    }
}