@@ -8,6 +8,7 @@ use std::fs;
88use std:: path:: Path ;
99use thiserror:: Error ;
1010
11+ use crate :: shared:: ssh:: SshPublicKey ;
1112use crate :: shared:: Username ;
1213
1314/// Errors that can occur when creating a `CloudInitContext`
@@ -30,24 +31,31 @@ pub enum CloudInitContextError {
3031#[ derive( Debug , Clone , Serialize ) ]
3132pub struct CloudInitContext {
3233 /// SSH public key content to be injected into cloud-init configuration
33- pub ssh_public_key : String ,
34+ pub ssh_public_key : SshPublicKey ,
3435 /// Username to be created in the cloud-init configuration
3536 pub username : Username ,
3637}
3738
3839/// Builder for `CloudInitContext` with fluent interface
3940#[ derive( Debug , Default ) ]
4041pub struct CloudInitContextBuilder {
41- ssh_public_key : Option < String > ,
42+ ssh_public_key : Option < SshPublicKey > ,
4243 username : Option < Username > ,
4344}
4445
4546impl CloudInitContextBuilder {
4647 /// Set the SSH public key content directly
47- #[ must_use]
48- pub fn with_ssh_public_key ( mut self , ssh_public_key : String ) -> Self {
49- self . ssh_public_key = Some ( ssh_public_key) ;
50- self
48+ ///
49+ /// # Errors
50+ /// Returns an error if the SSH public key is invalid
51+ pub fn with_ssh_public_key < S : Into < String > > (
52+ mut self ,
53+ ssh_public_key : S ,
54+ ) -> Result < Self , CloudInitContextError > {
55+ let key = SshPublicKey :: new ( ssh_public_key)
56+ . map_err ( |e| CloudInitContextError :: SshPublicKeyReadError ( e. to_string ( ) ) ) ?;
57+ self . ssh_public_key = Some ( key) ;
58+ Ok ( self )
5159 }
5260
5361 /// Set the username for the cloud-init configuration
@@ -67,7 +75,7 @@ impl CloudInitContextBuilder {
6775 /// Set the SSH public key by reading from a file path
6876 ///
6977 /// # Errors
70- /// Returns an error if the file cannot be read
78+ /// Returns an error if the file cannot be read or the SSH public key is invalid
7179 pub fn with_ssh_public_key_from_file < P : AsRef < Path > > (
7280 mut self ,
7381 ssh_public_key_path : P ,
@@ -80,8 +88,10 @@ impl CloudInitContextBuilder {
8088 ) )
8189 } ) ?;
8290
83- // Trim any trailing newlines or whitespace from the SSH key
84- self . ssh_public_key = Some ( content. trim ( ) . to_string ( ) ) ;
91+ // Trim any trailing newlines or whitespace from the SSH key and create SshPublicKey
92+ let key = SshPublicKey :: new ( content. trim ( ) )
93+ . map_err ( |e| CloudInitContextError :: SshPublicKeyReadError ( e. to_string ( ) ) ) ?;
94+ self . ssh_public_key = Some ( key) ;
8595 Ok ( self )
8696 }
8797
@@ -110,14 +120,17 @@ impl CloudInitContext {
110120 ///
111121 /// # Errors
112122 /// Returns an error if the username is invalid according to Linux naming requirements
123+ /// or if the SSH public key is invalid
113124 pub fn new < S : Into < String > > (
114- ssh_public_key : String ,
125+ ssh_public_key : S ,
115126 username : S ,
116127 ) -> Result < Self , CloudInitContextError > {
128+ let key = SshPublicKey :: new ( ssh_public_key)
129+ . map_err ( |e| CloudInitContextError :: SshPublicKeyReadError ( e. to_string ( ) ) ) ?;
117130 let username = Username :: new ( username)
118131 . map_err ( |e| CloudInitContextError :: InvalidUsername ( e. to_string ( ) ) ) ?;
119132 Ok ( Self {
120- ssh_public_key,
133+ ssh_public_key : key ,
121134 username,
122135 } )
123136 }
@@ -131,7 +144,7 @@ impl CloudInitContext {
131144 /// Get the SSH public key content
132145 #[ must_use]
133146 pub fn ssh_public_key ( & self ) -> & str {
134- & self . ssh_public_key
147+ self . ssh_public_key . as_str ( )
135148 }
136149
137150 /// Get the username
@@ -151,7 +164,7 @@ mod tests {
151164 fn it_should_create_cloud_init_context_with_ssh_key ( ) {
152165 let ssh_key =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... [email protected] " ; 153166 let username = "testuser" ;
154- let context = CloudInitContext :: new ( ssh_key. to_string ( ) , username) . unwrap ( ) ;
167+ let context = CloudInitContext :: new ( ssh_key, username) . unwrap ( ) ;
155168
156169 assert_eq ! ( context. ssh_public_key( ) , ssh_key) ;
157170 assert_eq ! ( context. username( ) , username) ;
@@ -162,7 +175,8 @@ mod tests {
162175 let ssh_key =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... [email protected] " ; 163176 let username = "testuser" ;
164177 let context = CloudInitContext :: builder ( )
165- . with_ssh_public_key ( ssh_key. to_string ( ) )
178+ . with_ssh_public_key ( ssh_key)
179+ . unwrap ( )
166180 . with_username ( username)
167181 . unwrap ( )
168182 . build ( )
@@ -209,7 +223,8 @@ mod tests {
209223 fn it_should_fail_when_username_is_missing ( ) {
210224 let ssh_key =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... [email protected] " ; 211225 let result = CloudInitContext :: builder ( )
212- . with_ssh_public_key ( ssh_key. to_string ( ) )
226+ . with_ssh_public_key ( ssh_key)
227+ . unwrap ( )
213228 . build ( ) ;
214229
215230 assert ! ( result. is_err( ) ) ;
@@ -235,7 +250,7 @@ mod tests {
235250 fn it_should_serialize_to_json ( ) {
236251 let ssh_key =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... [email protected] " ; 237252 let username = "testuser" ;
238- let context = CloudInitContext :: new ( ssh_key. to_string ( ) , username) . unwrap ( ) ;
253+ let context = CloudInitContext :: new ( ssh_key, username) . unwrap ( ) ;
239254
240255 let json = serde_json:: to_value ( & context) . unwrap ( ) ;
241256 assert_eq ! ( json[ "ssh_public_key" ] , ssh_key) ;
@@ -247,7 +262,7 @@ mod tests {
247262 let ssh_key =
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... [email protected] " ; 248263 let invalid_username = "123invalid" ; // starts with digit
249264
250- let result = CloudInitContext :: new ( ssh_key. to_string ( ) , invalid_username) ;
265+ let result = CloudInitContext :: new ( ssh_key, invalid_username) ;
251266 assert ! ( result. is_err( ) ) ;
252267 assert ! ( matches!(
253268 result. unwrap_err( ) ,
@@ -261,7 +276,8 @@ mod tests {
261276 let invalid_username = "@invalid" ; // contains @ symbol
262277
263278 let result = CloudInitContext :: builder ( )
264- . with_ssh_public_key ( ssh_key. to_string ( ) )
279+ . with_ssh_public_key ( ssh_key)
280+ . unwrap ( )
265281 . with_username ( invalid_username) ;
266282
267283 assert ! ( result. is_err( ) ) ;
0 commit comments