wasmer_cli/commands/app/volumes/
enable_s3.rs

1use super::select_volume_by_selector;
2use crate::{
3    commands::{AsyncCliCommand, app::util::AppIdentOpts},
4    config::WasmerEnv,
5};
6
7/// Enable (or disable) the S3 endpoint for an app's volumes.
8///
9/// Enabling S3 provisions per-volume S3 credentials; retrieve them with
10/// `wasmer app volume credentials` and rotate them with `wasmer app volume
11/// rotate-secrets`.
12#[derive(clap::Parser, Debug)]
13pub struct CmdAppVolumesEnableS3 {
14    #[clap(flatten)]
15    pub env: WasmerEnv,
16
17    #[clap(flatten)]
18    pub ident: AppIdentOpts,
19
20    /// Only change this volume, by name, mount path, or volume id.
21    /// Without it, every volume of the app is affected.
22    #[clap(long)]
23    pub volume: Option<String>,
24
25    /// Disable S3 instead of enabling it.
26    #[clap(long)]
27    pub disable: bool,
28}
29
30#[async_trait::async_trait]
31impl AsyncCliCommand for CmdAppVolumesEnableS3 {
32    type Output = ();
33
34    async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
35        let client = self.env.client()?;
36        let (_ident, app) = self.ident.load_app(&client).await?;
37
38        let volumes = super::list_volumes(&client, &app.owner.global_name, &app.name).await?;
39
40        let selected: Vec<&_> = match &self.volume {
41            Some(sel) => vec![select_volume_by_selector(&app.name, &volumes, sel)?],
42            None => volumes.iter().collect(),
43        };
44
45        if selected.is_empty() {
46            eprintln!("App {} has no volumes.", app.name);
47            return Ok(());
48        }
49
50        let enable = !self.disable;
51        let mut changed = 0usize;
52        let mut failures = Vec::new();
53        for volume in selected {
54            // Skip volumes already in the desired state.
55            if volume.s3_enabled == enable {
56                eprintln!(
57                    "S3 already {} for volume {}",
58                    if enable { "enabled" } else { "disabled" },
59                    volume.label()
60                );
61                continue;
62            }
63
64            match wasmer_backend_api::query::update_volume_s3_enabled(
65                &client,
66                volume.id.clone(),
67                enable,
68            )
69            .await
70            {
71                Ok(payload) if payload.success => {
72                    changed += 1;
73                    eprintln!(
74                        "{} S3 for volume {}",
75                        if enable { "Enabled" } else { "Disabled" },
76                        volume.label()
77                    );
78                }
79                Ok(_) => {
80                    eprintln!(
81                        "Failed to update S3 for volume {}: backend reported failure",
82                        volume.label()
83                    );
84                    failures.push(volume.label());
85                }
86                Err(err) => {
87                    eprintln!("Failed to update S3 for volume {}: {err:#}", volume.label());
88                    failures.push(volume.label());
89                }
90            }
91        }
92
93        if !failures.is_empty() {
94            anyhow::bail!(
95                "Failed to update S3 for {} volume(s): {}",
96                failures.len(),
97                failures.join(", ")
98            );
99        }
100
101        if enable && changed > 0 {
102            eprintln!("Retrieve the S3 credentials with `wasmer app volume credentials`.");
103        }
104
105        Ok(())
106    }
107}