11use std:: collections:: BTreeMap ;
2+ use std:: fmt;
23use std:: net:: SocketAddr ;
34use std:: path:: PathBuf ;
45
56use anyhow:: Result ;
67use argh:: FromArgs ;
78use figment:: providers:: { Env , Format , Serialized , Yaml } ;
9+ use secrecy:: { CloneableSecret , SecretBox , SerializableSecret , zeroize:: Zeroize } ;
810use serde:: { Deserialize , Serialize } ;
911
1012const ENV_PREFIX : & str = "FSS_" ;
1113
14+ /// Newtype around `String` that may protect against accidental
15+ /// logging of secrets in our configuration struct. Use with
16+ /// [`secrecy::SecretBox`].
17+ #[ derive( Clone , Default , Serialize , Deserialize , PartialEq ) ]
18+ pub ( crate ) struct ConfigSecret ( String ) ;
19+
20+ impl ConfigSecret {
21+ pub fn as_str ( & self ) -> & str {
22+ self . 0 . as_str ( )
23+ }
24+ }
25+
26+ impl std:: ops:: Deref for ConfigSecret {
27+ type Target = String ;
28+ fn deref ( & self ) -> & Self :: Target {
29+ & self . 0
30+ }
31+ }
32+
33+ impl fmt:: Debug for ConfigSecret {
34+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> Result < ( ) , fmt:: Error > {
35+ write ! ( f, "[redacted]" )
36+ }
37+ }
38+
39+ impl CloneableSecret for ConfigSecret { }
40+ impl SerializableSecret for ConfigSecret { }
41+ impl Zeroize for ConfigSecret {
42+ fn zeroize ( & mut self ) {
43+ self . 0 . zeroize ( ) ;
44+ }
45+ }
46+
1247#[ derive( Debug , Clone , Deserialize , Serialize ) ]
1348#[ serde( tag = "type" , rename_all = "lowercase" ) ]
1449pub enum Storage {
@@ -33,7 +68,7 @@ pub enum Storage {
3368
3469#[ derive( Debug , Clone , Deserialize , Serialize ) ]
3570pub struct Sentry {
36- pub dsn : String ,
71+ pub dsn : SecretBox < ConfigSecret > ,
3772 pub sample_rate : Option < f32 > ,
3873 pub traces_sample_rate : Option < f32 > ,
3974}
@@ -49,7 +84,7 @@ pub struct Config {
4984
5085 // others
5186 pub sentry : Option < Sentry > ,
52- pub datadog_key : Option < String > ,
87+ pub datadog_key : Option < SecretBox < ConfigSecret > > ,
5388 pub metric_tags : BTreeMap < String , String > ,
5489}
5590
@@ -103,8 +138,16 @@ pub struct Args {
103138mod tests {
104139 use std:: io:: Write ;
105140
141+ use secrecy:: ExposeSecret ;
142+
106143 use super :: * ;
107144
145+ impl PartialEq < str > for ConfigSecret {
146+ fn eq ( & self , other : & str ) -> bool {
147+ self . 0 == other
148+ }
149+ }
150+
108151 #[ test]
109152 fn configurable_via_env ( ) {
110153 figment:: Jail :: expect_with ( |jail| {
@@ -135,7 +178,7 @@ mod tests {
135178 sample_rate,
136179 traces_sample_rate,
137180 } = & dbg ! ( & config) . sentry . as_ref ( ) . unwrap ( ) ;
138- assert_eq ! ( dsn, "abcde" ) ;
181+ assert_eq ! ( dsn. expose_secret ( ) , "abcde" ) ;
139182 assert_eq ! ( sample_rate, & Some ( 0.5 ) ) ;
140183 assert_eq ! ( traces_sample_rate, & Some ( 0.5 ) ) ;
141184
@@ -177,8 +220,43 @@ mod tests {
177220 sample_rate,
178221 traces_sample_rate,
179222 } = & dbg ! ( & config) . sentry . as_ref ( ) . unwrap ( ) ;
180- assert_eq ! ( dsn, "abcde" ) ;
223+ assert_eq ! ( dsn. expose_secret ( ) , "abcde" ) ;
181224 assert_eq ! ( sample_rate, & Some ( 0.5 ) ) ;
182225 assert_eq ! ( traces_sample_rate, & Some ( 0.5 ) ) ;
183226 }
227+
228+ #[ test]
229+ fn configured_with_env_and_yaml ( ) {
230+ let mut tempfile = tempfile:: NamedTempFile :: new ( ) . unwrap ( ) ;
231+ tempfile
232+ . write_all (
233+ br#"
234+ long_term_storage:
235+ type: s3compatible
236+ endpoint: http://localhost:8888
237+ bucket: whatever
238+ "# ,
239+ )
240+ . unwrap ( ) ;
241+ figment:: Jail :: expect_with ( |jail| {
242+ jail. set_env ( "fss_long_term_storage__endpoint" , "http://localhost:9001" ) ;
243+
244+ let args = Args {
245+ config : Some ( tempfile. path ( ) . into ( ) ) ,
246+ } ;
247+ let config = Config :: from_args ( args) . unwrap ( ) ;
248+
249+ let Storage :: S3Compatible {
250+ endpoint,
251+ bucket : _bucket,
252+ } = & dbg ! ( & config) . long_term_storage
253+ else {
254+ panic ! ( "expected s3 storage" ) ;
255+ } ;
256+ // Env should overwrite the yaml config
257+ assert_eq ! ( endpoint, "http://localhost:9001" ) ;
258+
259+ Ok ( ( ) )
260+ } ) ;
261+ }
184262}
0 commit comments