Skip to content

Commit

Permalink
feat: Make the cloud linter start and end dates configurable
Browse files Browse the repository at this point in the history
Add optional arguments to the cloud-linter command that take a start and
end date in YYYY-MM-DD form. If the start-date isn't provided, it
defaults to end-date - 30. If end-date isn't provided, it defaults to
today.

Make the process resources function argument orders consistent.

Delete some commented out code.
  • Loading branch information
nand4011 committed Aug 1, 2024
1 parent 0f4746d commit fcd2c37
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 58 deletions.
10 changes: 10 additions & 0 deletions momento-cli-opts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,16 @@ to help find opportunities for optimizations with Momento.
default_value = "10"
)]
metric_collection_rate: u32,
#[arg(
long = "start-date",
help = "The UTC start date of the metric collection period. Will use (end-date - 30 days) if not provided. (YYYY-MM-DD)",
)]
metric_start_date: Option<String>,
#[arg(
long = "end-date",
help = "The UTC end date of the metric collection period. Will use the current date if not provided. (YYYY-MM-DD)",
)]
metric_end_date: Option<String>,
},
}

Expand Down
25 changes: 18 additions & 7 deletions momento/src/commands/cloud_linter/api_gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ pub(crate) async fn process_api_gateway_resources(
control_plane_limiter: Arc<DefaultDirectRateLimiter>,
metrics_limiter: Arc<DefaultDirectRateLimiter>,
sender: Sender<Resource>,
metrics_start_millis: i64,
metrics_end_millis: i64,
) -> Result<(), CliError> {
let region = config.region().map(|r| r.as_ref()).ok_or(CliError {
msg: "No region configured for client".to_string(),
Expand Down Expand Up @@ -103,11 +105,13 @@ pub(crate) async fn process_api_gateway_resources(
list_apis_bar.finish();
process_apis(
apig_client.clone(),
&apis,
region,
sender,
&metrics_client,
&metrics_limiter,
region,
metrics_start_millis,
metrics_end_millis,
&apis,
sender,
)
.await?;

Expand All @@ -116,11 +120,13 @@ pub(crate) async fn process_api_gateway_resources(

async fn process_apis(
apig_client: aws_sdk_apigateway::Client,
apis: &[RestApi],
region: &str,
sender: Sender<Resource>,
metrics_client: &aws_sdk_cloudwatch::Client,
metrics_limiter: &Arc<DefaultDirectRateLimiter>,
region: &str,
metrics_start_millis: i64,
metrics_end_millis: i64,
apis: &[RestApi],
sender: Sender<Resource>,
) -> Result<(), CliError> {
let mut resources: Vec<Resource> = Vec::with_capacity(apis.len());
let get_apis_bar =
Expand Down Expand Up @@ -155,7 +161,12 @@ async fn process_apis(
match resource {
Resource::ApiGateway(mut apig_resource) => {
apig_resource
.append_metrics(metrics_client, Arc::clone(metrics_limiter))
.append_metrics(
metrics_client,
Arc::clone(metrics_limiter),
metrics_start_millis,
metrics_end_millis,
)
.await?;
sender
.send(Resource::ApiGateway(apig_resource))
Expand Down
17 changes: 14 additions & 3 deletions momento/src/commands/cloud_linter/dynamodb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ pub(crate) async fn process_ddb_resources(
metrics_limiter: Arc<DefaultDirectRateLimiter>,
describe_ttl_limiter: Arc<DefaultDirectRateLimiter>,
sender: Sender<Resource>,
metrics_start_millis: i64,
metrics_end_millis: i64,
enable_ddb_ttl_check: bool,
enable_gsi: bool,
) -> Result<(), CliError> {
Expand Down Expand Up @@ -249,13 +251,15 @@ pub(crate) async fn process_ddb_resources(
let res = process_table_resources(
&ddb_client_clone,
&metrics_client_clone,
&table_name_clone,
control_plane_limiter_clone,
metrics_limiter_clone,
describe_ttl_limiter_clone,
sender_clone,
metrics_start_millis,
metrics_end_millis,
enable_ddb_ttl_check,
enable_gsi,
&table_name_clone,
)
.await;
progress_bar_clone.inc(1);
Expand Down Expand Up @@ -361,13 +365,15 @@ async fn is_ddb_ttl_enabled(
async fn process_table_resources(
ddb_client: &aws_sdk_dynamodb::Client,
metrics_client: &aws_sdk_cloudwatch::Client,
table_name: &str,
control_plane_limiter: Arc<DefaultDirectRateLimiter>,
metrics_limiter: Arc<DefaultDirectRateLimiter>,
describe_ttl_limiter: Arc<DefaultDirectRateLimiter>,
sender: Sender<Resource>,
metrics_start_millis: i64,
metrics_end_millis: i64,
enable_ddb_ttl_check: bool,
enable_gsi: bool,
table_name: &str,
) -> Result<(), CliError> {
let region = ddb_client
.config()
Expand Down Expand Up @@ -537,7 +543,12 @@ async fn process_table_resources(
continue;
}
resource
.append_metrics(metrics_client, Arc::clone(&metrics_limiter))
.append_metrics(
metrics_client,
Arc::clone(&metrics_limiter),
metrics_start_millis,
metrics_end_millis,
)
.await?;
let ttl_enabled = match enable_ddb_ttl_check {
true => {
Expand Down
17 changes: 14 additions & 3 deletions momento/src/commands/cloud_linter/elasticache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ pub(crate) async fn process_elasticache_resources(
control_plane_limiter: Arc<DefaultDirectRateLimiter>,
metrics_limiter: Arc<DefaultDirectRateLimiter>,
sender: Sender<Resource>,
metrics_start_millis: i64,
metrics_end_millis: i64,
) -> Result<(), CliError> {
let region = config.region().map(|r| r.as_ref()).ok_or(CliError {
msg: "No region configured for client".to_string(),
Expand All @@ -123,8 +125,10 @@ pub(crate) async fn process_elasticache_resources(
&metrics_client,
control_plane_limiter,
metrics_limiter,
region,
sender,
region,
metrics_start_millis,
metrics_end_millis,
)
.await?;

Expand All @@ -136,8 +140,10 @@ async fn process_resources(
metrics_client: &aws_sdk_cloudwatch::Client,
control_plane_limiter: Arc<DefaultDirectRateLimiter>,
metrics_limiter: Arc<DefaultDirectRateLimiter>,
region: &str,
sender: Sender<Resource>,
region: &str,
metrics_start_millis: i64,
metrics_end_millis: i64,
) -> Result<(), CliError> {
let describe_bar = ProgressBar::new_spinner().with_message("Listing ElastiCache resources");
describe_bar.enable_steady_tick(Duration::from_millis(100));
Expand Down Expand Up @@ -165,7 +171,12 @@ async fn process_resources(

futures.push(tokio::spawn(async move {
resource
.append_metrics(&metrics_client_clone, metrics_limiter_clone)
.append_metrics(
&metrics_client_clone,
metrics_limiter_clone,
metrics_start_millis,
metrics_end_millis,
)
.await?;

let wrapped_resource = Resource::ElastiCache(resource);
Expand Down
83 changes: 83 additions & 0 deletions momento/src/commands/cloud_linter/linter_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::time::Duration;
use crate::commands::cloud_linter::api_gateway::process_api_gateway_resources;
use aws_config::retry::RetryConfig;
use aws_config::{BehaviorVersion, Region};
use chrono::{NaiveDate, NaiveDateTime, Utc};
use flate2::write::GzEncoder;
use flate2::Compression;
use governor::{Quota, RateLimiter};
Expand All @@ -30,12 +31,16 @@ pub async fn run_cloud_linter(
enable_gsi: bool,
only_collect_for_resource: Option<CloudLinterResources>,
metric_collection_rate: u32,
start_date: Option<String>,
end_date: Option<String>,
) -> Result<(), CliError> {
let (tx, mut rx) = mpsc::channel::<Resource>(32);
let file_path = "linter_results.json";
// first we check to make sure we have perms to write files to the current directory
check_output_is_writable(file_path).await?;

let (metric_start_time, metric_end_time) = get_metric_time_range(start_date, end_date)?;

// here we write the unzipped json file, containing all the linter results
let unzipped_tokio_file = File::create(file_path).await?;
let mut unzipped_file = unzipped_tokio_file.into_std().await;
Expand All @@ -51,6 +56,8 @@ pub async fn run_cloud_linter(
enable_gsi,
only_collect_for_resource,
metric_collection_rate,
metric_start_time,
metric_end_time,
)
.await;
});
Expand Down Expand Up @@ -87,6 +94,8 @@ async fn process_data(
enable_gsi: bool,
only_collect_for_resource: Option<CloudLinterResources>,
metric_collection_rate: u32,
metrics_start_millis: i64,
metrics_end_millis: i64,
) -> Result<(), CliError> {
let retry_config = RetryConfig::adaptive()
.with_initial_backoff(Duration::from_millis(250))
Expand Down Expand Up @@ -122,6 +131,8 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;
Ok(())
Expand All @@ -132,6 +143,8 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;
Ok(())
Expand All @@ -143,6 +156,8 @@ async fn process_data(
Arc::clone(&metrics_limiter),
Arc::clone(&describe_ttl_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
enable_ddb_ttl_check,
enable_gsi,
)
Expand All @@ -155,6 +170,8 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;

Expand All @@ -163,6 +180,8 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;
Ok(())
Expand All @@ -175,6 +194,8 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;

Expand All @@ -183,6 +204,8 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;

Expand All @@ -192,6 +215,8 @@ async fn process_data(
Arc::clone(&metrics_limiter),
Arc::clone(&describe_ttl_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
enable_ddb_ttl_check,
enable_gsi,
)
Expand All @@ -202,6 +227,8 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;

Expand All @@ -210,12 +237,68 @@ async fn process_data(
Arc::clone(&control_plane_limiter),
Arc::clone(&metrics_limiter),
sender.clone(),
metrics_start_millis,
metrics_end_millis,
)
.await?;

Ok(())
}

/// Calculates a metric time range based on optional start and end dates.
/// If start_date is not provided, it defaults to end_date - 30 days.
/// If end_date is not provided, it defaults to now.
///
/// # Arguments
///
/// * `start_date` - An optional String representing the start date in "YYYY-MM-DD" format.
/// * `end_date` - An optional String representing the end date in "YYYY-MM-DD" format.
///
/// # Returns
///
/// A Result containing a tuple of the start and end timestamps in millis, or a CliError
/// if date parsing fails.
fn get_metric_time_range(
start_date: Option<String>,
end_date: Option<String>,
) -> Result<(i64, i64), CliError> {
let now = Utc::now();
let thirty_days = chrono::Duration::days(30);

let parse_date = |date_str: &str| -> Result<NaiveDateTime, CliError> {
let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
date.and_hms_opt(0, 0, 0).ok_or_else(|| CliError {
msg: "invalid time".to_string(),
})
};

match (start_date.as_deref(), end_date.as_deref()) {
(Some(s), Some(e)) => {
let start = parse_date(s)?;
let end = parse_date(e)?;
Ok((start.timestamp_millis(), end.timestamp_millis()))
}
(Some(s), None) => {
let start = parse_date(s)?;
Ok((
start.timestamp_millis(),
now.timestamp_millis(),
))
}
(None, Some(e)) => {
let end = parse_date(e)?;
Ok((
(end - thirty_days).timestamp_millis(),
end.timestamp_millis(),
))
}
(None, None) => Ok((
(now - thirty_days).timestamp_millis(),
now.timestamp_millis(),
)),
}
}

async fn check_output_is_writable(file_path: &str) -> Result<(), CliError> {
let path = Path::new(file_path);

Expand Down
Loading

0 comments on commit fcd2c37

Please sign in to comment.