def test_create_config_file(self): flexmock(file_io).should_receive('write') monit_app_configuration.create_config_file('mywatch', 'start_cmd', 'pidfile', 4000, { 'ENV1': 'VALUE1', 'ENV2': 'VALUE2' })
def update_worker(self, queue_config): """ Updates a worker's configuration and restarts it. Args: queue_config: A JSON string specifying queue configuration. """ self._write_worker_configuration(queue_config) status = yield self._wait_for_stable_state() # Start the worker if it doesn't exist. Restart it if it does. if status == MonitStates.MISSING: command = self.celery_command() env_vars = { 'APP_ID': self.project_id, 'HOST': options.load_balancers[0], 'C_FORCE_ROOT': True } pidfile = os.path.join(PID_DIR, 'celery-{}.pid'.format(self.project_id)) create_config_file(self.monit_watch, command, pidfile, env_vars=env_vars, max_memory=CELERY_SAFE_MEMORY) logger.info('Starting push worker for {}'.format(self.project_id)) yield self.monit_operator.reload() else: logger.info('Restarting push worker for {}'.format( self.project_id)) yield self.monit_operator.send_command(self.monit_watch, 'restart') start_future = self.monit_operator.ensure_running(self.monit_watch) yield gen.with_timeout(timedelta(seconds=60), start_future, IOLoop.current())
def update_worker(self, queue_config): """ Updates a worker's configuration and restarts it. Args: queue_config: A JSON string specifying queue configuration. """ self._write_worker_configuration(queue_config) status = yield self._wait_for_stable_state() pid_location = os.path.join(VAR_DIR, 'celery-{}.pid'.format(self.project_id)) try: with open(pid_location) as pidfile: old_pid = int(pidfile.read().strip()) except IOError: old_pid = None # Start the worker if it doesn't exist. Restart it if it does. if status == MonitStates.MISSING: command = self.celery_command() env_vars = { 'APP_ID': self.project_id, 'HOST': options.load_balancers[0], 'C_FORCE_ROOT': True } create_config_file(self.monit_watch, command, pid_location, env_vars=env_vars, max_memory=CELERY_SAFE_MEMORY) logger.info('Starting push worker for {}'.format(self.project_id)) yield self.monit_operator.reload() else: logger.info('Restarting push worker for {}'.format( self.project_id)) yield self.monit_operator.send_command(self.monit_watch, 'restart') start_future = self.monit_operator.ensure_running(self.monit_watch) yield gen.with_timeout(timedelta(seconds=60), start_future, IOLoop.current()) try: yield self.ensure_pid_changed(old_pid, pid_location) except AssertionError: # Occasionally, Monit will get interrupted during a restart. Retry the # restart if the Celery worker PID is the same. logger.warning( '{} worker PID did not change. Restarting it.'.format( self.project_id)) yield self.update_worker(queue_config)
def test_create_config_file(self): flexmock(file_io)\ .should_receive('write')\ .and_return() temp_file = monit_app_configuration.create_config_file( "mywatch", "start_cmd", "stop_cmd", [1, 2, 3], { 'ENV1': "VALUE1", 'ENV2': "VALUE2" }) self.assertIsNone(temp_file)
def update_worker(self, queue_config): """ Updates a worker's configuration and restarts it. Args: queue_config: A JSON string specifying queue configuration. """ self._write_worker_configuration(queue_config) status = yield self._wait_for_stable_state() pid_location = os.path.join(PID_DIR, 'celery-{}.pid'.format(self.project_id)) try: with open(pid_location) as pidfile: old_pid = int(pidfile.read().strip()) except IOError: old_pid = None # Start the worker if it doesn't exist. Restart it if it does. if status == MonitStates.MISSING: command = self.celery_command() env_vars = {'APP_ID': self.project_id, 'HOST': options.load_balancers[0], 'C_FORCE_ROOT': True} create_config_file(self.monit_watch, command, pid_location, env_vars=env_vars, max_memory=CELERY_SAFE_MEMORY) logger.info('Starting push worker for {}'.format(self.project_id)) yield self.monit_operator.reload() else: logger.info('Restarting push worker for {}'.format(self.project_id)) yield self.monit_operator.send_command(self.monit_watch, 'restart') start_future = self.monit_operator.ensure_running(self.monit_watch) yield gen.with_timeout(timedelta(seconds=60), start_future, IOLoop.current()) try: yield self.ensure_pid_changed(old_pid, pid_location) except AssertionError: # Occasionally, Monit will get interrupted during a restart. Retry the # restart if the Celery worker PID is the same. logger.warning( '{} worker PID did not change. Restarting it.'.format(self.project_id)) yield self.update_worker(queue_config)
def ensure_api_server(project_id): """ Make sure there is a running API server for a project. Args: project_id: A string specifying the project ID. Returns: An integer specifying the API server port. """ global api_servers if project_id in api_servers: return api_servers[project_id] server_port = MAX_API_SERVER_PORT for port in api_servers.values(): if port <= server_port: server_port = port - 1 zk_locations = appscale_info.get_zk_node_ips() start_cmd = ' '.join([ API_SERVER_LOCATION, '--port', str(server_port), '--project-id', project_id, '--zookeeper-locations', ' '.join(zk_locations) ]) watch = ''.join([API_SERVER_PREFIX, project_id]) full_watch = '-'.join([watch, str(server_port)]) pidfile = os.path.join(PID_DIR, '{}.pid'.format(full_watch)) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, server_port, max_memory=DEFAULT_MAX_APPSERVER_MEMORY, check_port=True) assert monit_interface.start( full_watch, is_group=False), ('Monit was unable to start {}'.format(watch)) api_servers[project_id] = server_port return server_port
def ensure_api_server(project_id): """ Make sure there is a running API server for a project. Args: project_id: A string specifying the project ID. Returns: An integer specifying the API server port. """ global api_servers if project_id in api_servers: raise gen.Return(api_servers[project_id]) server_port = MAX_API_SERVER_PORT for port in api_servers.values(): if port <= server_port: server_port = port - 1 zk_locations = appscale_info.get_zk_node_ips() start_cmd = ' '.join([ API_SERVER_LOCATION, '--port', str(server_port), '--project-id', project_id, '--zookeeper-locations', ' '.join(zk_locations) ]) watch = ''.join([API_SERVER_PREFIX, project_id]) full_watch = '-'.join([watch, str(server_port)]) pidfile = os.path.join(VAR_DIR, '{}.pid'.format(full_watch)) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, server_port, max_memory=DEFAULT_MAX_APPSERVER_MEMORY, check_port=True) monit_operator = MonitOperator() yield monit_operator.reload(thread_pool) yield monit_operator.send_command_retry_process(full_watch, 'start') api_servers[project_id] = server_port raise gen.Return(server_port)
def ensure_api_server(project_id): """ Make sure there is a running API server for a project. Args: project_id: A string specifying the project ID. Returns: An integer specifying the API server port. """ global api_servers if project_id in api_servers: return api_servers[project_id] server_port = MAX_API_SERVER_PORT for port in api_servers.values(): if port <= server_port: server_port = port - 1 zk_locations = appscale_info.get_zk_node_ips() start_cmd = ' '.join([API_SERVER_LOCATION, '--port', str(server_port), '--project-id', project_id, '--zookeeper-locations', ' '.join(zk_locations)]) watch = ''.join([API_SERVER_PREFIX, project_id]) full_watch = '-'.join([watch, str(server_port)]) pidfile = os.path.join(PID_DIR, '{}.pid'.format(full_watch)) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, server_port, max_memory=DEFAULT_MAX_APPSERVER_MEMORY, check_port=True) assert monit_interface.start(full_watch, is_group=False), ( 'Monit was unable to start {}'.format(watch)) api_servers[project_id] = server_port return server_port
def _ensure_api_server(self, project_id): """ Make sure there is a running API server for a project. Args: project_id: A string specifying the project ID. Returns: An integer specifying the API server port. """ if project_id in self._api_servers: raise gen.Return(self._api_servers[project_id]) server_port = MAX_API_SERVER_PORT for port in self._api_servers.values(): if port <= server_port: server_port = port - 1 zk_locations = appscale_info.get_zk_node_ips() start_cmd = ' '.join([API_SERVER_LOCATION, '--port', str(server_port), '--project-id', project_id, '--zookeeper-locations', ' '.join(zk_locations)]) watch = ''.join([API_SERVER_PREFIX, project_id]) full_watch = '-'.join([watch, str(server_port)]) pidfile = os.path.join(VAR_DIR, '{}.pid'.format(full_watch)) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, server_port, max_memory=DEFAULT_MAX_APPSERVER_MEMORY, check_port=True) yield self._monit_operator.reload(self._thread_pool) yield self._monit_operator.send_command_retry_process(full_watch, 'start') self._api_servers[project_id] = server_port raise gen.Return(server_port)
def start_service(service_name): """ Creates a monit configuration file and prompts Monit to start service. Args: service_name: The name of the service to start. """ logging.info("Starting " + service_name) watch_name = "" if service_name == datastore_upgrade.CASSANDRA_WATCH_NAME: cassandra_cmd = CASSANDRA_EXECUTABLE + " -p " + PID_FILE start_cmd = 'su -c "{0}" cassandra'.format(cassandra_cmd) stop_cmd = "/usr/bin/python2 " + APPSCALE_HOME + "/scripts/stop_service.py java cassandra" watch_name = datastore_upgrade.CASSANDRA_WATCH_NAME ports = [CASSANDRA_PORT] match_cmd = cassandra_interface.CASSANDRA_INSTALL_DIR if service_name == datastore_upgrade.ZK_WATCH_NAME: zk_server="zookeeper-server" command = 'service --status-all|grep zookeeper$' if subprocess.call(command, shell=True) == 0: zk_server = "zookeeper" start_cmd = "/usr/sbin/service " + zk_server + " start" stop_cmd = "/usr/sbin/service " + zk_server + " stop" watch_name = datastore_upgrade.ZK_WATCH_NAME match_cmd = "org.apache.zookeeper.server.quorum.QuorumPeerMain" ports = [zk.DEFAULT_PORT] monit_app_configuration.create_config_file(watch_name, start_cmd, stop_cmd, ports, upgrade_flag=True, match_cmd=match_cmd) if not monit_interface.start(watch_name): logging.error("Monit was unable to start " + service_name) return 1 else: logging.info('Monit configured for {}'.format(service_name)) return 0
def test_create_config_file(self): flexmock(file_io).should_receive('write') monit_app_configuration.create_config_file( 'mywatch', 'start_cmd', 'pidfile', 4000, {'ENV1': 'VALUE1', 'ENV2': 'VALUE2'})
def _start_instance(self, version, port): """ Starts a Google App Engine application on this machine. It will start it up and then proceed to fetch the main page. Args: version: A Version object. port: An integer specifying a port to use. """ version_details = version.version_details runtime = version_details['runtime'] env_vars = version_details.get('envVariables', {}) runtime_params = self._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) source_archive = version_details['deployment']['zip']['sourceUrl'] api_server_port = yield self._ensure_api_server(version.project_id) yield self._source_manager.ensure_source( version.revision_key, source_archive, runtime) logger.info('Starting {}:{}'.format(version, port)) pidfile = PIDFILE_TEMPLATE.format(revision=version.revision_key, port=port) if runtime == GO: env_vars['GOPATH'] = os.path.join(UNPACK_ROOT, version.revision_key, 'gopath') env_vars['GOROOT'] = os.path.join(GO_SDK, 'goroot') watch = ''.join([MONIT_INSTANCE_PREFIX, version.revision_key]) if runtime in (PYTHON27, GO, PHP): start_cmd = create_python27_start_cmd( version.project_id, self._login_server, port, pidfile, version.revision_key, api_server_port) env_vars.update(create_python_app_env(self._login_server, version.project_id)) elif runtime == 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( version.project_id, port, self._login_server, max_heap, pidfile, version.revision_key, api_server_port ) env_vars.update(create_java_app_env(self._deployment_config)) else: raise BadConfigurationException( 'Unknown runtime {} for {}'.format(runtime, version.project_id)) logger.info("Start command: " + str(start_cmd)) logger.info("Environment variables: " + str(env_vars)) base_version = version.revision_key.rsplit(VERSION_PATH_SEPARATOR, 1)[0] log_tag = "app_{}".format(hashlib.sha1(base_version).hexdigest()[:28]) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, port, env_vars, max_memory, self._syslog_server, check_port=True, kill_exceeded_memory=True, log_tag=log_tag, ) full_watch = '{}-{}'.format(watch, port) yield self._monit_operator.reload(self._thread_pool) # The reload command does not block, and we don't have a good way to check # if Monit is ready with its new configuration yet. If the daemon begins # reloading while it is handling the 'start', it can end up in a state # where it never starts the process. As a temporary workaround, this # small period allows it to finish reloading. This can be removed if # instances are started inside a cgroup. yield gen.sleep(1) yield self._monit_operator.send_command_retry_process(full_watch, 'start') # Make sure the version registration node exists. self._zk_client.ensure_path( '/'.join([VERSION_REGISTRATION_NODE, version.version_key])) instance = Instance(version.revision_key, port) yield self._add_routing(instance) if version.project_id == DASHBOARD_PROJECT_ID: log_size = DASHBOARD_LOG_SIZE else: log_size = APP_LOG_SIZE if not setup_logrotate(version.project_id, log_size): logger.error("Error while setting up log rotation for application: {}". format(version.project_id))
def _start_instance(self, version, port): """ Starts a Google App Engine application on this machine. It will start it up and then proceed to fetch the main page. Args: version: A Version object. port: An integer specifying a port to use. """ version_details = version.version_details runtime = version_details['runtime'] env_vars = version_details.get('envVariables', {}) runtime_params = self._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) source_archive = version_details['deployment']['zip']['sourceUrl'] api_server_port = yield self._ensure_api_server(version.project_id) yield self._source_manager.ensure_source(version.revision_key, source_archive, runtime) logger.info('Starting {}:{}'.format(version, port)) pidfile = PIDFILE_TEMPLATE.format(revision=version.revision_key, port=port) if runtime == GO: env_vars['GOPATH'] = os.path.join(UNPACK_ROOT, version.revision_key, 'gopath') env_vars['GOROOT'] = os.path.join(GO_SDK, 'goroot') watch = ''.join([MONIT_INSTANCE_PREFIX, version.revision_key]) if runtime in (PYTHON27, GO, PHP): start_cmd = create_python27_start_cmd(version.project_id, self._login_server, port, pidfile, version.revision_key, api_server_port) env_vars.update( create_python_app_env(self._login_server, version.project_id)) elif runtime == 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(version.project_id, port, self._login_server, max_heap, pidfile, version.revision_key, api_server_port) env_vars.update(create_java_app_env(self._deployment_config)) else: raise BadConfigurationException('Unknown runtime {} for {}'.format( runtime, version.project_id)) logger.info("Start command: " + str(start_cmd)) logger.info("Environment variables: " + str(env_vars)) monit_app_configuration.create_config_file(watch, start_cmd, pidfile, port, env_vars, max_memory, self._syslog_server, check_port=True, kill_exceeded_memory=True) full_watch = '{}-{}'.format(watch, port) yield self._monit_operator.reload(self._thread_pool) # The reload command does not block, and we don't have a good way to check # if Monit is ready with its new configuration yet. If the daemon begins # reloading while it is handling the 'start', it can end up in a state # where it never starts the process. As a temporary workaround, this # small period allows it to finish reloading. This can be removed if # instances are started inside a cgroup. yield gen.sleep(0.5) yield self._monit_operator.send_command_retry_process( full_watch, 'start') # Make sure the version registration node exists. self._zk_client.ensure_path('/'.join( [VERSION_REGISTRATION_NODE, version.version_key])) instance = Instance(version.revision_key, port) yield self._add_routing(instance) if version.project_id == DASHBOARD_PROJECT_ID: log_size = DASHBOARD_LOG_SIZE else: log_size = APP_LOG_SIZE if not setup_logrotate(version.project_id, log_size): logger.error( "Error while setting up log rotation for application: {}". format(version.project_id))
def _ensure_api_server(self, project_id, runtime): """ Make sure there is a running API server for a project. Args: project_id: A string specifying the project ID. runtime: The runtime for the project Returns: An integer specifying the API server port. """ ensure_app_server_api = runtime==JAVA8 if project_id in self._api_servers: api_server_ports = self._api_servers[project_id] if not ensure_app_server_api: raise gen.Return(api_server_ports[0]) elif len(api_server_ports) > 1: raise gen.Return(api_server_ports[1]) server_port = MAX_API_SERVER_PORT for ports in self._api_servers.values(): for port in ports: if port <= server_port: server_port = port - 1 full_watch = None if not project_id in self._api_servers: watch = ''.join([API_SERVER_PREFIX, project_id]) full_watch = '-'.join([watch, str(server_port)]) pidfile = os.path.join(VAR_DIR, '{}.pid'.format(full_watch)) zk_locations = appscale_info.get_zk_node_ips() start_cmd = ' '.join([API_SERVER_LOCATION, '--port', str(server_port), '--project-id', project_id, '--zookeeper-locations', ' '.join(zk_locations)]) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, server_port, max_memory=DEFAULT_MAX_APPSERVER_MEMORY, check_port=True, check_host='127.0.0.1') api_server_port = server_port else: api_server_port = self._api_servers[project_id][0] full_watch_app = None if ensure_app_server_api: # Start an Python 27 runtime API server if api_server_port==server_port: server_port -= 1 watch = ''.join([API_SERVER_PREFIX, '1_', project_id]) full_watch_app = '-'.join([watch, str(server_port)]) pidfile = os.path.join(VAR_DIR, '{}.pid'.format(full_watch_app)) start_cmd = create_python_api_start_cmd(project_id, self._login_server, server_port, pidfile, api_server_port) monit_app_configuration.create_config_file( watch, start_cmd, pidfile, server_port, max_memory=DEFAULT_MAX_APPSERVER_MEMORY, check_port=True, check_host='127.0.0.1', group='api-server') self._api_servers[project_id] = [api_server_port, server_port] else: self._api_servers[project_id] = [server_port] yield self._monit_operator.reload(self._thread_pool) if full_watch: yield self._monit_operator.send_command_retry_process(full_watch, 'start') if full_watch_app: yield self._monit_operator.send_command_retry_process(full_watch_app, 'start') raise gen.Return(server_port)
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. 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))
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))