@@ -18,10 +18,20 @@ use crate::super_agent::defaults::{REMOTE_AGENT_DATA_DIR, SUPER_AGENT_DATA_DIR};
1818#[ cfg( target_family = "unix" ) ]
1919const DIRECTORY_PERMISSIONS : u32 = 0o700 ;
2020
21+ #[ derive( Debug , Serialize , Deserialize , PartialEq , Clone , Hash , Eq ) ]
22+ #[ serde( rename_all = "snake_case" ) ]
23+ #[ serde( tag = "state" ) ]
24+ enum ConfigState {
25+ Applying ,
26+ Applied ,
27+ Failed { error_message : String } ,
28+ }
29+
2130#[ derive( Debug , Serialize , Deserialize , PartialEq , Clone , Hash , Eq ) ]
2231pub struct Hash {
2332 hash : String ,
24- applied : bool ,
33+ #[ serde( flatten) ]
34+ state : ConfigState ,
2535}
2636
2737#[ derive( Error , Debug ) ]
@@ -43,22 +53,44 @@ pub enum HashRepositoryError {
4353}
4454
4555impl Hash {
46- pub fn new ( hash : String ) -> Self {
47- Self {
48- hash,
49- applied : false ,
50- }
51- }
5256 pub fn get ( & self ) -> String {
5357 self . hash . clone ( )
5458 }
55-
5659 pub fn is_applied ( & self ) -> bool {
57- self . applied
60+ self . state == ConfigState :: Applied
61+ }
62+
63+ pub fn is_applying ( & self ) -> bool {
64+ self . state == ConfigState :: Applying
65+ }
66+
67+ pub fn is_failed ( & self ) -> bool {
68+ // if let self.state = ConfigState::Failed(msg)
69+ matches ! ( & self . state, ConfigState :: Failed { .. } )
5870 }
5971
72+ pub fn error_message ( & self ) -> Option < String > {
73+ match & self . state {
74+ ConfigState :: Failed { error_message : msg } => Some ( msg. clone ( ) ) ,
75+ _ => None ,
76+ }
77+ }
78+ }
79+
80+ impl Hash {
81+ pub fn new ( hash : String ) -> Self {
82+ Self {
83+ hash,
84+ state : ConfigState :: Applying ,
85+ }
86+ }
6087 pub fn apply ( & mut self ) {
61- self . applied = true
88+ self . state = ConfigState :: Applied ;
89+ }
90+
91+ // It is mandatory for a failed hash to have the error
92+ pub fn fail ( & mut self , error_message : String ) {
93+ self . state = ConfigState :: Failed { error_message } ;
6294 }
6395}
6496
@@ -168,8 +200,8 @@ pub mod test {
168200 use std:: os:: unix:: fs:: PermissionsExt ;
169201
170202 use super :: {
171- Hash , HashRepository , HashRepositoryError , HashRepositoryFile , DIRECTORY_PERMISSIONS ,
172- HASH_FILE_EXTENSION ,
203+ ConfigState , Hash , HashRepository , HashRepositoryError , HashRepositoryFile ,
204+ DIRECTORY_PERMISSIONS , HASH_FILE_EXTENSION ,
173205 } ;
174206 use crate :: config:: persister:: config_persister_file:: FILE_PERMISSIONS ;
175207 use crate :: config:: persister:: config_writer_file:: test:: MockFileWriterMock ;
@@ -186,8 +218,15 @@ pub mod test {
186218 impl Hash {
187219 pub fn applied ( hash : String ) -> Self {
188220 Self {
189- applied : true ,
190221 hash,
222+ state : ConfigState :: Applied ,
223+ }
224+ }
225+
226+ pub fn failed ( hash : String , error_message : String ) -> Self {
227+ Self {
228+ hash,
229+ state : ConfigState :: Failed { error_message } ,
191230 }
192231 }
193232 }
@@ -206,15 +245,11 @@ pub mod test {
206245 }
207246
208247 impl MockHashRepositoryMock {
209- pub fn should_get_applied_hash ( & mut self , agent_id : & AgentID , hash : Hash ) {
248+ pub fn should_get_hash ( & mut self , agent_id : & AgentID , hash : Hash ) {
210249 self . expect_get ( )
211250 . with ( predicate:: eq ( agent_id. clone ( ) ) )
212251 . once ( )
213- . returning ( move |_| {
214- let mut hash = hash. clone ( ) ;
215- hash. apply ( ) ;
216- Ok ( hash)
217- } ) ;
252+ . returning ( move |_| Ok ( hash. clone ( ) ) ) ;
218253 }
219254 pub fn should_save_hash ( & mut self , agent_id : & AgentID , hash : & Hash ) {
220255 self . expect_save ( )
@@ -257,7 +292,7 @@ pub mod test {
257292
258293 // This indentation and the single quotes is to match serde yaml
259294 let content = r#"hash: '123456789'
260- applied: true
295+ state: applied
261296"# ;
262297
263298 let mut expected_path = some_path. clone ( ) ;
@@ -294,4 +329,53 @@ applied: true
294329 let result = hash_repository. get ( & agent_id) ;
295330 assert_eq ! ( hash, result. unwrap( ) ) ;
296331 }
332+
333+ #[ test]
334+ fn test_config_state_default_status ( ) {
335+ //default status for a hash should be applying
336+ let hash = Hash :: new ( "some-hash" . into ( ) ) ;
337+ assert ! ( hash. is_applying( ) )
338+ }
339+
340+ #[ test]
341+ fn test_config_state_transition ( ) {
342+ // hash can change state. This is not ideal, as an applied hash should not go to failed
343+ let mut hash = Hash :: new ( "some-hash" . into ( ) ) ;
344+ assert ! ( hash. is_applying( ) ) ;
345+ hash. apply ( ) ;
346+ assert ! ( hash. is_applied( ) ) ;
347+ hash. fail ( "this is an error message" . to_string ( ) ) ;
348+ assert ! ( hash. is_failed( ) ) ;
349+ }
350+
351+ #[ test]
352+ fn test_hash_serialization ( ) {
353+ let mut hash = Hash :: new ( "123456789" . to_string ( ) ) ;
354+ let expected = "hash: '123456789'\n state: applying\n " ;
355+ assert_eq ! ( expected, serde_yaml:: to_string( & hash) . unwrap( ) ) ;
356+
357+ hash. apply ( ) ;
358+ let expected = "hash: '123456789'\n state: applied\n " ;
359+ assert_eq ! ( expected, serde_yaml:: to_string( & hash) . unwrap( ) ) ;
360+
361+ hash. fail ( "this is an error message" . to_string ( ) ) ;
362+ let expected =
363+ "hash: '123456789'\n state: failed\n error_message: this is an error message\n " ;
364+ assert_eq ! ( expected, serde_yaml:: to_string( & hash) . unwrap( ) ) ;
365+ }
366+
367+ #[ test]
368+ fn test_hash_deserialization ( ) {
369+ let mut hash = Hash :: new ( "123456789" . to_string ( ) ) ;
370+ let content = "hash: '123456789'\n state: applying\n " ;
371+ assert_eq ! ( hash, serde_yaml:: from_str:: <Hash >( & content) . unwrap( ) ) ;
372+
373+ hash. apply ( ) ;
374+ let content = "hash: '123456789'\n state: applied\n " ;
375+ assert_eq ! ( hash, serde_yaml:: from_str:: <Hash >( & content) . unwrap( ) ) ;
376+
377+ hash. fail ( "this is an error message" . to_string ( ) ) ;
378+ let content = "hash: '123456789'\n state: failed\n error_message: this is an error message\n " ;
379+ assert_eq ! ( hash, serde_yaml:: from_str:: <Hash >( & content) . unwrap( ) ) ;
380+ }
297381}
0 commit comments