@@ -419,6 +419,28 @@ impl<P: Pal + Debug> Node<P> {
419
419
}
420
420
421
421
pub async fn update ( & mut self , config_update : ConfigUpdate ) -> commands:: Result < ( ) > {
422
+ let status = self . status ( ) . await ;
423
+ if status == VmStatus :: Failed {
424
+ return Err ( commands:: Error :: Internal ( anyhow ! (
425
+ "can't update node in Failed state"
426
+ ) ) ) ;
427
+ }
428
+ let params_changed = !config_update. new_values . is_empty ( ) ;
429
+ if params_changed {
430
+ if status == VmStatus :: Running
431
+ && self
432
+ . babel_engine
433
+ . get_jobs ( )
434
+ . await ?
435
+ . iter ( )
436
+ . any ( |( _, job) | job. status == JobStatus :: Running && job. upgrade_blocking )
437
+ {
438
+ command_failed ! ( commands:: Error :: BlockingJobRunning {
439
+ retry_hint: DEFAULT_UPGRADE_RETRY_HINT ,
440
+ } ) ;
441
+ }
442
+ self . state . initialized = false ;
443
+ }
422
444
self . state . image . config_id = config_update. config_id ;
423
445
if let Some ( display_name) = config_update. new_display_name {
424
446
self . state . display_name = display_name;
@@ -448,10 +470,17 @@ impl<P: Pal + Debug> Node<P> {
448
470
self . state . expected_status = VmStatus :: Failed ;
449
471
}
450
472
self . save_state ( ) . await ?;
451
- res?
473
+ res?;
474
+ } else {
475
+ self . save_state ( ) . await ?;
452
476
}
453
477
self . machine . update_node_env ( & self . state ) ;
454
478
self . node_env = self . machine . node_env ( ) ;
479
+ if params_changed && status == VmStatus :: Running {
480
+ self . babel_engine . init ( ) . await ?;
481
+ self . state . initialized = true ;
482
+ self . save_state ( ) . await ?;
483
+ }
455
484
Ok ( ( ) )
456
485
}
457
486
@@ -1102,10 +1131,12 @@ pub mod tests {
1102
1131
request: Request <String >,
1103
1132
) -> Result <Response <( ) >, Status >;
1104
1133
async fn stop_job( & self , request: Request <String >) -> Result <Response <( ) >, Status >;
1134
+ async fn stop_all_jobs( & self , request: Request <( ) >) -> Result <Response <( ) >, Status >;
1105
1135
async fn skip_job( & self , request: Request <String >) -> Result <Response <( ) >, Status >;
1106
1136
async fn cleanup_job( & self , request: Request <String >) -> Result <Response <( ) >, Status >;
1107
1137
async fn job_info( & self , request: Request <String >) -> Result <Response <JobInfo >, Status >;
1108
1138
async fn get_job_shutdown_timeout( & self , request: Request <String >) -> Result <Response <Duration >, Status >;
1139
+ async fn get_active_jobs_shutdown_timeout( & self , request: Request <( ) >) -> Result <Response <Duration >, Status >;
1109
1140
async fn get_jobs( & self , request: Request <( ) >) -> Result <Response <JobsInfo >, Status >;
1110
1141
async fn run_jrpc(
1111
1142
& self ,
@@ -1861,6 +1892,14 @@ pub mod tests {
1861
1892
} )
1862
1893
. times ( 3 )
1863
1894
. returning ( |_| Ok ( Response :: new ( ( ) ) ) ) ;
1895
+ babel_mock
1896
+ . expect_get_active_jobs_shutdown_timeout ( )
1897
+ . times ( 2 )
1898
+ . returning ( |_| Ok ( Response :: new ( Duration :: from_secs ( 1 ) ) ) ) ;
1899
+ babel_mock
1900
+ . expect_stop_all_jobs ( )
1901
+ . times ( 2 )
1902
+ . returning ( |_| Ok ( Response :: new ( ( ) ) ) ) ;
1864
1903
babel_mock
1865
1904
. expect_run_sh ( )
1866
1905
. withf ( |req| req. get_ref ( ) == "echo ok" )
@@ -2079,6 +2118,8 @@ pub mod tests {
2079
2118
pal. expect_create_vm ( ) . return_once ( move |_, _| {
2080
2119
let mut mock = MockTestVM :: new ( ) ;
2081
2120
let plugin_path = plugin_path. clone ( ) ;
2121
+ mock. expect_state ( ) . once ( ) . returning ( || VmState :: SHUTOFF ) ;
2122
+ mock. expect_state ( ) . times ( 2 ) . returning ( || VmState :: RUNNING ) ;
2082
2123
mock. expect_plugin_path ( )
2083
2124
. once ( )
2084
2125
. returning ( move || plugin_path. clone ( ) ) ;
@@ -2115,29 +2156,60 @@ pub mod tests {
2115
2156
. await ?;
2116
2157
assert_eq ! ( VmStatus :: Running , node. expected_status( ) ) ;
2117
2158
2118
- assert_eq ! ( node. state. firewall, node_state. firewall) ;
2119
- node. update ( ConfigUpdate {
2120
- config_id : "new-cfg_id" . to_string ( ) ,
2121
- new_display_name : Some ( "new name" . to_string ( ) ) ,
2122
- new_firewall : Some ( updated_firewall. clone ( ) ) ,
2123
- new_org_id : Some ( "new org_id" . to_string ( ) ) ,
2124
- new_org_name : Some ( "org name" . to_string ( ) ) ,
2125
- new_values : HashMap :: from_iter ( [
2126
- ( "new_key1" . to_string ( ) , "new value " . to_string ( ) ) ,
2127
- ( "new_key2" . to_string ( ) , "new value 2" . to_string ( ) ) ,
2128
- ] ) ,
2129
- } )
2130
- . await ?;
2131
- assert_eq ! ( node. state. firewall, updated_firewall) ;
2132
- assert_eq ! ( node. state. display_name, "new name" . to_string( ) ) ;
2133
- assert_eq ! ( node. state. org_id, "new org_id" . to_string( ) ) ;
2134
- assert_eq ! ( node. state. org_name, "org name" . to_string( ) ) ;
2135
2159
assert_eq ! (
2136
- node. state. properties. get( "new_key1" ) ,
2137
- Some ( "new value " . to_string( ) ) . as_ref( )
2160
+ "BV internal error: 'can't update node in Failed state'" ,
2161
+ node. update( ConfigUpdate {
2162
+ config_id: "new-cfg_id" . to_string( ) ,
2163
+ new_display_name: None ,
2164
+ new_firewall: None ,
2165
+ new_org_id: None ,
2166
+ new_org_name: None ,
2167
+ new_values: Default :: default ( ) ,
2168
+ } , )
2169
+ . await
2170
+ . unwrap_err( )
2171
+ . to_string( )
2138
2172
) ;
2139
- test_env. assert_node_state_saved ( & node. state ) . await ;
2140
2173
2174
+ let mut babel_mock = MockTestBabelService :: new ( ) ;
2175
+ babel_mock
2176
+ . expect_get_jobs ( )
2177
+ . times ( 2 )
2178
+ . returning ( |_| Ok ( Response :: new ( HashMap :: default ( ) ) ) ) ;
2179
+ babel_mock
2180
+ . expect_get_active_jobs_shutdown_timeout ( )
2181
+ . once ( )
2182
+ . returning ( |_| Ok ( Response :: new ( Duration :: from_millis ( 1 ) ) ) ) ;
2183
+ babel_mock
2184
+ . expect_stop_all_jobs ( )
2185
+ . once ( )
2186
+ . returning ( |_| Ok ( Response :: new ( ( ) ) ) ) ;
2187
+ babel_mock
2188
+ . expect_run_sh ( )
2189
+ . withf ( |req| req. get_ref ( ) == "touch /blockjoy/.protocol_data.lock" )
2190
+ . once ( )
2191
+ . returning ( |_| {
2192
+ Ok ( Response :: new ( ShResponse {
2193
+ exit_code : 0 ,
2194
+ stdout : "" . to_string ( ) ,
2195
+ stderr : "" . to_string ( ) ,
2196
+ } ) )
2197
+ } ) ;
2198
+ babel_mock
2199
+ . expect_protocol_data_stamp ( )
2200
+ . once ( )
2201
+ . returning ( |_| Ok ( Response :: new ( Some ( SystemTime :: now ( ) ) ) ) ) ;
2202
+ babel_mock
2203
+ . expect_create_job ( )
2204
+ . times ( 2 )
2205
+ . returning ( |_| Ok ( Response :: new ( ( ) ) ) ) ;
2206
+ babel_mock
2207
+ . expect_start_job ( )
2208
+ . times ( 2 )
2209
+ . returning ( |_| Ok ( Response :: new ( ( ) ) ) ) ;
2210
+ let server = test_env. start_server ( babel_mock) . await ;
2211
+
2212
+ assert_eq ! ( node. state. firewall, node_state. firewall) ;
2141
2213
assert_eq ! (
2142
2214
"BV internal error: 'failed to apply firewall config'" ,
2143
2215
node. update( ConfigUpdate {
@@ -2146,15 +2218,46 @@ pub mod tests {
2146
2218
new_firewall: Some ( firewall:: Config :: default ( ) ) ,
2147
2219
new_org_id: Some ( "failed_org_id" . to_string( ) ) ,
2148
2220
new_org_name: None ,
2149
- new_values: Default :: default ( ) ,
2221
+ new_values: HashMap :: from_iter( [ (
2222
+ "new_key0" . to_string( ) ,
2223
+ "new value 0" . to_string( )
2224
+ ) , ] ) ,
2150
2225
} , )
2151
2226
. await
2152
2227
. unwrap_err( )
2153
2228
. to_string( )
2154
2229
) ;
2155
2230
assert_eq ! ( 0 , node. state. firewall. rules. len( ) ) ;
2231
+ assert ! ( !node. state. initialized) ;
2156
2232
test_env. assert_node_state_saved ( & node. state ) . await ;
2233
+ assert_eq ! ( VmStatus :: Failed , node. expected_status( ) ) ;
2157
2234
2235
+ node. state . initialized = true ;
2236
+ node. state . expected_status = VmStatus :: Running ;
2237
+ node. update ( ConfigUpdate {
2238
+ config_id : "new-cfg_id" . to_string ( ) ,
2239
+ new_display_name : Some ( "new name" . to_string ( ) ) ,
2240
+ new_firewall : Some ( updated_firewall. clone ( ) ) ,
2241
+ new_org_id : Some ( "new org_id" . to_string ( ) ) ,
2242
+ new_org_name : Some ( "org name" . to_string ( ) ) ,
2243
+ new_values : HashMap :: from_iter ( [
2244
+ ( "new_key1" . to_string ( ) , "new value 1" . to_string ( ) ) ,
2245
+ ( "new_key2" . to_string ( ) , "new value 2" . to_string ( ) ) ,
2246
+ ] ) ,
2247
+ } )
2248
+ . await
2249
+ . unwrap ( ) ;
2250
+ assert ! ( node. state. initialized) ;
2251
+ assert_eq ! ( node. state. firewall, updated_firewall) ;
2252
+ assert_eq ! ( node. state. display_name, "new name" . to_string( ) ) ;
2253
+ assert_eq ! ( node. state. org_id, "new org_id" . to_string( ) ) ;
2254
+ assert_eq ! ( node. state. org_name, "org name" . to_string( ) ) ;
2255
+ assert_eq ! (
2256
+ node. state. properties. get( "new_key1" ) ,
2257
+ Some ( "new value 1" . to_string( ) ) . as_ref( )
2258
+ ) ;
2259
+ test_env. assert_node_state_saved ( & node. state ) . await ;
2260
+ server. assert ( ) . await ;
2158
2261
Ok ( ( ) )
2159
2262
}
2160
2263
@@ -2429,6 +2532,14 @@ pub mod tests {
2429
2532
. expect_get_jobs ( )
2430
2533
. once ( )
2431
2534
. returning ( |_| Ok ( Response :: new ( HashMap :: default ( ) ) ) ) ;
2535
+ babel_mock
2536
+ . expect_get_active_jobs_shutdown_timeout ( )
2537
+ . once ( )
2538
+ . returning ( |_| Ok ( Response :: new ( Duration :: from_millis ( 1 ) ) ) ) ;
2539
+ babel_mock
2540
+ . expect_stop_all_jobs ( )
2541
+ . once ( )
2542
+ . returning ( |_| Ok ( Response :: new ( ( ) ) ) ) ;
2432
2543
let data_dir = test_env. tmp_root . clone ( ) ;
2433
2544
babel_mock. expect_run_sh ( ) . once ( ) . returning ( move |_| {
2434
2545
babel_api:: utils:: touch_protocol_data ( & data_dir) . unwrap ( ) ;
0 commit comments