@@ -38,9 +38,16 @@ export!(Component with_types_in generated);
3838
3939const VOLUME_NAME : & str = "db" ;
4040const VM_NAME_TEMP : & str = "temp" ;
41+
42+ const MAX_VM_FAILURE_RETRIES : u32 = 5 ;
43+
44+ const MINIO_VM_NAME : & str = "minio" ;
45+ const MINIO_IMAGE : & str = "minio/minio:RELEASE.2025-09-07T16-13-09Z-cpuv1" ;
46+ const MINIO_BUCKET_NAME : & str = "litestream-bucket" ;
47+
4148const VM_NAME_FINAL : & str = "obelisk" ;
4249const VOLUME_MOUNT_PATH : & str = "/volume" ;
43- const IMAGE : & str = "getobelisk/obelisk:0.25.3-ubuntu" ;
50+ const FINAL_IMAGE : & str = "getobelisk/obelisk:0.25.3-ubuntu" ;
4451const OBELISK_TOML_PATH : & str = formatcp ! ( "{VOLUME_MOUNT_PATH}/obelisk.toml" ) ;
4552const OBELISK_BIN_PATH : & str = "/obelisk/obelisk" ;
4653const REGION : Region = Region :: Ams ;
@@ -69,6 +76,27 @@ fn allocate_ip(app_name: &str) -> Result<(), AppInitModifyError> {
6976 Ok ( ( ) )
7077}
7178
79+ fn wait_until_started ( app_name : & str , machine_id : & str ) -> Result < ( ) , AppInitModifyError > {
80+ for _ in 0 ..10 {
81+ let machine = activity_fly_http:: machines:: get ( app_name, machine_id)
82+ . map_err ( AppInitModifyError :: TempVmError ) ?;
83+ let state = machine
84+ . ok_or_else ( || {
85+ AppInitModifyError :: TempVmError (
86+ "cannot find temp VM that was created successfuly" . to_string ( ) ,
87+ )
88+ } ) ?
89+ . state ;
90+ if state == MachineState :: Started {
91+ break ;
92+ }
93+ workflow_support:: sleep ( ScheduleAt :: In ( SchedulingDuration :: Seconds (
94+ SLEEP_BETWEEN_RETRIES . as_secs ( ) ,
95+ ) ) ) ;
96+ }
97+ Ok ( ( ) )
98+ }
99+
72100fn setup_volume ( app_name : & str , obelisk_toml : & str ) -> Result < ( ) , AppInitModifyError > {
73101 // Create a volume
74102 activity_fly_http:: volumes:: create (
@@ -83,11 +111,11 @@ fn setup_volume(app_name: &str, obelisk_toml: &str) -> Result<(), AppInitModifyE
83111 . map_err ( AppInitModifyError :: VolumeCreateError ) ?;
84112
85113 // Launch a temporary VM
86- let temp_vm = activity_fly_http:: machines:: create (
114+ let temp_vm_id = activity_fly_http:: machines:: create (
87115 app_name,
88116 VM_NAME_TEMP ,
89117 & MachineConfig {
90- image : IMAGE . to_string ( ) ,
118+ image : FINAL_IMAGE . to_string ( ) ,
91119 guest : Some ( GuestConfig {
92120 cpu_kind : Some ( CpuKind :: Shared ) ,
93121 cpus : Some ( 1 ) ,
@@ -119,29 +147,12 @@ fn setup_volume(app_name: &str, obelisk_toml: &str) -> Result<(), AppInitModifyE
119147 )
120148 . map_err ( AppInitModifyError :: TempVmError ) ?;
121149
122- // Wait until its state is "started"
123- for _ in 0 ..10 {
124- let machine = activity_fly_http:: machines:: get ( app_name, & temp_vm)
125- . map_err ( AppInitModifyError :: TempVmError ) ?;
126- let state = machine
127- . ok_or_else ( || {
128- AppInitModifyError :: TempVmError (
129- "cannot find temp VM that was created successfuly" . to_string ( ) ,
130- )
131- } ) ?
132- . state ;
133- if state == MachineState :: Started {
134- break ;
135- }
136- workflow_support:: sleep ( ScheduleAt :: In ( SchedulingDuration :: Seconds (
137- SLEEP_BETWEEN_RETRIES . as_secs ( ) ,
138- ) ) ) ;
139- }
150+ wait_until_started ( app_name, & temp_vm_id) ?;
140151
141152 // Write obelisk.toml
142153 let exec_response = activity_fly_http:: machines:: exec (
143154 app_name,
144- & temp_vm ,
155+ & temp_vm_id ,
145156 & [
146157 "sh" . to_string ( ) ,
147158 "-c" . to_string ( ) ,
@@ -157,7 +168,7 @@ fn setup_volume(app_name: &str, obelisk_toml: &str) -> Result<(), AppInitModifyE
157168 // Download WASM Components, verify configuration.
158169 let exec_response = activity_fly_http:: machines:: exec (
159170 app_name,
160- & temp_vm ,
171+ & temp_vm_id ,
161172 & [
162173 OBELISK_BIN_PATH . to_string ( ) ,
163174 "server" . to_string ( ) ,
@@ -175,13 +186,13 @@ fn setup_volume(app_name: &str, obelisk_toml: &str) -> Result<(), AppInitModifyE
175186 }
176187 // Attempt to shutdown the temp VM.
177188 // Ignore failure to shut down, temp VM will be deleted with force.
178- let _ = activity_fly_http:: machines:: stop ( app_name, & temp_vm ) ;
189+ let _ = activity_fly_http:: machines:: stop ( app_name, & temp_vm_id ) ;
179190 // Wait a bit for clean shutdown
180191 workflow_support:: sleep ( ScheduleAt :: In ( SchedulingDuration :: Seconds (
181192 SLEEP_AFTER_TEMP_VM_SHUTDOWN . as_secs ( ) ,
182193 ) ) ) ;
183194 // Destroy the VM with force.
184- activity_fly_http:: machines:: delete ( app_name, & temp_vm , true )
195+ activity_fly_http:: machines:: delete ( app_name, & temp_vm_id , true )
185196 . map_err ( AppInitModifyError :: TempVmError ) ?;
186197
187198 Ok ( ( ) )
@@ -220,12 +231,74 @@ fn wait_for_secrets(
220231 Ok ( ( ) )
221232}
222233
223- fn launch_final_vm ( app_name : & str ) -> Result < ( ) , AppInitModifyError > {
224- activity_fly_http:: machines:: create (
234+ fn minio_start ( app_name : & str ) -> Result < String , AppInitModifyError > {
235+ let machine_id = activity_fly_http:: machines:: create (
236+ app_name,
237+ MINIO_VM_NAME ,
238+ & MachineConfig {
239+ image : MINIO_IMAGE . to_string ( ) ,
240+ guest : Some ( GuestConfig {
241+ cpu_kind : Some ( CpuKind :: Shared ) ,
242+ cpus : Some ( 1 ) ,
243+ memory_mb : Some ( 256 ) ,
244+ kernel_args : None ,
245+ } ) ,
246+ auto_destroy : None ,
247+ init : Some ( InitConfig {
248+ cmd : Some (
249+ "server /data --console-address :9001"
250+ . split ( ' ' )
251+ . map ( ToString :: to_string)
252+ . collect ( ) ,
253+ ) ,
254+ entrypoint : None ,
255+ exec : None ,
256+ kernel_args : None ,
257+ swap_size_mb : None ,
258+ tty : None ,
259+ } ) ,
260+ env : None ,
261+ restart : Some ( MachineRestart {
262+ max_retries : Some ( MAX_VM_FAILURE_RETRIES ) ,
263+ policy : RestartPolicy :: OnFailure ,
264+ } ) ,
265+ stop_config : None ,
266+ mounts : None ,
267+ services : None ,
268+ } ,
269+ Some ( REGION ) ,
270+ )
271+ . map_err ( AppInitModifyError :: MinioVmError ) ?;
272+ wait_until_started ( app_name, & machine_id) ?;
273+ Ok ( machine_id)
274+ }
275+
276+ fn minio_configure ( app_name : & str , machine_id : & str ) -> Result < ( ) , AppInitModifyError > {
277+ let exec = |command : & str | {
278+ let exec_response = activity_fly_http:: machines:: exec (
279+ app_name,
280+ machine_id,
281+ & command
282+ . split ( ' ' )
283+ . map ( ToString :: to_string)
284+ . collect :: < Vec < _ > > ( ) ,
285+ )
286+ . map_err ( AppInitModifyError :: MinioVmError ) ?;
287+ assert_eq ! ( 0 , exec_response. exit_code. unwrap( ) ) ;
288+ Ok ( ( ) )
289+ } ;
290+ exec ( "mc alias set myminio http://127.0.0.1:9000 minioadmin minioadmin" ) ?;
291+ exec ( & format ! ( "mc mb myminio/{MINIO_BUCKET_NAME}" ) ) ?;
292+ exec ( "mc ls myminio --json" ) ?;
293+ Ok ( ( ) )
294+ }
295+
296+ fn start_final_vm ( app_name : & str ) -> Result < ( ) , AppInitModifyError > {
297+ let machine_id = activity_fly_http:: machines:: create (
225298 app_name,
226299 VM_NAME_FINAL ,
227300 & MachineConfig {
228- image : IMAGE . to_string ( ) ,
301+ image : FINAL_IMAGE . to_string ( ) ,
229302 guest : Some ( GuestConfig {
230303 cpu_kind : Some ( CpuKind :: Shared ) ,
231304 cpus : Some ( 1 ) ,
@@ -240,16 +313,16 @@ fn launch_final_vm(app_name: &str) -> Result<(), AppInitModifyError> {
240313 . map ( ToString :: to_string)
241314 . collect ( ) ,
242315 ) ,
243- entrypoint : None , // defaults to /obelisk/obelisk
316+ entrypoint : None ,
244317 exec : None ,
245318 kernel_args : None ,
246319 swap_size_mb : Some ( 256 ) ,
247320 tty : None ,
248321 } ) ,
249322 env : None ,
250323 restart : Some ( MachineRestart {
251- max_retries : None ,
252- policy : RestartPolicy :: No ,
324+ max_retries : Some ( MAX_VM_FAILURE_RETRIES ) ,
325+ policy : RestartPolicy :: OnFailure ,
253326 } ) ,
254327 stop_config : None ,
255328 mounts : Some ( vec ! [ Mount {
@@ -279,8 +352,9 @@ fn launch_final_vm(app_name: &str) -> Result<(), AppInitModifyError> {
279352 } ,
280353 Some ( REGION ) ,
281354 )
282- . map ( |_| ( ) )
283- . map_err ( AppInitModifyError :: FinalVmError )
355+ . map_err ( AppInitModifyError :: FinalVmError ) ?;
356+ wait_until_started ( app_name, & machine_id) ?;
357+ Ok ( ( ) )
284358}
285359
286360/// Sleep until the health check passes, observing the deadline, or the app is deleted.
@@ -307,13 +381,19 @@ fn check_health(app_name: &str, health_check_deadline_secs: u16) -> Result<(), A
307381 }
308382}
309383
310- fn cleanup ( app_name : & str , modify_error : AppInitModifyError ) -> AppInitError {
311- if matches ! (
312- modify_error,
313- AppInitModifyError :: AppNameGetError
314- | AppInitModifyError :: AppNameConflict
315- | AppInitModifyError :: AppDeleted
316- ) {
384+ fn cleanup (
385+ app_name : & str ,
386+ modify_error : AppInitModifyError ,
387+ skip_cleanup_on_error : bool ,
388+ ) -> AppInitError {
389+ if skip_cleanup_on_error
390+ || matches ! (
391+ modify_error,
392+ AppInitModifyError :: AppNameGetError
393+ | AppInitModifyError :: AppNameConflict
394+ | AppInitModifyError :: AppDeleted
395+ )
396+ {
317397 return AppInitError :: CleanupNotRequired ;
318398 }
319399 // Delete the app with force.
@@ -363,9 +443,16 @@ impl Guest for Component {
363443 Ok ( ( ) )
364444 }
365445
446+ fn minio_start ( app_name : String ) -> Result < String , AppInitModifyError > {
447+ minio_start ( & app_name)
448+ }
449+
450+ fn minio_configure ( app_name : String , machine_id : String ) -> Result < ( ) , AppInitModifyError > {
451+ minio_configure ( & app_name, & machine_id)
452+ }
453+
366454 fn start_final_vm ( app_name : String ) -> Result < ( ) , AppInitModifyError > {
367- launch_final_vm ( & app_name) ?;
368- Ok ( ( ) )
455+ start_final_vm ( & app_name)
369456 }
370457
371458 fn wait_for_health_check (
@@ -381,19 +468,26 @@ impl Guest for Component {
381468 app_name : String ,
382469 config : ObeliskConfig ,
383470 health_check_deadline_secs : u16 ,
471+ skip_cleanup_on_error : bool ,
384472 ) -> Result < ( ) , AppInitError > {
385473 // Launch sub-workflows by using import.
386474 // In case of any error including a trap (panic), delete the whole app.
387475 workflow_import:: prepare ( & org_slug, & app_name, & config)
388- . map_err ( |err| cleanup ( & app_name, err) ) ?;
476+ . map_err ( |err| cleanup ( & app_name, err, skip_cleanup_on_error ) ) ?;
389477
390478 workflow_import:: wait_for_secrets ( & app_name, & config)
391- . map_err ( |err| cleanup ( & app_name, err) ) ?;
479+ . map_err ( |err| cleanup ( & app_name, err, skip_cleanup_on_error) ) ?;
480+
481+ let minio_vm_id = workflow_import:: minio_start ( & app_name)
482+ . map_err ( |err| cleanup ( & app_name, err, skip_cleanup_on_error) ) ?;
483+ workflow_import:: minio_configure ( & app_name, & minio_vm_id)
484+ . map_err ( |err| cleanup ( & app_name, err, skip_cleanup_on_error) ) ?;
392485
393- workflow_import:: start_final_vm ( & app_name) . map_err ( |err| cleanup ( & app_name, err) ) ?;
486+ workflow_import:: start_final_vm ( & app_name)
487+ . map_err ( |err| cleanup ( & app_name, err, skip_cleanup_on_error) ) ?;
394488
395489 workflow_import:: wait_for_health_check ( & app_name, health_check_deadline_secs)
396- . map_err ( |err| cleanup ( & app_name, err) ) ?;
490+ . map_err ( |err| cleanup ( & app_name, err, skip_cleanup_on_error ) ) ?;
397491
398492 Ok ( ( ) )
399493 }
0 commit comments