cloud_terrastodon_entrypoint 0.34.0

Main entrypoint for the Cloud Terrastodon CLI
use cloud_terrastodon_azure::prelude::Scope;
use cloud_terrastodon_azure::prelude::SubscriptionName;
use cloud_terrastodon_azure::prelude::fetch_all_storage_accounts;
use cloud_terrastodon_azure::prelude::fetch_all_subscriptions;
use cloud_terrastodon_command::CommandBuilder;
use cloud_terrastodon_command::CommandKind;
use cloud_terrastodon_user_input::Choice;
use cloud_terrastodon_user_input::PickerTui;
use eyre::Result;
use eyre::bail;
use std::collections::HashMap;
use tokio::join;
use tracing::info;

pub async fn copy_azurerm_backend_menu() -> Result<()> {
    info!("Fetching storage accounts");
    info!("Fetching subscriptions");
    let (storage_accounts, subscriptions) =
        join!(fetch_all_storage_accounts(), fetch_all_subscriptions());
    let storage_accounts = storage_accounts?;
    let subscriptions = subscriptions?
        .into_iter()
        .map(|sub| (sub.id.to_owned(), sub))
        .collect::<HashMap<_, _>>();

    info!("Picking storage account");
    let chosen_storage_account = PickerTui::new()
        .set_header("Picking the storage account for the state file")
        .pick_one(storage_accounts.into_iter().map(|sa| {
            let sub_name = subscriptions
                .get(&sa.id.resource_group_id.subscription_id)
                .map(|sub| sub.name.to_owned())
                .unwrap_or_else(|| SubscriptionName::try_new("Unknown Subscription").unwrap());
            Choice {
                key: format!(
                    "{:<32} {:<64} {}",
                    sub_name.to_string(),
                    sa.id.resource_group_id.resource_group_name.to_string(),
                    sa.name
                ),
                value: (sa, sub_name),
            }
        }))?;

    info!("Fetching blob containers for {}", chosen_storage_account.1);
    let mut cmd = CommandBuilder::new(CommandKind::AzureCLI);
    cmd.args(["storage", "container", "list", "--account-name"]);
    cmd.arg(&*chosen_storage_account.0.name);
    cmd.arg("--subscription");
    cmd.arg(
        chosen_storage_account
            .0
            .id
            .resource_group_id
            .subscription_id
            .short_form(),
    );
    cmd.args(["--query", "[].name", "--output", "json"]);
    let blob_container_names = cmd.run::<Vec<String>>().await?;

    let chosen_blob_container = match blob_container_names.len() {
        0 => {
            bail!("No blob containers found in {}", chosen_storage_account.1);
        }
        1 => blob_container_names.into_iter().next().unwrap(),
        _ => {
            info!("Picking blob container");
            PickerTui::new()
                .set_header("Blob Container Name")
                .pick_one(blob_container_names.into_iter().map(|name| Choice {
                    key: name.clone(),
                    value: name,
                }))?
        }
    };

    let output = format!(
        r#"
    resource_group_name  = "{}"
    storage_account_name = "{}"
    container_name       = "{}"
    subscription_id      = "{}" # {}
    "#,
        chosen_storage_account
            .0
            .id
            .resource_group_id
            .resource_group_name,
        chosen_storage_account.0.name,
        chosen_blob_container,
        chosen_storage_account
            .0
            .id
            .resource_group_id
            .subscription_id
            .short_form(),
        chosen_storage_account.1
    );

    info!("You chose:\n{output}");

    Ok(())
}

#[cfg(test)]
mod test {
    use cloud_terrastodon_azure::prelude::SubscriptionName;

    #[test]
    pub fn it_works() -> eyre::Result<()> {
        SubscriptionName::try_new("Unknown Subscription")?;
        Ok(())
    }
}