cloud_scanner_cli/
lib.rs

1//!  # cloud_scanner_cli
2//!
3//!  A module that returns an estimation of environmental impact of the resources used in a cloud account.
4//!
5//! It performs inventory of resources of the account and combines it with Boavizta API to return impact data.
6//!
7
8use crate::model::{load_inventory_from_file, EstimatedInventory};
9use aws_cloud_provider::*;
10use boavizta_api_v1::*;
11use cloud_provider::*;
12use impact_provider::ImpactProvider;
13use impact_provider::ImpactsSummary;
14use metric_exporter::*;
15use std::path::Path;
16
17#[macro_use]
18extern crate rocket;
19
20#[macro_use]
21extern crate log;
22use model::Inventory;
23use pkg_version::*;
24
25pub mod aws_cloud_provider;
26pub mod boavizta_api_v1;
27pub mod cloud_provider;
28pub mod estimated_inventory_exporter;
29pub mod impact_provider;
30pub mod inventory_exporter;
31pub mod metric_exporter;
32pub mod model;
33pub mod standalone_server;
34pub mod usage_location;
35
36use crate::estimated_inventory_exporter::build_impact_summary;
37use anyhow::{Context, Result};
38
39/// Returns estimated impacts as the result of a live inventory.
40pub async fn estimate_impacts(
41    use_duration_hours: &f32,
42    tags: &[String],
43    aws_region: &str,
44    api_url: &str,
45    verbose: bool,
46    include_block_storage: bool,
47) -> Result<EstimatedInventory> {
48    let aws_provider: AwsCloudProvider = AwsCloudProvider::new(aws_region).await?;
49    let inventory: Inventory = aws_provider
50        .list_resources(tags, include_block_storage)
51        .await
52        .context("Cannot perform resources inventory")?;
53
54    let api: BoaviztaApiV1 = BoaviztaApiV1::new(api_url);
55    let estimated_inventory = api
56        .get_impacts(inventory, use_duration_hours, verbose)
57        .await
58        .context("Failure while retrieving impacts")?;
59
60    Ok(estimated_inventory)
61}
62
63/// Returns impacts for and existing inventory file
64pub async fn estimate_impacts_of_inventory_file(
65    use_duration_hours: &f32,
66    api_url: &str,
67    verbose: bool,
68    inventory_file: &Path,
69) -> Result<EstimatedInventory> {
70    let inventory = load_inventory_from_file(inventory_file)
71        .await
72        .context("Failed to load inventory file.")?;
73    let estimated_inventory =
74        estimate_impacts_of_inventory(use_duration_hours, api_url, verbose, inventory)
75            .await
76            .context("Cannot get estimations")?;
77    Ok(estimated_inventory)
78}
79
80/// Returns impacts for and existing inventory
81pub async fn estimate_impacts_of_inventory(
82    use_duration_hours: &f32,
83    api_url: &str,
84    verbose: bool,
85    inventory: Inventory,
86) -> Result<EstimatedInventory> {
87    let api: BoaviztaApiV1 = BoaviztaApiV1::new(api_url);
88    let estimated_inventory = api
89        .get_impacts(inventory, use_duration_hours, verbose)
90        .await
91        .context("Failure while retrieving impacts")?;
92
93    Ok(estimated_inventory)
94}
95
96/// Returns impacts of a live inventory as metrics
97pub async fn get_impacts_as_metrics(
98    use_duration_hours: &f32,
99    tags: &[String],
100    aws_region: &str,
101    api_url: &str,
102    include_storage: bool,
103) -> Result<String> {
104    let resources_with_impacts = estimate_impacts(
105        use_duration_hours,
106        tags,
107        aws_region,
108        api_url,
109        false,
110        include_storage,
111    )
112    .await
113    .context("Cannot perform standard scan")?;
114
115    let summary: ImpactsSummary =
116        build_impact_summary(&resources_with_impacts, aws_region, use_duration_hours).await?;
117    debug!("Summary: {:#?}", summary);
118
119    let all_metrics = get_all_metrics(&summary, resources_with_impacts).with_context(|| {
120        format!(
121            "Unable to get resource impacts as metrics for region {}",
122            aws_region
123        )
124    })?;
125
126    Ok(all_metrics)
127}
128
129/// Returns a live inventory of cloud resources
130pub async fn get_inventory(
131    tags: &[String],
132    aws_region: &str,
133    include_block_storage: bool,
134) -> Result<Inventory> {
135    let aws_inventory: AwsCloudProvider = AwsCloudProvider::new(aws_region).await?;
136    let inventory: Inventory = aws_inventory
137        .list_resources(tags, include_block_storage)
138        .await
139        .context("Cannot perform inventory.")?;
140    Ok(inventory)
141}
142
143/// Starts a server that exposes metrics http like <http://localhost:8000/metrics?aws-region=eu-west-1>
144pub async fn serve_metrics(api_url: &str) -> Result<()> {
145    let config = standalone_server::Config {
146        boavizta_url: api_url.to_string(),
147    };
148    warn!("Starting server.");
149    standalone_server::run(config).await?;
150    Ok(())
151}
152/// Return current version of the cloud-scanner-cli crate
153pub fn get_version() -> String {
154    const MAJOR: u32 = pkg_version_major!();
155    const MINOR: u32 = pkg_version_minor!();
156    const PATCH: u32 = pkg_version_patch!();
157    format!("{}.{}.{}", MAJOR, MINOR, PATCH)
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use crate::model::EstimationMetadata;
164
165    #[tokio::test]
166    async fn summary_has_to_contain_a_usage_duration() {
167        use crate::impact_provider::CloudResourceWithImpacts;
168
169        let resources: Vec<CloudResourceWithImpacts> = Vec::new();
170
171        let resources_with_impacts: EstimatedInventory = EstimatedInventory {
172            impacting_resources: resources,
173            metadata: EstimationMetadata {
174                description: None,
175                boavizta_api_version: Some("v1.2.3".to_owned()),
176                cloud_scanner_version: Some("acb".to_owned()),
177                estimation_date: None,
178                execution_statistics: None,
179            },
180        };
181
182        let usage_duration_hours = 1.5;
183
184        let summary: ImpactsSummary = ImpactsSummary::new(
185            String::from("eu-west-1"),
186            String::from("IRL"),
187            &resources_with_impacts,
188            usage_duration_hours,
189        );
190
191        assert_eq!(
192            summary.duration_of_use_hours, usage_duration_hours,
193            "Duration of summary should match"
194        );
195    }
196}