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
use aws_sdk_cloudwatchlogs::error::SdkError;
use aws_sdk_cloudwatchlogs::operation::describe_log_groups::DescribeLogGroupsError;
use aws_sdk_cloudwatchlogs::Client as CloudwatchLogsClient;
use snafu::Snafu;

use crate::sinks::aws_cloudwatch_logs::config::CloudwatchLogsSinkConfig;

#[allow(clippy::large_enum_variant)]
#[derive(Debug, Snafu)]
enum HealthcheckError {
    #[snafu(display("DescribeLogGroups failed: {}", source))]
    DescribeLogGroupsFailed {
        source: SdkError<DescribeLogGroupsError>,
    },
    #[snafu(display("No log group found"))]
    NoLogGroup,
    #[snafu(display("Unable to extract group name"))]
    GroupNameError,
    #[snafu(display("Group name mismatch: expected {}, found {}", expected, name))]
    GroupNameMismatch { expected: String, name: String },
}

pub async fn healthcheck(
    config: CloudwatchLogsSinkConfig,
    client: CloudwatchLogsClient,
) -> crate::Result<()> {
    let group_name = config.group_name.get_ref().to_owned();
    let expected_group_name = group_name.clone();

    // This will attempt to find the group name passed in and verify that
    // it matches the one that AWS sends back.
    let result = client
        .describe_log_groups()
        .limit(1)
        .log_group_name_prefix(group_name)
        .send()
        .await;

    match result {
        Ok(resp) => match resp.log_groups.and_then(|g| g.into_iter().next()) {
            Some(group) => {
                if let Some(name) = group.log_group_name {
                    if name == expected_group_name {
                        Ok(())
                    } else {
                        Err(HealthcheckError::GroupNameMismatch {
                            expected: expected_group_name,
                            name,
                        }
                        .into())
                    }
                } else {
                    Err(HealthcheckError::GroupNameError.into())
                }
            }
            None => {
                if config.group_name.is_dynamic() {
                    info!("Skipping healthcheck log group check: `group_name` is dynamic.");
                    Ok(())
                } else if config.create_missing_group {
                    info!("Skipping healthcheck log group check: `group_name` will be created if missing.");
                    Ok(())
                } else {
                    Err(HealthcheckError::NoLogGroup.into())
                }
            }
        },
        Err(source) => Err(HealthcheckError::DescribeLogGroupsFailed { source }.into()),
    }
}