def stop_maintenance(cls): """ Stop maintenance mode """ config = Config() dict_ = config.get_dict() if not config.multi_servers or config.frontend: # Shut down maintenance container in case it's up&running maintenance_down_command = [ 'docker-compose', '-f', 'docker-compose.maintenance.yml', '-f', 'docker-compose.maintenance.override.yml', '-p', config.get_prefix('maintenance'), 'down' ] CLI.run_command(maintenance_down_command, dict_['kobodocker_path']) # Create and start NGINX container frontend_command = [ 'docker-compose', '-f', 'docker-compose.frontend.yml', '-f', 'docker-compose.frontend.override.yml', '-p', config.get_prefix('frontend'), 'up', '-d', 'nginx' ] CLI.run_command(frontend_command, dict_['kobodocker_path']) CLI.colored_print('Maintenance mode has been stopped', CLI.COLOR_SUCCESS) dict_['maintenance_enabled'] = False config.write_config()
def __questions_postgres_backups(self): """ Asks all questions about backups. """ self.__dict['use_backup'] = CLI.yes_no_question( 'Do you want to activate backups?', default=self.__dict['use_backup']) if self.__dict['use_backup']: self.__dict['use_wal_e'] = False schedule_regex_pattern = ( r'^((((\d+(,\d+)*)|(\d+-\d+)|(\*(\/\d+)?)))' r'(\s+(((\d+(,\d+)*)|(\d+\-\d+)|(\*(\/\d+)?)))){4})$') message = ('Schedules use linux cron syntax with UTC datetimes.\n' 'For example, schedule at 12:00 AM E.S.T every Sunday ' 'would be:\n' '0 5 * * 0\n' '\n' 'Please visit https://crontab.guru/ to generate a ' 'cron schedule.') CLI.colored_print('PostgreSQL backup cron expression?', CLI.COLOR_QUESTION) self.__dict['postgres_backup_schedule'] = CLI.get_response( '~{}'.format(schedule_regex_pattern), self.__dict['postgres_backup_schedule']) if self.aws: self.__questions_aws_backup_settings()
def __questions_kobo_postgres(self): """ KoBoToolbox's credentials """ # kobo_db_server self.__dict['kobo_db_server'] = CLI.colored_input( 'KoBoToolbox PostgreSQL server?', CLI.COLOR_QUESTION, self.__dict['kobo_db_server']) # kobo_db_name - Kobo Form CLI.colored_print('KoBoToolbox\'s KoboFORM PostgreSQL database name?', CLI.COLOR_QUESTION) self.__dict['kobo_db_name'] = CLI.get_response( r'~^\w+$', self.__dict['kobo_db_name'], to_lower=False) # kobo_cat_db_name - Kobo Form CLI.colored_print('KoBoToolbox\'s KoboCAT PostgreSQL database name?', CLI.COLOR_QUESTION) self.__dict['kobo_cat_db_name'] = CLI.get_response( r'~^\w+$', self.__dict['kobo_cat_db_name'], to_lower=False) # kobo_db_port self.__dict['kobo_db_port'] = CLI.colored_input( 'KoBoToolbox PostgreSQL Port?', CLI.COLOR_QUESTION, self.__dict['kobo_db_port']) # kobo_db_user self.__dict['kobo_db_user'] = CLI.colored_input( 'KoBoToolbox PostgreSQL User?', CLI.COLOR_QUESTION, self.__dict['kobo_db_user']) # kobo_db_password self.__dict['kobo_db_password'] = CLI.colored_input( 'KoBoToolbox PostgreSQL Password?', CLI.COLOR_QUESTION, self.__dict['kobo_db_password'])
def run(force_setup=False): if sys.version_info[0] == 2: message = ( 'DEPRECATION: Python 2.7 has reached the end of its life on ' 'January 1st, 2020. Please upgrade your Python as Python 2.7 is ' 'not maintained anymore.\n\n' 'A future version of KoBoInstall will drop support for it.') CLI.framed_print(message) if not platform.system() in ['Linux', 'Darwin']: CLI.colored_print('Not compatible with this OS', CLI.COLOR_ERROR) else: config = Config() dict_ = config.get_dict() if config.first_time: force_setup = True if force_setup: dict_ = config.build() Setup.clone_kobodocker(config) Template.render(config) config.init_letsencrypt() Setup.update_hosts(dict_) else: if config.auto_detect_network(): Template.render(config) Setup.update_hosts(dict_) Command.start()
def version(cls): git_commit_version_command = ['git', 'rev-parse', 'HEAD'] stdout = CLI.run_command(git_commit_version_command) CLI.colored_print('kobo-install Version: {} (build {})'.format( Config.KOBO_INSTALL_VERSION, stdout.strip()[0:7], ), CLI.COLOR_SUCCESS)
def stop(cls, output=True, frontend_only=False): """ Stop containers """ config = Config() dict_ = config.get_dict() if not config.multi_servers or config.frontend: # Shut down maintenance container in case it's up&running maintenance_down_command = [ 'docker-compose', '-f', 'docker-compose.maintenance.yml', '-f', 'docker-compose.maintenance.override.yml', '-p', config.get_prefix('maintenance'), 'down' ] CLI.run_command(maintenance_down_command, dict_['kobodocker_path']) # Stop reverse proxy if user uses it. if config.use_letsencrypt: proxy_command = ['docker-compose', 'down'] CLI.run_command(proxy_command, config.get_letsencrypt_repo_path()) # Shut down front-end containers frontend_command = [ 'docker-compose', '-f', 'docker-compose.frontend.yml', '-f', 'docker-compose.frontend.override.yml', '-p', config.get_prefix('frontend'), 'down', ] cls.__validate_custom_yml(config, frontend_command) CLI.run_command(frontend_command, dict_['kobodocker_path']) if not frontend_only and config.backend: backend_role = dict_['backend_server_role'] backend_command = [ 'docker-compose', '-f', 'docker-compose.backend.{}.yml'.format(backend_role), '-f', 'docker-compose.backend.{}.override.yml'.format(backend_role), '-p', config.get_prefix('backend'), 'down' ] cls.__validate_custom_yml(config, backend_command) CLI.run_command(backend_command, dict_['kobodocker_path']) if output: CLI.colored_print('KoboToolbox has been stopped', CLI.COLOR_SUCCESS)
def start_maintenance(cls): config = Config() dict_ = config.get_dict() frontend_command = [ 'docker-compose', '-f', 'docker-compose.maintenance.yml', '-f', 'docker-compose.maintenance.override.yml', '-p', config.get_prefix('maintenance'), 'up', '-d' ] CLI.run_command(frontend_command, dict_['kobodocker_path']) CLI.colored_print('Maintenance mode has been started', CLI.COLOR_SUCCESS)
def write_unique_id(self): try: unique_id_file = os.path.join(self.__dict['support_api_path'], Config.UNIQUE_ID_FILE) with open(unique_id_file, 'w') as f: f.write(str(self.__dict['unique_id'])) os.chmod(unique_id_file, stat.S_IWRITE | stat.S_IREAD) except (IOError, OSError): CLI.colored_print('Could not write unique_id file', CLI.COLOR_ERROR) return False return True
def __validate_installation(self): """ Validates if installation is not run over existing data. The check is made only the first time the setup is run. :return: bool """ if self.first_time: postgres_dir_path = os.path.join(self.__dict['support_api_path'], '.vols', 'db') postgres_data_exists = os.path.exists( postgres_dir_path) and os.path.isdir(postgres_dir_path) if postgres_data_exists: # Not a reliable way to detect whether folder contains # kobo-install files. We assume that if # `docker-compose.backend.template.yml` is there, Docker # images are the good ones. # TODO Find a better way docker_composer_file_path = os.path.join( self.__dict['support_api_path'], 'docker-compose.backend.template.yml') if not os.path.exists(docker_composer_file_path): message = ( 'WARNING!\n\n' 'You are installing over existing data.\n' '\n' 'It is recommended to backup your data and import it ' 'to a fresh installed (by Support API install) database.\n' '\n' 'support-api-install uses these images:\n' ' - PostgreSQL: mdillon/postgis:9.5\n' '\n' 'Be sure to upgrade to these versions before going ' 'further!') CLI.framed_print(message) response = CLI.yes_no_question( 'Are you sure you want to continue?', default=False) if response is False: sys.exit(0) else: CLI.colored_print( 'Privileges escalation is needed to prepare DB', CLI.COLOR_WARNING) # Write `kobo_first_run` file to run postgres # container's entrypoint flawlessly. os.system( 'echo $(date) | sudo tee -a {} > /dev/null'.format( os.path.join(self.__dict['support_api_path'], '.vols', 'db', 'kobo_first_run')))
def get_prefix(self, role): roles = { 'frontend': 'support', 'backend': 'support', 'dashboards': 'support' } try: prefix_ = roles[role] except KeyError: CLI.colored_print('Invalid composer file', CLI.COLOR_ERROR) sys.exit(1) if not self.__dict['docker_prefix']: return prefix_ return '{}-{}'.format(self.__dict['docker_prefix'], prefix_)
def stop(cls, output=True, frontend_only=False): """ Stop containers """ config_object = Config() config = config_object.get_config() if not frontend_only: if (config.get("multi") == Config.TRUE and config.get("server_role") == "backend") or \ config.get("multi") != Config.TRUE: backend_role = config.get("backend_server_role", "master") backend_command = [ "docker-compose", "-f", "docker-compose.backend.{}.yml".format(backend_role), "-f", "docker-compose.backend.{}.override.yml".format( backend_role), "down" ] if config.get("docker_prefix", "") != "": backend_command.insert(-1, "-p") backend_command.insert(-1, config.get("docker_prefix")) CLI.run_command(backend_command, config.get("kobodocker_path")) if (config.get("multi") == Config.TRUE and config.get("server_role") == "frontend") or \ config.get("multi") != Config.TRUE: frontend_command = [ "docker-compose", "-f", "docker-compose.frontend.yml", "-f", "docker-compose.frontend.override.yml", "down" ] if config.get("docker_prefix", "") != "": frontend_command.insert(-1, "-p") frontend_command.insert(-1, config.get("docker_prefix")) CLI.run_command(frontend_command, config.get("kobodocker_path")) # Stop reverse proxy if user uses it. if config_object.use_letsencrypt: proxy_command = ["docker-compose", "down"] CLI.run_command(proxy_command, config_object.get_letsencrypt_repo_path()) if output: CLI.colored_print("KoBoToolbox has been stopped", CLI.COLOR_SUCCESS)
def start(cls, frontend_only=False): config = Config() dict_ = config.get_dict() cls.stop(output=False, frontend_only=frontend_only) if frontend_only: CLI.colored_print('Launching frontend containers', CLI.COLOR_INFO) else: CLI.colored_print('Launching environment', CLI.COLOR_INFO) backend_command = [ 'docker-compose', '-f', 'docker-compose.db.yml', '-p', config.get_prefix('backend'), 'up', '-d' ] CLI.run_command(backend_command, dict_['support_api_path']) # Start the front-end containers # if config.frontend: # If this was previously a shared-database setup, migrate to # separate databases for KPI and KoBoCAT #Upgrading.migrate_single_to_two_databases(config) frontend_command = ['docker-compose', '-f', 'docker-compose.frontend.yml', '-f', 'docker-compose.frontend.override.yml', '-p', config.get_prefix('frontend'), 'up', '-d'] CLI.run_command(frontend_command, dict_['support_api_path']) # Start Dashboards container frontend_command = ['docker-compose', '-f', 'docker-compose.shiny.yml', '-p', config.get_prefix('dashboards'), 'up', '-d'] CLI.run_command(frontend_command, dict_['support_api_path'])
def __create_directory(self): """ Create repository directory if it doesn't exist. """ CLI.colored_print('Where do you want to install?', CLI.COLOR_QUESTION) while True: support_api_path = CLI.colored_input( '', CLI.COLOR_QUESTION, self.__dict['support_api_path']) if support_api_path.startswith('.'): base_dir = os.path.dirname( os.path.dirname(os.path.realpath(__file__))) support_api_path = os.path.normpath( os.path.join(base_dir, support_api_path)) question = 'Please confirm path [{}]'.format(support_api_path) response = CLI.yes_no_question(question) if response is True: if os.path.isdir(support_api_path): break else: try: os.makedirs(support_api_path) break except OSError: CLI.colored_print( 'Could not create directory {}!'.format( support_api_path), CLI.COLOR_ERROR) CLI.colored_print( 'Please make sure you have permissions ' 'and path is correct', CLI.COLOR_ERROR) self.__dict['support_api_path'] = support_api_path self.write_unique_id() self.__validate_installation()