summaryrefslogtreecommitdiff
path: root/src/autoscaling.rs
blob: 024b5564b6c1e6b11d46b8549f574cbe88059e82 (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
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 trust_dns_proto::rr::Name;

use crate::ec2::{instance_proposal, Ec2};
use crate::hashable::Hashable;

pub trait AutoScaling {
    fn autoscaling(&self) -> &autoscaling::Client;
}

pub async fn propose_asg_recordsets<C>(
    aws_context: &C,
    asg_name: &str,
    dns_name: &Name,
    dns_ttl: i64,
) -> Result<HashSet<Hashable<ResourceRecordSet>>>
where
    C: AutoScaling + Ec2,
{
    let asg = asg_by_name(aws_context, asg_name).await?;
    let asg_name = asg
        .auto_scaling_group_name()
        .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?;

    Ok(proposed)
}

async fn asg_by_name<C>(aws_context: &C, name: &str) -> Result<AutoScalingGroup>
where
    C: AutoScaling,
{
    let groups_resp = aws_context
        .autoscaling()
        .describe_auto_scaling_groups()
        .auto_scaling_group_names(name)
        .send()
        .await?;

    let mut groups = groups_resp.auto_scaling_groups().iter();
    let group = match (groups.next(), groups.next()) {
        (Some(group), None) => group,
        (None, _) => bail!("No autoscaling group found with name: {name}"),
        (Some(_), Some(_)) => bail!("Multiple autoscaling groups found with name: {name}"),
    };

    Ok(group.to_owned())
}

fn live_instance_ids<'a>(instances: impl IntoIterator<Item = &'a Instance>) -> Vec<String> {
    use LifecycleState::*;
    instances
        .into_iter()
        .filter(|instance| match instance.lifecycle_state() {
            // See <https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroupLifecycle.html>
            //
            // Include pending instances so that they can obtain certs, etc.
            Some(Pending) => true,
            Some(PendingWait) => true,
            Some(PendingProceed) => true,
            Some(InService) => true,
            _ => false,
        })
        .flat_map(|instance| instance.instance_id())
        .map(ToOwned::to_owned)
        .collect()
}