Source code for supercell.api.service

# vim: set fileencoding=utf-8 :
#
# Copyright (c) 2013 Daniel Truemper <truemped at googlemail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
'''A :class:`Service` is the main element of a `supercell` application. It will
instanciate the :class:`supercell.api.Environment` and parse the configuration
files as well as the command line. In the final step the
:class:`tornado.web.Application` is created and bound to a socket.
'''
from __future__ import absolute_import, division, print_function, with_statement

import logging.config
import os

import tornado.options
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.options import define

from supercell.api.environment import Environment
from supercell.api.healthchecks import SystemHealthCheck


define('logconf', default='logging.cfg',
       help='Name of the logging configuration file')


define('port', default=8080, help='Port to listen on')


define('address', default='127.0.0.1', help='Address to bind on')


define('socketfd', default=None, help='Filedescriptor used from circus')


[docs]class Service(object): '''Main service implementation managing the :class:`tornado.web.Application` and taking care of configuration.'''
[docs] def main(self): '''Main method starting a **supercell** process. This will first instantiate the :class:`tornado.web.Application` and then bind it to the socket. There are two possibilities to bind to a socket: either by binding to a certain port and address as defined by the configuration (the *port* and *address* configuration settings) or by the *socketfd* command line parameter. The latter is mainly used in combination with Circus (http://circus.readthedocs.org/). There you would bind the socket from circus and start the worker processes by binding to the file descriptor. ''' app = self.get_app() server = HTTPServer(app) if self.config.socketfd: import socket sock = socket.fromfd(int(self.config.socketfd), socket.AF_INET, socket.SOCK_STREAM) server.add_socket(sock) else: server.bind(self.config.port, address=self.config.address) server.start(1) self.slog.info('Starting supercell') IOLoop.instance().start()
[docs] def get_app(self): '''Create the :class:`tornado.web.Appliaction` instance and return it. In this method the :func:`Service.bootstrap()` is called, then :func:`Service.run()` will initialize the app.''' # initialize the environment self.environment # bootstrap the service self.bootstrap() # perform all the configuration parsing self.config # add handlers, health checks, managed objects to the environment self.run() self.environment.add_handler('/_system', SystemHealthCheck, {}) for check_name in self.environment.health_checks: check = self.environment.health_checks[check_name] self.environment.add_handler('/_system/%s' % check, check, {}) # do not allow any changes on the environment anymore. self.environment._finalize() return self.environment._application(self.config)
@property
[docs] def slog(self): '''Initialize the logging and return the logger.''' if not hasattr(self, '_slog'): self._slog = self.initialize_logging() return self._slog
@property
[docs] def environment(self): '''The default environment instance.''' if not hasattr(self, '_environment'): self._environment = Environment() return self._environment
@property
[docs] def config(self): '''Assemble the configration files and command line arguments in order to finalize the service's configuration. All configuration values can be overwritten by the command line.''' if not hasattr(self, '_config'): # parse config files and command line arguments self.parse_config_files() self.parse_command_line() from tornado.options import options self._config = options return self._config
[docs] def parse_config_files(self): '''Parse the config files and return the `config` object, i.e. the `tornado.options.options` instance. For each entry in the `Environment.config_file_paths()` it will check for a general *config.py* and then for a file named as defined by `Environment.config_name`. So if the config file paths are set to `['/etc/myservice', './etc/']` the following files are parsed:: /etc/myservice/config.cfg /etc/myservice/user_hostname.cfg ./etc/config.cfg ./etc/user_hostname.cfg ''' filename = self.environment.config_name for path in self.environment.config_file_paths: cfg = os.path.join(path, 'config.cfg') if os.path.exists(cfg): tornado.options.parse_config_file(cfg) cfg = os.path.join(path, filename) if os.path.exists(cfg): tornado.options.parse_config_file(cfg)
[docs] def parse_command_line(self): '''Parse the command line arguments to set different configuration values.''' tornado.options.parse_command_line()
[docs] def initialize_logging(self, name='supercell'): '''Initialize the python logging system.''' logging.config.fileConfig(self.config.logconf) slog = logging.getLogger(name) ts = self.environment.tornado_settings ts['log_function'] = self.environment.tornado_log_function(slog) return slog
[docs] def bootstrap(self): '''Implement this method in order to manipulate the configuration paths, e.g..''' pass
[docs] def run(self): '''Implement this method in order to add handlers and managed objects to the environment, before the app is started.''' pass

Project Versions

This Page