mz_deploy/secret_resolver/
aws_secret.rs1use super::json_field::extract_json_field;
20use super::{SecretProvider, SecretResolveError};
21use async_trait::async_trait;
22use aws_sdk_secretsmanager::Client;
23use std::ops::RangeInclusive;
24use tokio::sync::OnceCell;
25
26const PROVIDER_NAME: &str = "aws_secret";
28
29pub(super) struct AwsSecretProvider {
43 profile: String,
44 client: OnceCell<Client>,
45}
46
47impl AwsSecretProvider {
48 pub(super) fn new(profile: &str) -> Self {
49 Self {
50 profile: profile.to_string(),
51 client: OnceCell::new(),
52 }
53 }
54
55 async fn client(&self) -> &Client {
56 self.client
57 .get_or_init(|| async {
58 let config = mz_aws_util::defaults()
59 .profile_name(&self.profile)
60 .load()
61 .await;
62 Client::new(&config)
63 })
64 .await
65 }
66}
67
68#[async_trait]
69impl SecretProvider for AwsSecretProvider {
70 fn name(&self) -> &str {
71 PROVIDER_NAME
72 }
73
74 fn accepted_args(&self) -> RangeInclusive<usize> {
75 1..=2
76 }
77
78 async fn resolve(&self, args: &[String]) -> Result<String, SecretResolveError> {
79 let secret_name = &args[0];
80 let client = self.client().await;
81 let result = client
82 .get_secret_value()
83 .secret_id(secret_name)
84 .send()
85 .await
86 .map_err(|e| SecretResolveError::ResolutionFailed {
87 name: self.name().to_string(),
88 reason: format!("failed to fetch secret '{}': {}", secret_name, e),
89 })?;
90
91 let secret_string =
92 result
93 .secret_string()
94 .ok_or_else(|| SecretResolveError::ResolutionFailed {
95 name: self.name().to_string(),
96 reason: format!(
97 "secret '{}' is a binary secret; only text secrets are supported",
98 secret_name
99 ),
100 })?;
101
102 match args.get(1) {
103 None => Ok(secret_string.to_string()),
104 Some(json_key) => {
105 extract_json_field(secret_string, json_key, secret_name).map_err(|reason| {
106 SecretResolveError::ResolutionFailed {
107 name: self.name().to_string(),
108 reason,
109 }
110 })
111 }
112 }
113 }
114}
115
116pub(super) struct UnconfiguredAwsProvider;
120
121#[async_trait]
122impl SecretProvider for UnconfiguredAwsProvider {
123 fn name(&self) -> &str {
124 PROVIDER_NAME
125 }
126
127 fn accepted_args(&self) -> RangeInclusive<usize> {
128 1..=2
129 }
130
131 async fn resolve(&self, _args: &[String]) -> Result<String, SecretResolveError> {
132 Err(SecretResolveError::ResolutionFailed {
133 name: self.name().to_string(),
134 reason: "AWS Secrets Manager is not configured. Set 'aws_profile' under [<profile>.security] in project.toml to enable aws_secret().".to_string(),
135 })
136 }
137}