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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
pub(super) mod rotate_secrets;

use crate::{
    commands::{app::util::AppIdentOpts, AsyncCliCommand},
    config::WasmerEnv,
};

/// Retrieve access credentials for the volumes of an app.
///
/// The credentials can be used to access volumes with any S3 client, for example rclone. Note:
/// using --format=rclone - which is the default - will output an rclone configuration snippet.
#[derive(clap::Parser, Debug)]
pub struct CmdAppVolumesCredentials {
    #[clap(flatten)]
    pub env: WasmerEnv,

    #[clap(flatten)]
    pub fmt: ItemFormatOpts,

    #[clap(flatten)]
    pub ident: AppIdentOpts,
}

#[async_trait::async_trait]
impl AsyncCliCommand for CmdAppVolumesCredentials {
    type Output = ();

    async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
        let client = self.env.client()?;
        let (_ident, app) = self.ident.load_app(&client).await?;

        let creds =
            wasmer_backend_api::query::get_app_s3_credentials(&client, app.id.clone().into_inner())
                .await?;

        match self.fmt.format {
            CredsItemFormat::Rclone => {
                let rclone_config = format!(
                    r#"
[edge-{app_name}]
# rclone configuration for volumes of {app_name}
type = s3
provider = Other
acl = private
access_key_id = {access_key}
secret_access_key = {secret_key}
endpoint = {endpoint}

"#,
                    app_name = app.name,
                    access_key = creds.access_key,
                    secret_key = creds.secret_key,
                    endpoint = creds.endpoint,
                );
                println!("{rclone_config}");
            }
            CredsItemFormat::Json => {
                let value = serde_json::json!({
                    "app_name": app.name,
                    "access_key": creds.access_key,
                    "secret_key": creds.secret_key,
                    "endpoint": creds.endpoint,
                });

                println!("{}", serde_json::to_string_pretty(&value).unwrap());
            }
            CredsItemFormat::Yaml => {
                let config = format!(
                    r#"
app_name: {app_name}
access_key: {access_key}
secret_key: {secret_key}
endpoint: {endpoint}
"#,
                    app_name = app.name,
                    access_key = creds.access_key,
                    secret_key = creds.secret_key,
                    endpoint = creds.endpoint,
                );
                println!("{config}");
            }

            CredsItemFormat::Table => {
                let mut table = comfy_table::Table::new();
                table.add_row(vec!["App name", "Access key", "Secret key", "Endpoint"]);
                table.add_row(vec![
                    app.name,
                    creds.access_key,
                    creds.secret_key,
                    creds.endpoint,
                ]);

                println!("{table}");
            }
        }

        Ok(())
    }
}

/*
 * The following is a copy of [`crate::opts::ItemFormatOpts`] with an
 * additional formatting - rclone - that only makes sense in this context.
 */

/// The possibile formats to output the credentials in.
#[derive(Clone, Copy, Debug, PartialEq, Eq, clap::ValueEnum)]
pub enum CredsItemFormat {
    Json,
    Yaml,
    Table,
    Rclone,
}

/// Formatting options for credentials.
#[derive(clap::Parser, Debug)]
pub struct ItemFormatOpts {
    /// Output format
    #[clap(short = 'f', long, default_value = "rclone")]
    pub format: CredsItemFormat,
}