summaryrefslogtreecommitdiff
path: root/src/autoscaling.rs
blob: c43f11684869ed756cd012eb03741a6f40956b26 (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
use std::collections::HashSet;

use anyhow::{anyhow, bail, Result};
use aws_sdk_autoscaling as autoscaling;
use aws_sdk_autoscaling::types::{AutoScalingGroup, Instance, LifecycleState};

use crate::ec2::{asg_instances_proposal, Ec2};
use crate::route53::{ResourceRecordSet, Target};

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

pub async fn propose_asg_recordsets<C>(
    aws_context: &C,
    target: &Target,
    asg_name: &str,
) -> Result<HashSet<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 =
        asg_instances_proposal(aws_context, target, asg_name, &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()
}