def stop_app(version_key): """ Stops all process instances of a version on this machine. Args: version_key: Name of version to stop Returns: True on success, False otherwise """ project_id = version_key.split(VERSION_PATH_SEPARATOR)[0] if not misc.is_app_name_valid(project_id): raise BadConfigurationException( 'Invalid project ID: {}'.format(project_id)) logging.info('Stopping {}'.format(version_key)) version_group = ''.join([MONIT_INSTANCE_PREFIX, version_key]) monit_entries = yield monit_operator.get_entries() version_entries = [ entry for entry in monit_entries if entry.startswith(version_group) ] for entry in version_entries: yield unmonitor_and_terminate(entry) if project_id not in projects_manager and not remove_logrotate(project_id): logging.error( "Error while removing log rotation for application: {}".format( project_id)) yield monit_operator.reload() yield clean_old_sources()
def stop_app_instance(app_name, port): """ Stops a Google App Engine application process instance on current machine. Args: app_name: A string, the name of application to stop. port: The port the application is running on. Returns: True on success, False otherwise. """ if not misc.is_app_name_valid(app_name): logging.error("Unable to kill app process %s on port %d because of " \ "invalid name for application" % (app_name, int(port))) return False logging.info("Stopping application %s" % app_name) watch = "app___" + app_name + "-" + str(port) if not monit_interface.stop(watch, is_group=False): logging.error("Unable to stop application server for app {0} on " \ "port {1}".format(app_name, port)) return False # Now that the AppServer is stopped, remove its monit config file so that # monit doesn't pick it up and restart it. monit_config_file = '{}/appscale-{}.cfg'.format(MONIT_CONFIG_DIR, watch) try: os.remove(monit_config_file) except OSError as os_error: logging.error("Error deleting {0}".format(monit_config_file)) return True
def stop_app_instance(version_key, port): """ Stops a Google App Engine application process instance on current machine. Args: version_key: A string, the name of version to stop. port: The port the application is running on. Returns: True on success, False otherwise. """ project_id = version_key.split(VERSION_PATH_SEPARATOR)[0] if not misc.is_app_name_valid(project_id): raise BadConfigurationException( 'Invalid project ID: {}'.format(project_id)) logging.info('Stopping {}:{}'.format(version_key, port)) # Discover revision key from version and port. instance_key_re = re.compile('{}{}.*-{}'.format(MONIT_INSTANCE_PREFIX, version_key, port)) monit_entries = yield monit_operator.get_entries() try: watch = next(entry for entry in monit_entries if instance_key_re.match(entry)) except StopIteration: message = 'No entries exist for {}:{}'.format(version_key, port) raise HTTPError(HTTPCodes.INTERNAL_ERROR, message=message) yield unmonitor_and_terminate(watch) yield monit_operator.reload() yield clean_old_sources()
def test_is_app_name_valid(self): self.assertEqual(True, misc.is_app_name_valid("guestbook")) self.assertEqual(True, misc.is_app_name_valid("guestbook132")) self.assertEqual(True, misc.is_app_name_valid("guestbook_132")) self.assertEqual(True, misc.is_app_name_valid("guestbook-132")) self.assertEqual(False, misc.is_app_name_valid("asdf#")) self.assertEqual(False, misc.is_app_name_valid("%##;")) self.assertEqual(False, misc.is_app_name_valid("$78;"))
def stop_app_instance(version_key, port): """ Stops a Google App Engine application process instance on current machine. Args: version_key: A string, the name of version to stop. port: The port the application is running on. Returns: True on success, False otherwise. """ project_id = version_key.split(VERSION_PATH_SEPARATOR)[0] if not misc.is_app_name_valid(project_id): raise BadConfigurationException( 'Invalid project ID: {}'.format(project_id)) logging.info('Stopping {}:{}'.format(version_key, port)) # Discover revision key from version and port. instance_key_re = re.compile( '{}{}.*-{}'.format(MONIT_INSTANCE_PREFIX, version_key, port)) monit_entries = yield monit_operator.get_entries() try: watch = next(entry for entry in monit_entries if instance_key_re.match(entry)) except StopIteration: message = 'No entries exist for {}:{}'.format(version_key, port) raise HTTPError(HTTPCodes.INTERNAL_ERROR, message=message) revision_key, port = watch[len(MONIT_INSTANCE_PREFIX):].rsplit('-', 1) port = int(port) unregister_instance(Instance(revision_key, port)) yield unmonitor_and_terminate(watch) project_prefix = ''.join([MONIT_INSTANCE_PREFIX, project_id]) remaining_instances = [entry for entry in monit_entries if entry.startswith(project_prefix) and not instance_key_re.match(entry)] if not remaining_instances: yield stop_api_server(project_id) yield monit_operator.reload() yield clean_old_sources()
def stop_app(app_name): """ Stops all process instances of a Google App Engine application on this machine. Args: app_name: Name of application to stop Returns: True on success, False otherwise """ if not misc.is_app_name_valid(app_name): logging.error("Unable to kill app process %s on because of " \ "invalid name for application" % (app_name)) return False logging.info("Stopping application %s" % app_name) watch = "app___" + app_name monit_result = monit_interface.stop(watch) if not monit_result: logging.error("Unable to shut down monit interface for watch %s" % watch) return False # Remove the monit config files for the application. # TODO: Reload monit to pick up config changes. config_files = glob.glob('{}/appscale-{}-*.cfg'.format( MONIT_CONFIG_DIR, watch)) for config_file in config_files: try: os.remove(config_file) except OSError: logging.exception('Error removing {}'.format(config_file)) if not remove_logrotate(app_name): logging.error( "Error while setting up log rotation for application: {}".format( app_name)) return True
def stop_app_instance(app_name, port): """ Stops a Google App Engine application process instance on current machine. Args: app_name: A string, the name of application to stop. port: The port the application is running on. Returns: True on success, False otherwise. """ if not misc.is_app_name_valid(app_name): logging.error("Unable to kill app process %s on port %d because of " \ "invalid name for application" % (app_name, int(port))) return False logging.info("Stopping application %s" % app_name) watch = "app___" + app_name + "-" + str(port) pid_location = os.path.join(constants.PID_DIR, '{}.pid'.format(watch)) try: with open(pid_location) as pidfile: instance_pid = int(pidfile.read().strip()) except IOError: logging.error('{} does not exist'.format(pid_location)) return False unmonitor(watch) # Now that the AppServer is stopped, remove its monit config file so that # monit doesn't pick it up and restart it. monit_config_file = '{}/appscale-{}.cfg'.format(MONIT_CONFIG_DIR, watch) try: os.remove(monit_config_file) except OSError as os_error: logging.error("Error deleting {0}".format(monit_config_file)) monit_interface.run_with_retry([monit_interface.MONIT, 'reload']) threading.Thread(target=kill_instance, args=(watch, instance_pid)).start() return True
def stop_app(version_key): """ Stops all process instances of a version on this machine. Args: version_key: Name of version to stop Returns: True on success, False otherwise """ project_id = version_key.split(VERSION_PATH_SEPARATOR)[0] if not misc.is_app_name_valid(project_id): raise BadConfigurationException( 'Invalid project ID: {}'.format(project_id)) logging.info('Stopping {}'.format(version_key)) version_group = ''.join([MONIT_INSTANCE_PREFIX, version_key]) monit_entries = yield monit_operator.get_entries() version_entries = [entry for entry in monit_entries if entry.startswith(version_group)] for entry in version_entries: revision_key, port = entry[len(MONIT_INSTANCE_PREFIX):].rsplit('-', 1) port = int(port) unregister_instance(Instance(revision_key, port)) yield unmonitor_and_terminate(entry) project_prefix = ''.join([MONIT_INSTANCE_PREFIX, project_id]) remaining_instances = [entry for entry in monit_entries if entry.startswith(project_prefix) and entry not in version_entries] if not remaining_instances: yield stop_api_server(project_id) if project_id not in projects_manager and not remove_logrotate(project_id): logging.error("Error while removing log rotation for application: {}". format(project_id)) yield monit_operator.reload() yield clean_old_sources()
def start_app(config): """ Starts a Google App Engine application on this machine. It will start it up and then proceed to fetch the main page. Args: config: a dictionary that contains app_name: Name of the application to start app_port: Port to start on language: What language the app is written in login_ip: Public ip of deployment env_vars: A dict of environment variables that should be passed to the app. max_memory: An int that names the maximum amount of memory that this App Engine app is allowed to consume before being restarted. syslog_server: The IP of the syslog server to send the application logs to. Usually it's the login private IP. Returns: PID of process on success, -1 otherwise """ config = convert_config_from_json(config) if config is None: logging.error("Invalid configuration for application") return BAD_PID if not misc.is_app_name_valid(config['app_name']): logging.error("Invalid app name for application: " + config['app_name']) return BAD_PID logging.info("Starting %s application %s" % (config['language'], config['app_name'])) env_vars = config['env_vars'] pidfile = PIDFILE_TEMPLATE.format(project=config['app_name'], port=config['app_port']) if config['language'] == constants.GO: env_vars['GOPATH'] = os.path.join('/var', 'apps', config['app_name'], 'gopath') env_vars['GOROOT'] = os.path.join(GO_SDK, 'goroot') watch = "app___" + config['app_name'] match_cmd = "" if config['language'] == constants.PYTHON27 or \ config['language'] == constants.GO or \ config['language'] == constants.PHP: start_cmd = create_python27_start_cmd(config['app_name'], config['login_ip'], config['app_port'], pidfile) stop_cmd = create_python27_stop_cmd(config['app_port']) env_vars.update( create_python_app_env(config['login_ip'], config['app_name'])) elif config['language'] == constants.JAVA: remove_conflicting_jars(config['app_name']) copy_successful = copy_modified_jars(config['app_name']) if not copy_successful: return BAD_PID # Account for MaxPermSize (~170MB), the parent process (~50MB), and thread # stacks (~20MB). max_heap = config['max_memory'] - 250 if max_heap <= 0: return BAD_PID start_cmd = create_java_start_cmd(config['app_name'], config['app_port'], config['login_ip'], max_heap, pidfile) match_cmd = "java -ea -cp.*--port={}.*{}".format( str(config['app_port']), os.path.dirname( locate_dir("/var/apps/" + config['app_name'] + "/app/", "WEB-INF"))) stop_cmd = create_java_stop_cmd(config['app_port']) env_vars.update(create_java_app_env(config['app_name'])) else: logging.error("Unknown application language %s for appname %s" \ % (config['language'], config['app_name'])) return BAD_PID logging.info("Start command: " + str(start_cmd)) logging.info("Stop command: " + str(stop_cmd)) logging.info("Environment variables: " + str(env_vars)) # Set the syslog_server is specified. syslog_server = "" if 'syslog_server' in config: syslog_server = config['syslog_server'] monit_app_configuration.create_config_file(watch, start_cmd, pidfile, config['app_port'], env_vars, config['max_memory'], syslog_server, check_port=True) # We want to tell monit to start the single process instead of the # group, since monit can get slow if there are quite a few processes in # the same group. full_watch = "{}-{}".format(str(watch), str(config['app_port'])) if not monit_interface.start(full_watch, is_group=False): logging.warning("Monit was unable to start {}:{}".format( str(config['app_name']), config['app_port'])) return BAD_PID # Since we are going to wait, possibly for a long time for the # application to be ready, we do it in a thread. threading.Thread(target=add_routing, args=(config['app_name'], config['app_port'])).start() if 'log_size' in config.keys(): log_size = config['log_size'] else: if config['app_name'] == APPSCALE_DASHBOARD_ID: log_size = DASHBOARD_LOG_SIZE else: log_size = APP_LOG_SIZE if not setup_logrotate(config['app_name'], watch, log_size): logging.error( "Error while setting up log rotation for application: {}".format( config['app_name'])) return 0
def start_app(version_key, config): """ Starts a Google App Engine application on this machine. It will start it up and then proceed to fetch the main page. Args: version_key: A string specifying a version key. config: a dictionary that contains app_port: An integer specifying the port to use. login_server: The server address the AppServer will use for login urls. """ if 'app_port' not in config: raise BadConfigurationException('app_port is required') if 'login_server' not in config or not config['login_server']: raise BadConfigurationException('login_server is required') login_server = config['login_server'] project_id, service_id, version_id = version_key.split( VERSION_PATH_SEPARATOR) if not misc.is_app_name_valid(project_id): raise BadConfigurationException( 'Invalid project ID: {}'.format(project_id)) try: service_manager = projects_manager[project_id][service_id] version_details = service_manager[version_id].version_details except KeyError: raise BadConfigurationException('Version not found') runtime = version_details['runtime'] env_vars = version_details.get('envVariables', {}) runtime_params = deployment_config.get_config('runtime_parameters') max_memory = runtime_params.get('default_max_appserver_memory', DEFAULT_MAX_APPSERVER_MEMORY) if 'instanceClass' in version_details: max_memory = INSTANCE_CLASSES.get(version_details['instanceClass'], max_memory) revision_key = VERSION_PATH_SEPARATOR.join( [project_id, service_id, version_id, str(version_details['revision'])]) source_archive = version_details['deployment']['zip']['sourceUrl'] api_server_port = yield ensure_api_server(project_id) yield source_manager.ensure_source(revision_key, source_archive, runtime) logging.info('Starting {} application {}'.format(runtime, project_id)) pidfile = PIDFILE_TEMPLATE.format(revision=revision_key, port=config['app_port']) if runtime == constants.GO: env_vars['GOPATH'] = os.path.join(UNPACK_ROOT, revision_key, 'gopath') env_vars['GOROOT'] = os.path.join(GO_SDK, 'goroot') watch = ''.join([MONIT_INSTANCE_PREFIX, revision_key]) if runtime in (constants.PYTHON27, constants.GO, constants.PHP): start_cmd = create_python27_start_cmd(project_id, login_server, config['app_port'], pidfile, revision_key, api_server_port) env_vars.update(create_python_app_env(login_server, project_id)) elif runtime == constants.JAVA: # Account for MaxPermSize (~170MB), the parent process (~50MB), and thread # stacks (~20MB). max_heap = max_memory - 250 if max_heap <= 0: raise BadConfigurationException( 'Memory for Java applications must be greater than 250MB') start_cmd = create_java_start_cmd(project_id, config['app_port'], login_server, max_heap, pidfile, revision_key, api_server_port) env_vars.update(create_java_app_env(project_id)) else: raise BadConfigurationException('Unknown runtime {} for {}'.format( runtime, project_id)) logging.info("Start command: " + str(start_cmd)) logging.info("Environment variables: " + str(env_vars)) monit_app_configuration.create_config_file(watch, start_cmd, pidfile, config['app_port'], env_vars, max_memory, options.syslog_server, check_port=True, kill_exceeded_memory=True) full_watch = '{}-{}'.format(watch, config['app_port']) monit_operator = MonitOperator() yield monit_operator.reload(thread_pool) yield monit_operator.send_command_retry_process(full_watch, 'start') # Make sure the version node exists. zk_client.ensure_path('/'.join([VERSION_REGISTRATION_NODE, version_key])) # Since we are going to wait, possibly for a long time for the # application to be ready, we do it later. IOLoop.current().spawn_callback(add_routing, Instance(revision_key, config['app_port'])) if project_id == DASHBOARD_PROJECT_ID: log_size = DASHBOARD_LOG_SIZE else: log_size = APP_LOG_SIZE if not setup_logrotate(project_id, log_size): logging.error( "Error while setting up log rotation for application: {}".format( project_id))
def start_app(version_key, config): """ Starts a Google App Engine application on this machine. It will start it up and then proceed to fetch the main page. Args: version_key: A string specifying a version key. config: a dictionary that contains app_port: An integer specifying the port to use. """ if 'app_port' not in config: raise BadConfigurationException('app_port is required') project_id, service_id, version_id = version_key.split( VERSION_PATH_SEPARATOR) if not misc.is_app_name_valid(project_id): raise BadConfigurationException( 'Invalid project ID: {}'.format(project_id)) try: service_manager = projects_manager[project_id][service_id] version_details = service_manager[version_id].version_details except KeyError: raise BadConfigurationException('Version not found') runtime = version_details['runtime'] env_vars = version_details.get('envVariables', {}) runtime_params = deployment_config.get_config('runtime_parameters') max_memory = runtime_params.get('default_max_appserver_memory', DEFAULT_MAX_APPSERVER_MEMORY) if 'instanceClass' in version_details: max_memory = INSTANCE_CLASSES.get(version_details['instanceClass'], max_memory) version_key = VERSION_PATH_SEPARATOR.join( [project_id, service_id, version_id]) revision_key = VERSION_PATH_SEPARATOR.join( [project_id, service_id, version_id, str(version_details['revision'])]) source_archive = version_details['deployment']['zip']['sourceUrl'] yield source_manager.ensure_source(revision_key, source_archive, runtime) logging.info('Starting {} application {}'.format(runtime, project_id)) pidfile = PIDFILE_TEMPLATE.format(revision=revision_key, port=config['app_port']) if runtime == constants.GO: env_vars['GOPATH'] = os.path.join(UNPACK_ROOT, revision_key, 'gopath') env_vars['GOROOT'] = os.path.join(GO_SDK, 'goroot') watch = ''.join([MONIT_INSTANCE_PREFIX, revision_key]) if runtime in (constants.PYTHON27, constants.GO, constants.PHP): start_cmd = create_python27_start_cmd(project_id, options.login_ip, config['app_port'], pidfile, revision_key) env_vars.update(create_python_app_env(options.login_ip, project_id)) elif runtime == constants.JAVA: # Account for MaxPermSize (~170MB), the parent process (~50MB), and thread # stacks (~20MB). max_heap = max_memory - 250 if max_heap <= 0: raise BadConfigurationException( 'Memory for Java applications must be greater than 250MB') start_cmd = create_java_start_cmd(project_id, config['app_port'], options.login_ip, max_heap, pidfile, revision_key) env_vars.update(create_java_app_env(project_id)) else: raise BadConfigurationException('Unknown runtime {} for {}'.format( runtime, project_id)) logging.info("Start command: " + str(start_cmd)) logging.info("Environment variables: " + str(env_vars)) monit_app_configuration.create_config_file(watch, start_cmd, pidfile, config['app_port'], env_vars, max_memory, options.syslog_server, check_port=True) # We want to tell monit to start the single process instead of the # group, since monit can get slow if there are quite a few processes in # the same group. full_watch = '{}-{}'.format(watch, config['app_port']) assert monit_interface.start( full_watch, is_group=False), ('Monit was unable to start {}:{}'.format( project_id, config['app_port'])) # Since we are going to wait, possibly for a long time for the # application to be ready, we do it in a thread. threading.Thread(target=add_routing, args=(version_key, config['app_port'])).start() if project_id == DASHBOARD_PROJECT_ID: log_size = DASHBOARD_LOG_SIZE else: log_size = APP_LOG_SIZE if not setup_logrotate(project_id, log_size): logging.error( "Error while setting up log rotation for application: {}".format( project_id))
def start_app(version_key, config): """ Starts a Google App Engine application on this machine. It will start it up and then proceed to fetch the main page. Args: version_key: A string specifying a version key. config: a dictionary that contains app_port: An integer specifying the port to use. login_server: The server address the AppServer will use for login urls. """ if 'app_port' not in config: raise BadConfigurationException('app_port is required') if 'login_server' not in config or not config['login_server']: raise BadConfigurationException('login_server is required') login_server = config['login_server'] project_id, service_id, version_id = version_key.split( VERSION_PATH_SEPARATOR) if not misc.is_app_name_valid(project_id): raise BadConfigurationException( 'Invalid project ID: {}'.format(project_id)) try: service_manager = projects_manager[project_id][service_id] version_details = service_manager[version_id].version_details except KeyError: raise BadConfigurationException('Version not found') runtime = version_details['runtime'] env_vars = version_details.get('envVariables', {}) runtime_params = deployment_config.get_config('runtime_parameters') max_memory = runtime_params.get('default_max_appserver_memory', DEFAULT_MAX_APPSERVER_MEMORY) if 'instanceClass' in version_details: max_memory = INSTANCE_CLASSES.get(version_details['instanceClass'], max_memory) version_key = VERSION_PATH_SEPARATOR.join( [project_id, service_id, version_id]) revision_key = VERSION_PATH_SEPARATOR.join( [project_id, service_id, version_id, str(version_details['revision'])]) source_archive = version_details['deployment']['zip']['sourceUrl'] api_server_port = ensure_api_server(project_id) yield source_manager.ensure_source(revision_key, source_archive, runtime) logging.info('Starting {} application {}'.format(runtime, project_id)) pidfile = PIDFILE_TEMPLATE.format(revision=revision_key, port=config['app_port']) if runtime == constants.GO: env_vars['GOPATH'] = os.path.join(UNPACK_ROOT, revision_key, 'gopath') env_vars['GOROOT'] = os.path.join(GO_SDK, 'goroot') watch = ''.join([MONIT_INSTANCE_PREFIX, revision_key]) if runtime in (constants.PYTHON27, constants.GO, constants.PHP): start_cmd = create_python27_start_cmd( project_id, login_server, config['app_port'], pidfile, revision_key, api_server_port) env_vars.update(create_python_app_env( login_server, project_id)) elif runtime == constants.JAVA: # Account for MaxPermSize (~170MB), the parent process (~50MB), and thread # stacks (~20MB). max_heap = max_memory - 250 if max_heap <= 0: raise BadConfigurationException( 'Memory for Java applications must be greater than 250MB') start_cmd = create_java_start_cmd( project_id, config['app_port'], login_server, max_heap, pidfile, revision_key ) env_vars.update(create_java_app_env(project_id)) else: raise BadConfigurationException( 'Unknown runtime {} for {}'.format(runtime, project_id)) logging.info("Start command: " + str(start_cmd)) logging.info("Environment variables: " + str(env_vars)) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, config['app_port'], env_vars, max_memory, options.syslog_server, check_port=True, kill_exceeded_memory=True) # We want to tell monit to start the single process instead of the # group, since monit can get slow if there are quite a few processes in # the same group. full_watch = '{}-{}'.format(watch, config['app_port']) assert monit_interface.start(full_watch, is_group=False), ( 'Monit was unable to start {}:{}'.format(project_id, config['app_port'])) # Since we are going to wait, possibly for a long time for the # application to be ready, we do it in a thread. threading.Thread(target=add_routing, args=(version_key, config['app_port'])).start() if project_id == DASHBOARD_PROJECT_ID: log_size = DASHBOARD_LOG_SIZE else: log_size = APP_LOG_SIZE if not setup_logrotate(project_id, log_size): logging.error("Error while setting up log rotation for application: {}". format(project_id))