206 lines
5.5 KiB
Python
206 lines
5.5 KiB
Python
import re
|
|
import six
|
|
|
|
from abc import ABCMeta, abstractproperty
|
|
|
|
from ..api import Block, Location
|
|
from .exceptions import ConfigBuilderException
|
|
|
|
|
|
@six.add_metaclass(ABCMeta)
|
|
class Navigable(object):
|
|
""" Indicates that a class is navigable.
|
|
|
|
This means that it references some type of nginx config machinery,
|
|
and that this is traversable.
|
|
|
|
"""
|
|
_config_builder = None
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
""" Creates a new Navigable class
|
|
|
|
:param nginx.builder.ConfigBuilder config_builder: internal ConfigBuilder used to create nginx config objs
|
|
"""
|
|
# This can sometimes be added by direct access,
|
|
# in the case of plugins
|
|
self._config_builder = kwargs.get('config_builder', None)
|
|
self._parent = kwargs.get('parent', None)
|
|
|
|
def chobj(self, obj):
|
|
""" Changes the current working object to the one provided.
|
|
|
|
:param nginx.config.Block obj: object that we're scoping to
|
|
"""
|
|
self.config_builder._cwo = obj
|
|
|
|
@property
|
|
def current_obj(self):
|
|
""" Returns the current working object.
|
|
|
|
:returns nginx.config.Block: object that we're currently scoped to
|
|
"""
|
|
return self.config_builder._cwo
|
|
|
|
@property
|
|
def config_builder(self):
|
|
""" Internal config builder.
|
|
|
|
:returns nginx.builder.ConfigBuilder: the internal ConfigBuilder for manipulating the nginx config
|
|
"""
|
|
return self._config_builder
|
|
|
|
def up(self):
|
|
""" Traverse up the config hierarchy """
|
|
self.chobj(self.current_obj.parent)
|
|
|
|
def add_child(self, child):
|
|
""" Adds a child to the config object
|
|
|
|
:param nginx.config.Builder child: child to insert into config tree
|
|
"""
|
|
name = re.split(r'\s+', self.current_obj.name)[0]
|
|
if self.valid_cfg_parents and name not in self.valid_cfg_parents:
|
|
raise ConfigBuilderException(
|
|
'{parent} is not a valid parent for this plugin. Call this off of one of these: {valid_parents}'.format(
|
|
parent=name if name else 'top',
|
|
valid_parents=self.valid_cfg_parents
|
|
), plugin=self._get_name()
|
|
)
|
|
|
|
self.current_obj.sections.add(child)
|
|
|
|
def __getattr__(self, attr):
|
|
return getattr(self.config_builder, attr)
|
|
|
|
@abstractproperty
|
|
def valid_cfg_parents(self):
|
|
return None
|
|
|
|
@property
|
|
def parent(self):
|
|
return self._parent
|
|
|
|
def _get_name(self):
|
|
return getattr(self, 'name', self.current_obj.name)
|
|
|
|
|
|
@six.add_metaclass(ABCMeta)
|
|
class Plugin(Navigable):
|
|
""" Plugin base class. All plugins must inherit from this
|
|
|
|
Defines a few properties that must be defined:
|
|
|
|
name - name of the plugin. must be unique per config builder
|
|
exported_methods - dict of method names -> callables. method names don't need to match
|
|
callables. exported method names must be unique
|
|
"""
|
|
|
|
@abstractproperty
|
|
def name(self):
|
|
return ''
|
|
|
|
@abstractproperty
|
|
def exported_methods(self):
|
|
return {}
|
|
|
|
@property
|
|
def http(self):
|
|
return self.config_builder._http
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
@six.add_metaclass(ABCMeta)
|
|
class Endable(Navigable):
|
|
""" Role that adds an `end` convenience method to close scoped blocks (location, server, et al) """
|
|
|
|
def end(self):
|
|
self.up()
|
|
return self.config_builder
|
|
|
|
|
|
@six.add_metaclass(ABCMeta)
|
|
class Routable(Navigable):
|
|
""" A thing that can have routes attached to it.
|
|
|
|
In nginx, routes can either be attached to server blocks or other routes
|
|
|
|
"""
|
|
|
|
def add_route(self, path, *args, **kwargs):
|
|
loc = Location(path, *args, **kwargs)
|
|
self.add_child(loc)
|
|
self.chobj(loc)
|
|
|
|
return RouteWrapper(self.current_obj, self.config_builder)
|
|
|
|
|
|
@six.add_metaclass(ABCMeta)
|
|
class Wrapper(object):
|
|
def __getattr__(self, attr):
|
|
return getattr(self.config_builder, attr)
|
|
|
|
|
|
class RouteWrapper(Routable, Wrapper, Endable):
|
|
""" This needs to wrap routes emitted by this interface since we can nest routes in nginx configs.
|
|
|
|
This also enables users to use the returned routes/servers as a context manager, so that we can
|
|
sugar-coat syntax even more than we already do
|
|
|
|
"""
|
|
valid_cfg_parents = ('server', 'location')
|
|
|
|
def __init__(self, wrapped, config_builder):
|
|
super(RouteWrapper, self).__init__(
|
|
parent=wrapped,
|
|
config_builder=config_builder
|
|
)
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, typ, val, tb):
|
|
self.end()
|
|
|
|
|
|
class RoutePlugin(Plugin, Routable, Endable):
|
|
""" A plugin that creates routes
|
|
|
|
Routes can be nested infinitely.
|
|
|
|
Must be called off of either a server or a location block
|
|
"""
|
|
name = 'route'
|
|
valid_cfg_parents = ('location', 'server')
|
|
|
|
@property
|
|
def exported_methods(self):
|
|
return {
|
|
'add_route': self.add_route,
|
|
}
|
|
|
|
|
|
class ServerPlugin(Plugin, Routable, Endable):
|
|
""" A plugin that creates server blocks
|
|
|
|
Must only be called off of an http block
|
|
"""
|
|
name = 'server'
|
|
valid_cfg_parents = ('http',)
|
|
|
|
# XXX: add more server options
|
|
def add_server(self, hostname='_', **kwargs):
|
|
server = Block('server', server_name=hostname, **kwargs)
|
|
self.add_child(server)
|
|
self.chobj(server)
|
|
|
|
return RouteWrapper(self.current_obj, self.config_builder)
|
|
|
|
@property
|
|
def exported_methods(self):
|
|
return {
|
|
'add_server': self.add_server,
|
|
'end': self.end
|
|
}
|