wasmer_cli/commands/app/volumes/credentials/
rotate_secrets.rs1use super::{ItemFormatOpts, S3CredentialRecord, render_s3_credentials};
2use crate::{
3 commands::{AsyncCliCommand, app::util::AppIdentOpts},
4 config::WasmerEnv,
5};
6
7#[derive(clap::Parser, Debug)]
9pub struct CmdAppVolumesRotateSecrets {
10 #[clap(flatten)]
11 pub env: WasmerEnv,
12
13 #[clap(flatten)]
14 pub ident: AppIdentOpts,
15
16 #[clap(flatten)]
17 pub fmt: ItemFormatOpts,
18
19 #[clap(long)]
22 pub volume: Option<String>,
23}
24
25#[async_trait::async_trait]
26impl AsyncCliCommand for CmdAppVolumesRotateSecrets {
27 type Output = ();
28
29 async fn run_async(self) -> Result<Self::Output, anyhow::Error> {
30 let client = self.env.client()?;
31 let (_ident, app) = self.ident.load_app(&client).await?;
32
33 let volumes =
36 super::super::list_volumes(&client, &app.owner.global_name, &app.name).await?;
37
38 let selected: Vec<&_> = match &self.volume {
39 Some(sel) => vec![super::super::select_volume_by_selector(
40 &app.name, &volumes, sel,
41 )?],
42 None => volumes.iter().filter(|v| v.s3_enabled).collect(),
45 };
46
47 if selected.is_empty() {
48 eprintln!(
50 "App {} has no S3-enabled volumes to rotate secrets for.",
51 app.name
52 );
53 }
54
55 let mut records = Vec::new();
59 let mut failures = Vec::new();
60 for volume in selected {
61 let result =
62 wasmer_backend_api::query::rotate_s3_credentials(&client, volume.id.clone()).await;
63
64 match result {
65 Ok(payload) if payload.success => {
66 eprintln!("Rotated S3 credentials for volume {}", volume.label());
69 records.push(S3CredentialRecord {
70 app_name: app.name.clone(),
71 volume: volume.mount_path.clone(),
72 access_key: payload.access_key,
73 secret_key: payload.secret_key,
74 endpoint: payload.endpoint,
75 });
76 }
77 Ok(_) => {
78 eprintln!(
79 "Failed to rotate S3 credentials for volume {}",
80 volume.label()
81 );
82 failures.push(volume.label());
83 }
84 Err(err) => {
85 eprintln!(
86 "Failed to rotate S3 credentials for volume {}: {err:#}",
87 volume.label()
88 );
89 failures.push(volume.label());
90 }
91 }
92 }
93
94 println!("{}", render_s3_credentials(self.fmt.format, &records));
97
98 if !failures.is_empty() {
99 anyhow::bail!(
100 "Failed to rotate S3 credentials for {} volume(s): {}",
101 failures.len(),
102 failures.join(", ")
103 );
104 }
105
106 Ok(())
107 }
108}