@@ -64,7 +64,7 @@ def wait_till_up(ip):
6464 Exception: If the IP is not up after 180 seconds.
6565 """
6666 timeout = 180 # Total time to wait in seconds
67- interval = 10 # Time to wait between checks in seconds
67+ interval = 1 # Time to wait between checks in seconds
6868 elapsed_time = 0
6969
7070 while elapsed_time <= timeout :
@@ -100,7 +100,7 @@ def remove_empty_lines(text):
100100def validateRepository (git_repo_url ):
101101 """Checks if a GitHub repository exists. Raises an exception if not."""
102102 try :
103- response = requests .head (git_repo_url )
103+ response = requests .head (git_repo_url , allow_redirects = True , timeout = 20 )
104104 response .raise_for_status ()
105105 except requests .exceptions .RequestException as e :
106106 if "Not Found" in str (e ):
@@ -341,16 +341,16 @@ def setup_apache_load_balancer():
341341
342342def setup_systemctl (folder_name , uname ):
343343 systemctl_commands = f"""
344- sudo a2enmod proxy
345- sudo a2enmod proxy_http
346- sudo systemctl restart apache2
347344sudo chmod +x /home/{ uname } /{ folder_name } /backend.sh || true
348345sudo chmod +x /home/{ uname } /{ folder_name } /frontend.sh || true
349346sudo systemctl daemon-reload
350347sudo systemctl enable backend.service
351348sudo systemctl start backend.service
352349sudo systemctl enable frontend.service
353350sudo systemctl start frontend.service
351+ sudo a2enmod proxy
352+ sudo a2enmod proxy_http
353+ sudo systemctl restart apache2
354354"""
355355 subprocess .run (remove_empty_lines (systemctl_commands ), shell = True ,
356356 check = True ,
@@ -456,7 +456,6 @@ def setup_systemctl_for_data_scraper(launch_file_path, service_name):
456456
457457def install_scraper (git_repo_url , folder_name , max_retry ):
458458 uname = get_username ()
459-
460459 install_chrome (uname )
461460 clone_repository (git_repo_url , folder_name )
462461
@@ -469,7 +468,7 @@ def install_scraper(git_repo_url, folder_name, max_retry):
469468def install_ui_scraper_in_vm (git_repo_url , folder_name ):
470469 validateRepository (git_repo_url )
471470 install_ui_scraper (git_repo_url , folder_name )
472- click .echo ("Successfully installed the Scraper." )
471+ click .echo ("Successfully installed the Web Scraper." )
473472 click .echo ("Now, Checking VM Status..." )
474473 ip = get_vm_ip ()
475474 wait_till_up (ip )
@@ -480,7 +479,149 @@ def install_scraper_in_vm(git_repo_url, folder_name, max_retry):
480479 install_scraper (git_repo_url , folder_name , max_retry )
481480 click .echo ("Successfully installed the Scraper." )
482481 # todo check status is it running or error?
483- # if error show it
482+ def get_filename_from_url (url ):
483+ from urllib .parse import urlparse
484+ return os .path .basename (urlparse (url ).path .rstrip ("/" ))
485+
486+ def install_desktop_app_in_vm (
487+ debian_installer_url ,
488+ port ,
489+ skip_apache_request_routing ,
490+ api_path_prefix
491+ ):
492+ # Validate api_path_prefix
493+ api_path_prefix = api_path_prefix if api_path_prefix else ""
494+ if api_path_prefix :
495+ if not api_path_prefix .startswith ("/" ):
496+ api_path_prefix = "/" + api_path_prefix
497+ if api_path_prefix .endswith ("/" ):
498+ api_path_prefix = api_path_prefix [:- 1 ]
499+
500+ # Validate URL
501+ validate_url (debian_installer_url )
502+
503+ # Install the app
504+ uname = get_username ()
505+ install_chrome (uname )
506+ default_name = get_filename_from_url (debian_installer_url )
507+
508+ delete_installer (default_name )
509+ wget_command = f"wget { debian_installer_url } "
510+ subprocess .run (wget_command , shell = True , check = True , stderr = subprocess .STDOUT )
511+ install_command = f"sudo apt --fix-broken install ./{ default_name } -y"
512+ subprocess .run (install_command , shell = True , check = True , stderr = subprocess .STDOUT )
513+ package_name = subprocess .check_output (f"dpkg-deb -f ./{ default_name } Package" , shell = True ).decode ().strip ()
514+ delete_installer (default_name )
515+
516+ # Create systemd service for Xvfb
517+ xvfb_service_name = f"xvfb.service"
518+ xvfb_service_content = f"""[Unit]
519+ Description=Xvfb Service
520+ After=network.target
521+ [Service]
522+ Type=simple
523+ User={ uname }
524+ ExecStart=/usr/bin/Xvfb -ac :99 -screen 0 1280x1024x16
525+ Restart=no
526+ [Install]
527+ WantedBy=multi-user.target"""
528+ write_file_sudo (xvfb_service_content , f"/etc/systemd/system/{ xvfb_service_name } " )
529+
530+ # Create systemd service for the package
531+ package_service_name = f"{ package_name } .service"
532+ package_service_content = f"""[Unit]
533+ Description={ package_name } Service
534+ After=network.target
535+ StartLimitInterval=0
536+ [Service]
537+ Type=simple
538+ User={ uname }
539+ Environment="DISPLAY=:99"
540+ ExecStart=/usr/bin/{ package_name } --port { port } { '--api_path_prefix ' + api_path_prefix if api_path_prefix else '' }
541+ Restart=on-failure
542+ RestartSec=10
543+ [Install]
544+ WantedBy=multi-user.target"""
545+ write_file_sudo (package_service_content , f"/etc/systemd/system/{ package_service_name } " )
546+
547+ if not skip_apache_request_routing :
548+ setup_apache_load_balancer_desktop_app (port , api_path_prefix )
549+
550+ # Enable and start services
551+ systemctl_commands = f"""
552+ sudo systemctl daemon-reload
553+ sudo systemctl enable { xvfb_service_name }
554+ sudo systemctl start { xvfb_service_name }
555+ sudo systemctl enable { package_service_name }
556+ sudo systemctl start { package_service_name } """
557+ if not skip_apache_request_routing :
558+ systemctl_commands += """
559+ sudo a2enmod proxy
560+ sudo a2enmod proxy_http
561+ sudo systemctl restart apache2"""
562+ subprocess .run (remove_empty_lines (systemctl_commands ), shell = True , check = True , stderr = subprocess .STDOUT )
563+
564+ click .echo ("Successfully installed the Desktop App." )
565+ click .echo ("Now, Checking API Status..." )
566+ ip = get_vm_ip ()
567+ wait_till_desktop_api_up (ip , api_path_prefix )
568+ click .echo (f"Hurray! your desktop app is up and running. Visit http://{ ip } { api_path_prefix } / to see the API Docs." )
569+
570+ def delete_installer (default_name ):
571+ if os .path .exists (default_name ):
572+ os .remove (default_name )
573+
574+ def setup_apache_load_balancer_desktop_app (port , api_path_prefix ):
575+ apache_conf = f"""<VirtualHost *:80>
576+ ServerAdmin webmaster@localhost
577+ DocumentRoot /var/www/html
578+ ErrorLog ${{APACHE_LOG_DIR}}/error.log
579+ CustomLog ${{APACHE_LOG_DIR}}/access.log combined
580+ ProxyPass { api_path_prefix } / http://127.0.0.1:{ port } { api_path_prefix } /
581+ ProxyPassReverse { api_path_prefix } / http://127.0.0.1:{ port } { api_path_prefix } /
582+ </VirtualHost>"""
583+ write_file_sudo (apache_conf , "/etc/apache2/sites-available/000-default.conf" )
584+
585+ def validate_url (url ):
586+ try :
587+ response = requests .head (url , allow_redirects = True , timeout = 20 )
588+ response .raise_for_status ()
589+ except requests .exceptions .RequestException as e :
590+ raise Exception (f"URL validation failed: { e } " )
591+
592+ def wait_till_desktop_api_up (ip , api_path_prefix ):
593+ """
594+ Polls the given IP address every 10 seconds for 180 seconds to check if it's up.
595+
596+ Args:
597+ ip (str): The IP address to check.
598+ api_path_prefix (str): The API path prefix.
599+
600+ Raises:
601+ Exception: If the IP is not up after 180 seconds.
602+ """
603+ timeout = 180 # Total time to wait in seconds
604+ interval = 1 # Time to wait between checks in seconds
605+ elapsed_time = 0
606+
607+ while elapsed_time <= timeout :
608+ try :
609+ # Attempt to connect to the IP address
610+ response = requests .get (f"http://{ ip } { api_path_prefix } /ui/app-props" , timeout = 5 )
611+
612+ # If the response is successful, return without raising an exception
613+ if response .status_code == 200 :
614+ return
615+ except requests .ConnectionError :
616+ # If a connection error occurs, just wait and try again
617+ pass
618+
619+ time .sleep (interval )
620+ elapsed_time += interval
621+
622+ # If the function hasn't returned after the loop, raise an exception
623+ raise Exception (f"The Desktop App at http://{ ip } / is not up after { timeout } seconds." )
624+
484625# python -m bota.vm
485626if __name__ == "__main__" :
486627 pass
0 commit comments