Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: nrh/pyrange
base: master
...
head fork: nrh/pyrange
compare: bottle
Checking mergeability… Don't worry, you can still create the pull request.
  • 12 commits
  • 35 files changed
  • 0 commit comments
  • 1 contributor
Showing with 901 additions and 911 deletions.
  1. +61 −2 README.md
  2. +2 −2 bin/pyrange-server
  3. +0 −39 pyrange/acl.py
  4. +0 −306 pyrange/api_handler.py
  5. +28 −10 pyrange/config.py
  6. +177 −0 pyrange/core.py
  7. +60 −0 pyrange/create_db.py
  8. +276 −0 pyrange/handler.py
  9. +0 −30 pyrange/namespace.py
  10. 0  pyrange/range_handler.py
  11. +79 −26 pyrange/range_parser.py
  12. +0 −38 pyrange/request.py
  13. +0 −30 pyrange/role.py
  14. +15 −0 pyrange/server.py
  15. +83 −9 pyrange/store.py
  16. 0  pyrange/store_sqlite.py
  17. +0 −20 pyrange/wsgiserver.py
  18. +63 −20 test/01_namespace_test.py
  19. +24 −0 test/10_range_parser_test.data
  20. +30 −0 test/10_range_parser_test.py
  21. +0 −15 test/nginx/cert.key
  22. +0 −17 test/nginx/cert.pem
  23. +0 −80 test/nginx/mime.types
  24. +0 −47 test/nginx/nginx.conf
  25. +0 −33 test/nginx/ssl/ca.crt
  26. +0 −54 test/nginx/ssl/ca.key
  27. +0 −23 test/nginx/ssl/client.crt
  28. +0 −12 test/nginx/ssl/client.csr
  29. +0 −18 test/nginx/ssl/client.key
  30. +0 −15 test/nginx/ssl/client.key.nopass
  31. +0 −21 test/nginx/ssl/server.crt
  32. +0 −11 test/nginx/ssl/server.csr
  33. +0 −18 test/nginx/ssl/server.key
  34. +0 −15 test/nginx/ssl/server.key.nopass
  35. +3 −0  test/pyrange.conf
View
63 README.md
@@ -1,8 +1,67 @@
pyrange: simple API for storing lists of hosts
----------------------------------------------
-A clean, python+gevent-based reimplementation of the excellent range
-utilities developed at Yahoo: https://github.com/ytoolshed/range/
+A clean, python+bottle+gevent-based reimplementation of the excellent
+range utilities developed at Yahoo: https://github.com/ytoolshed/range/
+When you manage a large amount of hosts, it is important to be able to:
+* address groups of hosts not by lists of individual names, but based on
+ function
+* easily share, modify and manipulate those groups
+* apply your own semantics
+pyrange by example
+------------------
+
+### Give me a list of all my hosts that serve pgsql
+
+ > GET /apps/pgsql
+
+ < 200 OK
+ < {"members": ["db1.phoenix.foo","db2.phoenix.foo","db1.london.foo",...]}
+
+### Give me a list of all my hosts that are in london
+
+ > GET /sites/london
+
+ < 200 OK
+ < {"members": ["db1.london.foo","web1.london.foo","gopher3.london.foo",...]}
+
+### Give me a list of all my pgsql hosts that are in london
+
+ > POST /range/
+ > {"members":["@apps.pgsql,&@sites.london"]}
+
+ < 200 OK
+ < {"members": ["db1.london.foo"]}
+
+### Create a special role for just london pgsql hosts
+
+ > PUT /apps/pgsql-london
+ > {"definition":["@apps.pgsqlg,&@sites.london"]}`
+
+ < 201 OK Created /apps/pgsql-london
+
+### Now give me a list of all my pgsql hosts that are in london
+
+ > GET /apps/pgsql-london
+
+ < 200 OK
+ < {"members": ["db1.london.foo"]}
+
+## Add a new database host in london
+
+ > PUT /apps/pgsql/definition
+ > {"append":["db2.london.foo"]}`
+
+ < 200 OK Appended /apps/pgsql/definition
+
+### Now give me a list of all my pgsql hosts that are in london
+
+ > GET /apps/pgsql-london
+
+ < 200 OK
+ < {"members": ["db1.london.foo"]}
+
+For more information, see the [wiki](https://github.com/nrh/pyrange/wiki)
View
4 bin/pyrange-server
@@ -1,8 +1,8 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-import pyrange.wsgiserver
+import pyrange.server
if __name__ == '__main__':
- pyrange.wsgiserver.start()
+ pyrange.server.start()
View
39 pyrange/acl.py
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-
-class AccessList(object):
- '''
- pyrange.AccessList
- attributes:
- name
- users
- groups
- created_by
- created_at
- last_modified_by
- last_modified_at
- '''
-
- def __init__(self, name):
- self._meta = {'name': name}
-
- def name(self):
- return self._meta['name']
-
- def acls(self):
- return self._acls
-
- def created_by(self):
- return self._created_by
-
- def roles(self):
- return store.get('namespace',name)
-
-
-class User(object):
- '''
- '''
-
-class Group(object):
- '''
- '''
-
View
306 pyrange/api_handler.py
@@ -1,306 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-import json
-import re
-import webob
-
-import store
-import auth
-import request
-
-baseurl = '/v1'
-method_map = {
- 'GET': {},
- 'HEAD': {},
- 'PUT': {},
- 'POST': {},
- 'DELETE': {},
- }
-
-resp_codes = {
- 200: 'OK',
- 201: 'Created',
- 400: 'Bad Request',
- 401: 'Unauthorized',
- 403: 'Forbidden',
- 404: 'Not Found',
- }
-
-
-def route(method, path_re):
- '''decorator for mapping an (http_method,regex) tuple to a function'''
-
- def decorate(f):
- regex = re.compile(path_re)
- method_map[method][regex] = f
- return f
-
- return decorate
-
-
-class APIHandler(object):
-
- '''Handle a range API request, generate a usseful response for WSGI'''
-
- def __init__(self):
- self.response_headers = [('Content-type', 'application/json')]
- self.store = store.Store()
-
- def handle_request(self, env):
- '''Handle an API request, returning a webob.Response
-
- In all cases including errors, this method should return a valid
- webob.Response.
-
- @type env: dict
- @param env: the WSGI environment for the request
- @rtype: webob.Response
- @return: response object suitable for presentation in the API
- '''
-
- request_handler = self.default_handler
-
- # authenticate
- env = auth.authenticate(env)
- if isinstance(env, webob.Response):
- return env
-
- self.req = request.RangeRequest(env)
- path_map = method_map[self.req.method]
-
- handler_match = None
- for key in path_map.iterkeys():
- match = key.match(self.req.path)
- if match:
- handler_match = match.groups()
- request_handler = path_map[key]
-
- # fake out self here.
- return request_handler(self, handler_match)
-
- def default_handler(self, foo, match): # foo is another copy of self due to fakery above
- return self.response_bad_request()
-
- # {{{ namespaces
-
- @route('GET', r'^\/namespaces/?$')
- def get_all_namespaces(self, match):
- '''request a list of namespaces'''
-
- return self.response_ok(body=self.store.get_all_namespaces())
-
- @route('PUT', r'^\/namespaces/?$')
- def add_namespaces(self, match):
- '''add a new namespace'''
-
- # am I authenticated?
-
- pass
-
- @route('GET', r'^\/namespaces/[a-z0-9]+')
- def get_namespace(self, match):
- '''get a namespace'''
-
- pass
-
- @route('PUT', r'^\/namespaces/[a-z0-9]+')
- def update_namespace(self, match):
- '''get a namespace'''
-
- pass
-
- @route('DELETE', r'^\/namespaces/[a-z0-9]+')
- def delete_namespace(self, match):
- '''get a namespace'''
-
- pass
-
- # }}}
- # {{{ roles
-
- @route('GET', '^\/[a-z0-9]+\/roles\/?$')
- def get_namespace_roles(self, match):
- '''request a list of roles from a namespace'''
-
- pass
-
- @route('PUT', '^\/[a-z0-9]+\/roles\/?$')
- def add_namespace_roles(self, match):
- '''add or update roles in a namespace'''
-
- pass
-
- @route('PUT', '^\/[a-z0-9]+\/roles\/[a-z0-9\.]+')
- def update_namespace_role(self, match):
- '''update a role'''
-
- pass
-
- @route('DELETE', '^\/[a-z0-9]+\/roles\/[a-z0-9\.]+')
- def delete_namespace_role(self, match):
- '''delete a role'''
-
- pass
-
- # }}}
- # {{{ tags
-
- @route('GET', '^\/tags\/?$')
- def get_tags(self, match):
- '''get list of tags'''
-
- pass
-
- @route('PUT', '^\/tags\/?$')
- def add_tags(self, match):
- '''add or update tags'''
-
- pass
-
- @route('GET', '^\/tags/[a-z0-9_\:]+')
- def get_tag(self, match):
- '''get a tag'''
-
- pass
-
- @route('PUT', '^\/tags/[a-z0-9_\:]+')
- def update_tag(self, match):
- '''update a tag'''
-
- pass
-
- @route('DELETE', '^\/tags/[a-z0-9_\:]+')
- def delete_tag(self, match):
- '''delete a tag'''
-
- pass
-
- # }}}
- # {{{ acls
-
- @route('GET', '^\/acls\/?$')
- def get_acls(self, match):
- '''get list of acls'''
-
- pass
-
- @route('PUT', '^\/acls\/?$')
- def add_acls(self, match):
- '''add or update acls'''
-
- pass
-
- @route('GET', '^\/acls/[a-z0-9_\:]+')
- def get_acl(self, match):
- '''get a acl'''
-
- pass
-
- @route('PUT', '^\/acls/[a-z0-9_\:]+')
- def update_acl(self, match):
- '''update a acl'''
-
- pass
-
- @route('DELETE', '^\/acls/[a-z0-9_\:]+')
- def delete_acl(self, match):
- '''delete a acl'''
-
- pass
-
- # }}}
- # {{{ members
-
- @route('GET', '^\/members\/?$')
- def get_members(self, match):
- '''get a list of members'''
-
- pass
-
- @route('GET', '^\/members\/[a-z0-9_\.]+')
- def get_members(self, match):
- '''get a members'''
-
- pass
-
- # }}}
- # {{{ range
-
- @route('GET', '^\\/range\\/?$')
- @route('POST', '^\\/range\\/?$')
- def expand_range(self, match):
- '''expand a range expression'''
-
- pass
- # }}}
-
- def response(self, code, body):
- if param_is_true(self.req.params['suppress_response_codes']):
- code = 200
-
- if param_is_true(self.req.params['pretty']):
- jsopts = {'sort_keys': True, 'indent': 4}
- else:
- jsopts = {}
-
- message = '%d %s' % (code, resp_codes[code])
- self.res.status = message
-
- if self.req.fields:
- body = self.filter_body(body)
-
- self.req.start_resp(self.res.status, self.res.headerlist)
- self.res.body = json.dumps(body, **jsopts)
- return self.res
-
- def filter_body(self, body):
- d = dict({})
- for k in self.req.fields:
- try:
- d[k] = body[k]
- except KeyError:
- d[k] = None
- return d
-
- # {{{ success response types
- # body is always a dict
-
- def response_ok(self, body=dict({})):
- return self.response(200, body)
-
- def response_created(self, body=dict({})):
- return self.response(201, body)
-
- def response_deleted(self, body=dict({})):
- return self.response(200, body)
-
- # }}}
- # {{{ error response types
-
- def response_error(self, errc, errmsg, more_info=None, body=dict({})):
- body['error'] = errmsg
- if more_info:
- body['more_info'] = more_info
- return self.response(errc, body)
-
- def response_bad_request(self, body=dict({})):
- return self.response_error(400, "I couldn't understand your request",
- body=body)
-
- def response_unauthorized(self, body=dict({})):
- return self.response_error(401,
- 'Authorization required to access this resource'
- , body=body)
-
- def response_forbidden(self, body=dict({})):
- return self.response_error(403,
- 'Insufficient privileges to access this resource'
- , body=body)
-
- def response_not_found(self, body=dict({})):
- return self.response_error(404, 'Resource not found', body=body)
-
-
- # }}}
-
View
38 pyrange/config.py
@@ -1,18 +1,36 @@
+# -*- coding: utf-8 -*-
+import argparse
+import yaml
+
+parser = argparse.ArgumentParser(description='start the pyrange server')
+
+parser.add_argument('-c', dest='configfile', default=None,
+ type=str, help='config file')
+parser.add_argument('--port', default=9191, type=int, help='port')
+parser.add_argument('--addr', default='127.0.0.1', type=str,
+ help='address for bind()')
+parser.add_argument('--db', default='sqlite:///:memory:', type=str,
+ help='sqlalchemy-friend database reference')
+args = parser.parse_args()
+
class Config(object):
- _instance = None
- def __new__(cls, *args, **kwargs):
- if not cls._instance:
- cls._instance = super(Config, cls).__new__(cls, *args, **kwargs)
- return cls._instance
+ def __init__(self, d):
+ self.d = d
+ def __getattr__(self, m):
+ return self.d.get(m, None)
- def __init__(self):
- self.port = 9191
- self.addr = '127.0.0.1'
- self.auth_header = 'X-Authentication'
- self.auth_user_header = 'X-Authenticated-User'
+ def __str__(self):
+ return repr(self.d)
+if args.configfile:
+ with open(args.configfile) as f:
+ y = yaml.load(f)
+ conf = Config(dict(y.items() + dict(args._get_kwargs()).items()))
+ print conf
+else:
+ conf = args
View
177 pyrange/core.py
@@ -0,0 +1,177 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from bottle import HTTPError
+from traceback import format_exc
+import pdb
+import store
+from sqlalchemy.sql import text
+
+import time
+import logging
+FORMAT ='%(asctime)s %(levelname)s %(filename)s:%(linenum)d %(funcName)s %message'
+logging.basicConfig(level=logging.DEBUG, format=FORMAT)
+logger = logging.getLogger(__name__)
+
+__all__ = ["Namespace"]
+
+class Base(object):
+
+ def __init__(self):
+ pass
+
+# {{{ Namespace
+
+class Namespace(Base):
+ '''our main man, the namespace'''
+
+ def __init__(self, name, request_data=None): # name=None, data=None):
+ logger.debug("init %s" % name)
+ self.name = name
+ self._request_data = request_data
+ self._data = None
+
+ def exists(self):
+ logger.debug("exists %s" % self.name)
+ select = store.namespaces.select(store.namespaces.c.name == self.name)
+ res = select.execute()
+ if res.rowcount == -1:
+ return False
+ elif res.rowcount == 1:
+ return True
+
+ raise HTTPError(code=500, output='internal consistency error')
+
+ def create(self):
+ '''sudo make me a namespace'''
+ logger.debug("create %s" % self.name)
+ t = store.conn.begin()
+ insert = store.namespaces.insert({
+ 'name': self.name,
+ 'created_by': 'nrh',
+ 'modified_by': 'nrh',
+ })
+ try:
+ res = insert.execute()
+ t.commit()
+ except:
+ t.rollback()
+ raise HTTPError(code=500, output='transaction failure',
+ traceback=format_exc(10))
+
+ return self
+
+ def update(self):
+ ''' merge dicts and commit back to the database'''
+ logger.debug("update %s" % self.name)
+ pass
+
+ def data(self):
+ '''return a dict of namespace data, suitable for returning in an http response'''
+ logger.debug("data %s" % self.name)
+ if not self._data:
+ select = store.namespaces.select(store.namespaces.c.name == self.name)
+ res = select.execute()
+ row = res.fetchone()
+ self._data = dict([(k,row[k]) for k in res.keys()])
+
+ return self._data
+
+# }}}
+# {{{ Role
+
+
+class Role(object):
+
+ '''
+ pyrange.Role
+ attributes:
+ name
+ acls
+ tags
+ members
+ created_by
+ created_at
+ '''
+
+ def __init__(self, name):
+ self._meta = {'name': name}
+
+ def name(self):
+ return self._meta['name']
+
+ def acls(self):
+ return self._acls
+
+ def created_by(self):
+ return self._created_by
+
+ def roles(self):
+ return store.get('namespace', name)
+
+
+# }}}
+# {{{ Member
+
+
+class Member(Base):
+
+ def __init__(self):
+ pass
+
+
+# }}}
+# {{{ AccessList
+
+
+class AccessList(Base):
+
+ '''
+ pyrange.AccessList
+ attributes:
+ name
+ users
+ groups
+ created_by
+ created_at
+ last_modified_by
+ last_modified_at
+ '''
+
+ def __init__(self, name):
+ self._meta = {'name': name}
+
+ def name(self):
+ return self._meta['name']
+
+ def acls(self):
+ return self._acls
+
+ def created_by(self):
+ return self._created_by
+
+ def roles(self):
+ return store.get('namespace', name)
+
+
+# }}}
+# {{{ User
+
+
+class User(Base):
+
+ '''
+ '''
+
+
+# }}}
+# {{{ Group
+
+
+class Group(Base):
+
+ '''
+ '''
+
+
+# }}}
View
60 pyrange/create_db.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, DateTime
+from store import store, meta
+import datetime
+
+namespaces = Table('namespaces', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(64)),
+ Column('created_by', Integer, nullable=False),
+ Column('created_on', DateTime, nullable=False, onupdate=datetime.datetime.now),
+ Column('modified_by', Integer, nullable=False),
+ Column('modified_on', DateTime, nullable=False, onupdate=datetime.datetime.now)
+ )
+
+roles = Table('roles', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(64)),
+ Column('namespace_id', None, ForeignKey('namespaces.id')),
+ Column('created_by', Integer, nullable=False),
+ Column('created_on', DateTime, nullable=False, onupdate=datetime.datetime.now),
+ Column('modified_by', Integer, nullable=False),
+ Column('modified_on', DateTime, nullable=False, onupdate=datetime.datetime.now)
+ )
+
+definitions = Table('definitions', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('role_id', None, ForeignKey('roles.id')),
+ Column('definition', String(128))
+ )
+
+# need acl relationship table?
+acls = Table('acls', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(64), nullable=False),
+ Column('mask', String(4), nullable=False),
+ Column('created_by', Integer, nullable=False),
+ Column('created_on', DateTime, nullable=False, onupdate=datetime.datetime.now),
+ Column('modified_by', Integer, nullable=False),
+ Column('modified_on', DateTime, nullable=False, onupdate=datetime.datetime.now)
+ )
+
+members = Table('members', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(128)),
+ Column('role_id', None, ForeignKey('roles.id'))
+ )
+
+log = Table('log', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('namespace_id', Integer),
+ Column('role_id', Integer),
+ Column('acl_id', Integer),
+ Column('message', String(128))
+ )
+
+
+meta.create_all()
+
View
276 pyrange/handler.py
@@ -0,0 +1,276 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import pdb
+import sys
+import json
+from traceback import format_exc
+
+from bottle import Bottle, Response, HTTPError, HTTPResponse
+from bottle import response, request
+
+import core
+import store
+
+app = Bottle()
+import bottle
+
+import datetime
+import logging
+FORMAT = \
+ '%(asctime)s %(levelname)s %(filename)s:%(linenum)d %(funcName)s %message'
+logging.basicConfig(level=logging.DEBUG, format=FORMAT)
+logger = logging.getLogger(__name__)
+
+# {{{ namespaces
+
+
+@app.get('/namespaces')
+def get_all_namespaces():
+ '''request a list of namespaces'''
+ logger.debug('GET /namespaces')
+ body = {}
+ nlist = store.get_all_namespaces()
+ body.update({'namespaces': nlist })
+ return body
+
+
+@app.put('/namespaces')
+def add_namespace():
+ '''add a new namespace'''
+
+ logger.debug('PUT /namespaces')
+ body = {}
+
+ try:
+ r = json.load(request.body)
+ ns = core.Namespace(r['name'], r)
+ if ns.exists():
+ ns.update()
+ response.status = '200 OK Updated /namespaces/%s' % (ns.name, )
+ else:
+ ns.create()
+ response.status = '201 OK Created /namespaces/%s' % (ns.name, )
+
+ return body
+ except Exception, e:
+
+ logger.error(format_exc(10))
+ if isinstance(e, HTTPError):
+ raise
+ else:
+ raise HTTPError(code=400, output="couldn't understand your request"
+ , traceback=format_exc(10))
+
+
+@app.get('/namespaces/<nsname>')
+def get_namespace(nsname=None):
+ '''get a namespace'''
+
+ logger.debug('GET /namespace/%s' % (nsname, ))
+ body = {}
+
+ try:
+ ns = core.Namespace(nsname)
+ data = ns.data()
+ # need to turn the datetime objects into strings for serialization.
+ [data.update({k: int(data[k].strftime('%s'))}) for k in data.keys()
+ if isinstance(data[k], datetime.datetime)]
+ return {nsname: data}
+ except Exception, e:
+
+ logger.error(format_exc(10))
+ if isinstance(e, HTTPError):
+ raise
+ else:
+ raise HTTPError(code=400, output="couldn't understand your request"
+ , traceback=format_exc(10))
+
+
+@app.put('/namespaces/<ns>')
+def update_namespace(ns=None):
+ '''add/update a namespace'''
+
+ logger.debug('PUT /namespace/%s' % (ns, ))
+
+ pass
+
+
+@app.delete('/namespaces/<ns>')
+def delete_namespace(ns=None):
+ '''remove a namespace'''
+
+ pass
+
+# }}}
+# {{{ roles
+
+
+@app.get('/<ns>/roles')
+def get_namespace_roles(ns=None):
+ '''request a list of roles from a namespace'''
+
+ pass
+
+
+@app.put('/<ns>/roles')
+def add_namespace_roles(ns=None):
+ '''add or update roles in a namespace'''
+
+ pass
+
+
+@app.put('/<ns>/roles/<role>')
+def update_namespace_role(ns=None, role=None):
+ '''update a role'''
+
+ pass
+
+
+@app.delete('/<ns>/roles/<role>')
+def delete_namespace_role(ns=None, role=None):
+ '''delete a role'''
+
+ pass
+
+# }}}
+# {{{ tags
+#
+# @route('GET', '^\/tags\/?$')
+# def get_tags(self, match):
+# '''get list of tags'''
+#
+# pass
+#
+# @route('PUT', '^\/tags\/?$')
+# def add_tags(self, match):
+# '''add or update tags'''
+#
+# pass
+#
+# @route('GET', '^\/tags/[a-z0-9_\:]+')
+# def get_tag(self, match):
+# '''get a tag'''
+#
+# pass
+#
+# @route('PUT', '^\/tags/[a-z0-9_\:]+')
+# def update_tag(self, match):
+# '''update a tag'''
+#
+# pass
+#
+# @route('DELETE', '^\/tags/[a-z0-9_\:]+')
+# def delete_tag(self, match):
+# '''delete a tag'''
+#
+# pass
+#
+# # }}}
+# # {{{ acls
+#
+# @route('GET', '^\/acls\/?$')
+# def get_acls(self, match):
+# '''get list of acls'''
+#
+# pass
+#
+# @route('PUT', '^\/acls\/?$')
+# def add_acls(self, match):
+# '''add or update acls'''
+#
+# pass
+#
+# @route('GET', '^\/acls/[a-z0-9_\:]+')
+# def get_acl(self, match):
+# '''get a acl'''
+#
+# pass
+#
+# @route('PUT', '^\/acls/[a-z0-9_\:]+')
+# def update_acl(self, match):
+# '''update a acl'''
+#
+# pass
+#
+# @route('DELETE', '^\/acls/[a-z0-9_\:]+')
+# def delete_acl(self, match):
+# '''delete a acl'''
+#
+# pass
+#
+# # }}}
+# # {{{ members
+#
+# @route('GET', '^\/members\/?$')
+# def get_members(self, match):
+# '''get a list of members'''
+#
+# pass
+#
+# @route('GET', '^\/members\/[a-z0-9_\.]+')
+# def get_members(self, match):
+# '''get a members'''
+#
+# pass
+#
+# # }}}
+# # {{{ range
+#
+# @route('GET', '^\\/range\\/?$')
+# @route('POST', '^\\/range\\/?$')
+# def expand_range(self, match):
+# '''expand a range expression'''
+#
+# pass
+# # }}}
+# {{{ misc helpers
+
+
+def pretty_print(resp):
+ '''takes a parsed-from-json response and pretty-prints it'''
+
+ str = ''
+ for key in resp:
+ str += '''
+>>> %s
+''' % (key, )
+ str += resp[key]
+ str += '''
+<<< %s
+''' % (key, )
+
+ return str
+
+
+# }}}
+# {{{ error response types
+
+
+@app.error(400)
+@app.error(401)
+@app.error(403)
+@app.error(404)
+@app.error(405)
+@app.error(418)
+@app.error(500)
+def respond_error(err):
+ '''copy the err info into our HTTPResponse object'''
+
+ body = {}
+ response.content_type = 'application/json'
+
+ if request.query.suppress_response_codes == u'1':
+ response.status = '200 OK'
+ body.update({'errc': err.status})
+ else:
+ response.status = err.status
+
+ if err.traceback:
+ body.update({'traceback': err.traceback})
+
+ body.update({'error': err.output})
+ return json.dumps(body)
+
+# }}}
+
View
30 pyrange/namespace.py
@@ -1,30 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-class Namespace(object):
- '''
- pyrange.Namespace
- attributes:
- name
- acls
- roles
- created_by
- created_at
- '''
-
- def __init__(self, name):
- self._meta = {'name': name}
-
- def name(self):
- return self._meta['name']
-
- def acls(self):
- return self._acls
-
- def created_by(self):
- return self._created_by
-
- def roles(self):
- return store.get('namespace',name)
-
-
View
0  pyrange/range_handler.py
No changes.
View
105 pyrange/range_parser.py
@@ -1,40 +1,93 @@
+#!/usr/bin/python
# -*- coding: utf-8 -*-
-import ply.lex
-import re
+
+import ply.lex as lex
+import ply.yacc as yacc
import sys
+# foo[1-10]
+# foo[1-10],foo[13-14]
+
tokens = (
- 'ROLE', # @foo
- 'UNION', # exp,exp
- 'DIFF', # exp,-exp
- 'INTER', # exp,&exp
- 'EOL',
- 'EOF',
- 'WSPACE',
- 'TAGS',
+ 'STRING',
+ 'LBRACE',
+ 'NUMBER',
+ 'DASH',
+ 'RBRACE',
+ 'UNION',
+ 'INTERSECT',
+ 'SUBTRACT',
)
+t_STRING = r'[a-zA-Z0-9\.][a-zA-Z0-9\.]*'
+t_LBRACE = r'\['
+t_DASH = r'(-|\.\.)'
+t_RBRACE = r'\]'
+t_INTERSECT = r',\&'
+t_SUBTRACT = r',\-'
+t_UNION = r','
+
+precedence = (
+ ('right', 'INTERSECT','SUBTRACT'),
+ ('right', 'UNION')
+ )
+
+def t_NUMBER(t):
+ r'''\d+'''
+
+ t.value = int(t.value)
+ return t
+
+
+def t_newline(t):
+ r'''\n+'''
+
+ t.lexer.lineno += len(t.value)
+
+
+def t_error(t):
+ print "Illegal character '%s'" % t.value[0]
+ t.lexer.skip(1)
+
+
+def p_expression(p):
+ '''expression : expression UNION expression
+ | expression INTERSECT expression
+ | expression SUBTRACT expression'''
+
+ if p[2] == t_UNION:
+ p[0] = p[1].union(p[3])
+ elif p[2] == t_INTERSECT:
+ p[0] = p[1].intersection(p[3])
+ elif p[2] == t_SUBTRACT:
+ p[0] = set([x for x in p[1] if x not in p[3]])
+
+
+def p_expression_literal(p):
+ '''expression : STRING outer'''
+
+ p[0] = set([p[1] + str(x) for x in p[2]])
+
+
+def p_expansion_outer(p):
+ '''outer : LBRACE expando RBRACE'''
+
+ p[0] = p[2]
+
-t_LPAREN = r'\('
-t_RPAREN = r'\)'
+def p_expansion_inner(p):
+ '''expando : NUMBER DASH NUMBER'''
-def t_ROLE(t):
- r'^@[a-z\.]+'
+ p[0] = set(xrange(p[1], p[3] + 1))
-def t_UNION(t):
- r','
-def t_DIFF(t):
- r',-'
+def p_error(p):
+ print 'syntax error! %s' % p
-def t_INTER(t):
- r',&'
-def t_EOL(t):
- r'$'
-def t_WSPACE(t):
- r'\r\n\t\ '
+lexer = lex.lex(debug=1)
+parser = yacc.yacc(debug=1)
-def t_TAGS(t):
- r''
+def range_parser(exp):
+ return parser.parse(exp)
View
38 pyrange/request.py
@@ -1,38 +0,0 @@
-
-from urlparse import parse_qsl
-from webob import Request
-
-class RangeRequest(Request):
-
- '''Parse and validate a range request'''
-
- def parse_querystring(self, string):
- # do some custom crap to support '&foo=bar,baz'
- dict = {}
- for (name, value) in parse_qsl(string):
- if name in dict:
- dict[name] = dict[name] + value.split(',')
- else:
- dict[name] = value.split(',')
- return dict
-
- def param(self, key):
- try:
- return self.params[key]
- except KeyError:
-
- return False
-
- def parse_dn(self, dn, field):
- '''
- parse one of these:
- /C=US/ST=Florida/L=Orlando/O=CLIENT NAME/CN=CLIENT NAME
- '''
-
- d = dict({})
- for x in dn.split('/'):
- (k, v) = x.split('=')
- d[k] = v
-
- return d[field]
-
View
30 pyrange/role.py
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-
-class Role(object):
- '''
- pyrange.Role
- attributes:
- name
- acls
- tags
- members
- created_by
- created_at
- '''
-
- def __init__(self, name):
- self._meta = {'name': name}
-
- def name(self):
- return self._meta['name']
-
- def acls(self):
- return self._acls
-
- def created_by(self):
- return self._created_by
-
- def roles(self):
- return store.get('namespace',name)
-
-
View
15 pyrange/server.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from gevent import monkey; monkey.patch_all()
+from bottle import debug
+from bottle import run
+
+from handler import app
+from config import conf
+
+def start():
+ debug(True)
+ run(app, host=conf.addr, port=conf.port, server='gevent', reloader=True)
+
+
View
92 pyrange/store.py
@@ -1,16 +1,90 @@
-#!/usr/bin/env python
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
-class Store(object):
- '''abstracted storage'''
- def __init__(self):
- pass
+from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, \
+ DateTime, create_engine, select
+import datetime
+import os
+import pdb
- def get_all_namespaces(self):
- pass
+from config import conf
+import logging
+FORMAT ='%(asctime)s %(levelname)s %(filename)s:%(linenum)d %(funcName)s %message'
+logging.basicConfig(level=logging.DEBUG, format=FORMAT)
+logger = logging.getLogger(__name__)
- def get_all_acls(self):
- pass
+meta = MetaData()
+engine = create_engine(conf.db, echo=False)
+meta.bind = engine
+conn = engine.connect()
+# {{{ table definitions
+tables = {
+ 'namespaces': Table('namespaces', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(64)),
+ Column('created_by', String(64), nullable=False),
+ Column('created_on', DateTime, default=datetime.datetime.now),
+ Column('modified_by', String(64), nullable=False),
+ Column('modified_on', DateTime, onupdate=datetime.datetime.now, default=datetime.datetime.now),
+ ),
+ 'roles': Table('roles', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(64)),
+ Column('namespace_id', None, ForeignKey('namespaces.id')),
+ Column('created_by', Integer, nullable=False),
+ Column('created_on', DateTime, nullable=False,
+ onupdate=datetime.datetime.now),
+ Column('modified_by', Integer, nullable=False),
+ Column('modified_on', DateTime, nullable=False,
+ onupdate=datetime.datetime.now),
+ ),
+ 'definitions': Table('definitions', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('role_id', None, ForeignKey('roles.id')),
+ Column('definition', String(128))
+ ),
+ 'acls': Table('acls', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(64), nullable=False),
+ Column('mask', String(4), nullable=False),
+ Column('created_by', Integer, nullable=False),
+ Column('created_on', DateTime, nullable=False,
+ onupdate=datetime.datetime.now),
+ Column('modified_by', Integer, nullable=False),
+ Column('modified_on', DateTime, nullable=False,
+ onupdate=datetime.datetime.now),
+ ),
+ 'members': Table('members', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('name', String(128)),
+ Column('role_id', None, ForeignKey('roles.id'))
+ ),
+ 'log': Table( 'log', meta,
+ Column('id', Integer, primary_key=True, autoincrement=True),
+ Column('namespace_id', Integer),
+ Column('role_id', Integer),
+ Column('acl_id', Integer),
+ Column('message', String(128)),
+ ),
+ }
+# }}}
+def get_table(tname):
+ t = Table(tname, meta, autoload=True)
+ if t.exists():
+ logger.debug('re-using %s' % (tname,))
+ else:
+ logger.info('creating %s' % (tname,))
+ t.create()
+ return t
+
+def get_all_namespaces():
+ s = select([namespaces.c.name])
+ res = s.execute().fetchall()
+ return [x.values().pop() for x in res]
+
+namespaces = get_table('namespaces')
+
View
0  pyrange/store_sqlite.py
No changes.
View
20 pyrange/wsgiserver.py
@@ -1,20 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from gevent import monkey; monkey.patch_all()
-import bottle
-import config
-import api_handler
-
-conf = config.Config()
-api = api_handler.APIHandler()
-
-def rangeapp(env, start_response):
- res = api.handle_request(env)
- start_response(res.headerlist)
- return res.body
-
-def start():
- run(host=conf.addr, port=conf.port, server=gevent).serve_forever()
-
-
View
83 test/01_namespace_test.py
@@ -1,37 +1,80 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-import requests
+from wsgi_intercept.httplib2_intercept import install
+install()
+import bottle
+import httplib2
import json
-import random
+import pdb
+from nose.tools import *
+from pyrange.handler import pretty_print
import unittest
-from nose.tools import with_setup
+import wsgi_intercept
-randport = random.randrange(1025, 65534)
+import pyrange.handler
+import logging
+FORMAT = \
+ '%(asctime)s %(levelname)s %(filename)s:%(linenum)d %(funcName)s %message'
+logging.basicConfig(level=logging.DEBUG, format=FORMAT)
+logger = logging.getLogger(__name__)
+addr = 'test'
+port = 15232
+http = httplib2.Http()
+bottle.app.push(pyrange.handler.app)
+url = 'http://%s:%d' % (addr, port)
-class NamespaceTest(unittest.TestCase):
- def setup_stuff(self):
- '''set up test fixture'''
+bottle.debug(True)
- def teardown_stuff(self):
- '''tear down test fixture'''
+def setup_stuff():
+ '''set up test fixture'''
+ wsgi_intercept.add_wsgi_intercept(addr, port, bottle.default_app)
- @with_setup(setup_stuff, teardown_stuff)
- def test_put(self):
- namespace = {'name': 'testns', 'acls': []}
- url = 'http://localhost:%d/namespace/' % randport
- payload = json.dumps(namespace)
- r = requests.put(url, data=payload)
+def teardown_stuff():
+ '''tear down test fixture'''
- @with_setup(setup_stuff, teardown_stuff)
- def test_get(self):
- r = requests.get('http://localhost:%d/namespace/')
- print r
- pass
+ wsgi_intercept.remove_wsgi_intercept(addr, port, bottle.default_app)
+@with_setup(setup_stuff, teardown_stuff)
+def test_put():
+ namespace = {'name': 'testns', 'acls': []}
+ payload = json.dumps(namespace)
+ (r, c) = http.request(url + '/namespaces', 'PUT', body=payload)
+ logger.info('r=%s' % r)
+ logger.info('c=%s' % c)
+ c = json.loads(c)
+ if c.has_key('traceback'):
+ logger.error(c['traceback'])
+ assert_dict_contains_subset({'status': '201'}, r)
+
+
+@with_setup(setup_stuff, teardown_stuff)
+def test_get():
+ (r, c) = http.request(url + '/namespaces', 'GET')
+ logger.info('r=%s' % r)
+ logger.info('c=%s' % c)
+ c = json.loads(c)
+ if c.has_key('traceback'):
+ logger.error(c['traceback'])
+ assert_dict_contains_subset({u'namespaces': ['testns']}, c)
+ assert_dict_contains_subset({'status': '200'}, r)
+
+
+@with_setup(setup_stuff, teardown_stuff)
+def test_get_ns():
+ (r, c) = http.request(url + '/namespaces/testns', 'GET')
+ logger.info('r=%s' % r)
+ logger.info('c=%s' % c)
+ c = json.loads(c)
+ if c.has_key('traceback'):
+ logger.error(c['traceback'])
+ testns = c['testns']
+ assert_dict_contains_subset({'created_by': 'nrh'}, testns)
+ assert_dict_contains_subset({'status': '200'}, r)
+
View
24 test/10_range_parser_test.data
@@ -0,0 +1,24 @@
+--- # test1
+foo
+---
+foo
+--- # test2
+foo321
+---
+foo321
+--- # test3
+foo[1-10]
+---
+[ foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9, foo10 ]
+--- # test4
+foo[1-10],foo[12-13]
+---
+[ foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9, foo10, foo12, foo13 ]
+--- # test5
+foo,bar
+---
+---
+foo,foo
+---
+foo
+
View
30 test/10_range_parser_test.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+from nose.tools import *
+import logging
+import pdb
+import unittest
+import yaml
+
+from pyrange.range_parser import range_parser as rp
+
+FORMAT = \
+ '%(asctime)s %(levelname)s %(filename)s:%(linenum)d %(funcName)s %message'
+logging.basicConfig(level=logging.DEBUG, format=FORMAT)
+logger = logging.getLogger(__name__)
+
+def test_range():
+ with open('test/10_range_parser_test.data') as f:
+ foo = yaml.load_all(f)
+ i = 0
+ for test in foo:
+ logger.debug('test=%s' % test)
+ result = foo.next()
+ if not result:
+ assert_items_equal(result, rp(test), test)
+ else:
+ assert_is_none(rp(test), test)
+
+
+
View
15 test/nginx/cert.key
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDQBwC7iDAnVzLScvN/psj6didhUHoLsbnJNqbKNQUb53qYLbTw
-c4yzfxgqKviCbYbizorXaR6YiNS0sElxkFkoY2UbIoYNKV0QpHLzsK4IqJj5qA2M
-qfZsFsdXz4hP1rrZfeVTW1bGlqjFGmkCl95aDOon63CygV9ruDGJCaagiQIDAQAB
-AoGAe6T96Xj77hYlDfLnCeNvJ3cc1vmHYPYE0DTck+lJnRlLw7G/DwAPDA0ppr6d
-sKA7AQn0ei5v0gfaLX9uV8KNSqJGrSmj+ozjxM+7+TUPAJayluUAUr8G6KKMcT6F
-Hw6IFCQNjOUpYR0xc9FfdjjJU23TFffdd9nW7jnAxGijtrUCQQD2YZxRO18S/iPm
-IMgTYSMoGXxKeFa8uKwPNz6vLXAaouNX3bkCFKZeGTuvnAmJvrDHxlJmTNy3pPlX
-Es1qvclHAkEA2CYTETDI9hSSJRHD0oGPeDWi0M1YDY0x6BNolRdm25lXK6M97Z6y
-k3zLFgNfRlf+w7hnDs7O4qaeWtgEUVovrwJBAMXifidsNR9PCUxDFg8inJcPaRly
-789hz6fk6i7OotRx8YDX70kxhrHbOGoCPFLq2peJvRK3Nys0kKMT0fKife8CQQCX
-VLDszDcm8ugPRAzSCRulkgfYIKyRcFWMomoKWEQPY6CAUN4+y2lDkY+ubdX6rgkU
-hrrAx1JZpUYhXDRJsNbJAkEAzA3rQd6pv4p/NXVokZD9Wb6sPQ0mAR+gRQ8dpm5/
-OJP/V8leW0Cxuu2R8HML12Xl6B99f22ya3amyd8in4wplg==
------END RSA PRIVATE KEY-----
View
17 test/nginx/cert.pem
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICwjCCAiugAwIBAgIJALuXAKBGzKNKMA0GCSqGSIb3DQEBBQUAMEsxCzAJBgNV
-BAYTAlNFMRMwEQYDVQQIEwpTb21lLVN0YXRlMRIwEAYDVQQHEwlTdG9ja2hvbG0x
-EzARBgNVBAoTClNwb3RpZnkgQUIwHhcNMTIwMzExMTcyODA4WhcNMTMwMzExMTcy
-ODA4WjBLMQswCQYDVQQGEwJTRTETMBEGA1UECBMKU29tZS1TdGF0ZTESMBAGA1UE
-BxMJU3RvY2tob2xtMRMwEQYDVQQKEwpTcG90aWZ5IEFCMIGfMA0GCSqGSIb3DQEB
-AQUAA4GNADCBiQKBgQDQBwC7iDAnVzLScvN/psj6didhUHoLsbnJNqbKNQUb53qY
-LbTwc4yzfxgqKviCbYbizorXaR6YiNS0sElxkFkoY2UbIoYNKV0QpHLzsK4IqJj5
-qA2MqfZsFsdXz4hP1rrZfeVTW1bGlqjFGmkCl95aDOon63CygV9ruDGJCaagiQID
-AQABo4GtMIGqMB0GA1UdDgQWBBT1OB5T9DjRSQwgSj8hJITPmhbZvTB7BgNVHSME
-dDBygBT1OB5T9DjRSQwgSj8hJITPmhbZvaFPpE0wSzELMAkGA1UEBhMCU0UxEzAR
-BgNVBAgTClNvbWUtU3RhdGUxEjAQBgNVBAcTCVN0b2NraG9sbTETMBEGA1UEChMK
-U3BvdGlmeSBBQoIJALuXAKBGzKNKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
-BQADgYEAqp9qsfUI2DgZ8Defqa3mVDo7e3TmUL7F/xXyhnkqS7eCVmF8roVhmCbm
-DOE5VnWGE+kbxKCVQ7PofXsHCpCPavnzNoukz4e0yIHGoPAgPePApgAUf6VAHnh2
-T+//c0ALxQCc8fZFgGdQtCCBkdFXMclP586ztEsNEMiViZ5JaE8=
------END CERTIFICATE-----
View
80 test/nginx/mime.types
@@ -1,80 +0,0 @@
-
-types {
- text/html html htm shtml;
- text/css css;
- text/xml xml;
- image/gif gif;
- image/jpeg jpeg jpg;
- application/x-javascript js;
- application/atom+xml atom;
- application/rss+xml rss;
-
- text/mathml mml;
- text/plain txt;
- text/vnd.sun.j2me.app-descriptor jad;
- text/vnd.wap.wml wml;
- text/x-component htc;
-
- image/png png;
- image/tiff tif tiff;
- image/vnd.wap.wbmp wbmp;
- image/x-icon ico;
- image/x-jng jng;
- image/x-ms-bmp bmp;
- image/svg+xml svg svgz;
- image/webp webp;
-
- application/java-archive jar war ear;
- application/mac-binhex40 hqx;
- application/msword doc;
- application/pdf pdf;
- application/postscript ps eps ai;
- application/rtf rtf;
- application/vnd.ms-excel xls;
- application/vnd.ms-powerpoint ppt;
- application/vnd.wap.wmlc wmlc;
- application/vnd.google-earth.kml+xml kml;
- application/vnd.google-earth.kmz kmz;
- application/x-7z-compressed 7z;
- application/x-cocoa cco;
- application/x-java-archive-diff jardiff;
- application/x-java-jnlp-file jnlp;
- application/x-makeself run;
- application/x-perl pl pm;
- application/x-pilot prc pdb;
- application/x-rar-compressed rar;
- application/x-redhat-package-manager rpm;
- application/x-sea sea;
- application/x-shockwave-flash swf;
- application/x-stuffit sit;
- application/x-tcl tcl tk;
- application/x-x509-ca-cert der pem crt;
- application/x-xpinstall xpi;
- application/xhtml+xml xhtml;
- application/zip zip;
-
- application/octet-stream bin exe dll;
- application/octet-stream deb;
- application/octet-stream dmg;
- application/octet-stream eot;
- application/octet-stream iso img;
- application/octet-stream msi msp msm;
-
- audio/midi mid midi kar;
- audio/mpeg mp3;
- audio/ogg ogg;
- audio/x-m4a m4a;
- audio/x-realaudio ra;
-
- video/3gpp 3gpp 3gp;
- video/mp4 mp4;
- video/mpeg mpeg mpg;
- video/quicktime mov;
- video/webm webm;
- video/x-flv flv;
- video/x-m4v m4v;
- video/x-mng mng;
- video/x-ms-asf asx asf;
- video/x-ms-wmv wmv;
- video/x-msvideo avi;
-}
View
47 test/nginx/nginx.conf
@@ -1,47 +0,0 @@
-
-worker_processes 1;
-
-events {
- worker_connections 1024;
-}
-
-
-http {
- include mime.types;
- default_type application/octet-stream;
- sendfile on;
- #tcp_nopush on;
- keepalive_timeout 65;
- #gzip on;
-
- upstream range {
- server 127.0.0.1:9191;
- }
-
- server {
- listen 8443;
- server_name localhost;
-
- ssl on;
- ssl_certificate ssl/server.crt;
- ssl_certificate_key ssl/server.key.nopass;
-
- ssl_session_timeout 5m;
-
- ssl_protocols SSLv2 SSLv3 TLSv1;
- ssl_ciphers HIGH:!aNULL:!MD5;
- ssl_prefer_server_ciphers on;
-
- location / {
- root html;
- index index.html index.htm;
- }
-
- location /v1/ {
- proxy_pass http://range/;
- proxy_set_header X-Auth $ssl_client_verify;
- proxy_set_header X-Auth-User $ssl_client_s_dn;
- }
- }
-}
-
View
33 test/nginx/ssl/ca.crt
@@ -1,33 +0,0 @@
------BEGIN CERTIFICATE-----
-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
------END CERTIFICATE-----
View
54 test/nginx/ssl/ca.key
@@ -1,54 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,A0CE3DF4813062DD
-
-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
------END RSA PRIVATE KEY-----
View
23 test/nginx/ssl/client.crt
@@ -1,23 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDvjCCAaYCAQEwDQYJKoZIhvcNAQEFBQAwSjELMAkGA1UEBhMCU0UxEjAQBgNV
-BAgTCVN0b2NraG9sbTESMBAGA1UEBxMJU3RvY2tob2xtMRMwEQYDVQQKEwpTcG90
-aWZ5IEFCMB4XDTEyMDMxMTE4MDQxOVoXDTEzMDMxMTE4MDQxOVowgYMxCzAJBgNV
-BAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazERMA8GA1UEBxMITmV3IFlvcmsxEzAR
-BgNVBAoTClNwb3RpZnkgQUIxGTAXBgNVBAMTEE5pY2hvbGFzIEhhcnRlYXUxHjAc
-BgkqhkiG9w0BCQEWD25yaEBzcG90aWZ5LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
-jQAwgYkCgYEA1BeDotkhnxvaWjKsoJfAHGGkAUocF7CeKpMv+H0sYvhIwp6QfCeA
-kigFIkdZIzI3C20EyD5/ppimAmcwuKcNWV3wWKnjfnhIcR95Fm3jTJMY9GkMp6cI
-MFyLHBtPCxfl9XksRubjwRDN9bRr29U3ss9dsqN8dnw0KZv1D6FqJv0CAwEAATAN
-BgkqhkiG9w0BAQUFAAOCAgEAu46AjABw2TzJjz7qEBlOS2hvrJtoLePPrGYb+tC+
-V3rheS7XWGYVAl0Ln3/z7xLbR6pbQBP765kLrv2CP44wDV/EhRF/fX/gJ9ovE0z5
-u7wk++6JYp/BxHitcjw4BFX3rwuOcNFd7aFKw47oAbnsle0VqSEvq/EOoLKa394S
-/qXlPq7M28XAumMeP7lObnRYVty5JJJ2OzImtSTvYj6FvTKcxUZCdHDsb6SxHE4I
-eYQlJqQFJPYUd5Pv6K5syPB4oTediRhszt9NBExzV2n6IYkyFp5Lux3xZPKE+G2E
-qWUi/JhrVwMdoE/+OKXNqX1yK0yLOUZFdOcyOmHBsndvDlpP8qiTOC3Uf4T0AtFq
-hpqa/etVriqS4kRSE/HbjIF+e3D4IwD2l23eofqYIrRAl920dExoMlQdVVDOrpuE
-tUCZa7/r3yW8Du6CKyfsZQ6sTkfxxRsQ3xJltQaiaqpbyB7yp4gPwZQ7S5xfu2AY
-YcGCdqYMnRBlpewQSrXeyza4MjD/34oE8lo2156nAcb2bVkxF6/fh0GUBByAc+ga
-VQIrG4D1dHZ4+FJzGfrJ4DhdwX6ynza6pfGuuJBRJlOEbgURFMhtuujRqmZpP8h3
-OT1EKtxLTqsm+8vpMjl3i5ZBz0jIbeSEnjZazpvOsOpb/OylNfu4ttiiHGrONe1J
-JkI=
------END CERTIFICATE-----
View
12 test/nginx/ssl/client.csr
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIBxDCCAS0CAQAwgYMxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazER
-MA8GA1UEBxMITmV3IFlvcmsxEzARBgNVBAoTClNwb3RpZnkgQUIxGTAXBgNVBAMT
-EE5pY2hvbGFzIEhhcnRlYXUxHjAcBgkqhkiG9w0BCQEWD25yaEBzcG90aWZ5LmNv
-bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1BeDotkhnxvaWjKsoJfAHGGk
-AUocF7CeKpMv+H0sYvhIwp6QfCeAkigFIkdZIzI3C20EyD5/ppimAmcwuKcNWV3w
-WKnjfnhIcR95Fm3jTJMY9GkMp6cIMFyLHBtPCxfl9XksRubjwRDN9bRr29U3ss9d
-sqN8dnw0KZv1D6FqJv0CAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBAIZAbYRDcJTN
-aLQ0OBnBvrDDE/vJjlfjmxWNoFfBvJrPkBnR82wX6ramBetWHTbzrEpYuRdX/unK
-YDriG7gdC9px+P7YkfBExWJMTzDCUrVWczUqtFR092c652KusfYqYN7CH2TDqdGT
-Mv9fJSg/z6o+Rh1miTNop95xPb0MS8Fb
------END CERTIFICATE REQUEST-----
View
18 test/nginx/ssl/client.key
@@ -1,18 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,464D7AFC61195111
-
-q7kOY9IRdCRAukEFArm/qaX3Vzv6PPg8SEXQRcqqPC6AhHMKs8dUDyc9jOpqypG1
-Ds38ZwQfRwmnUeqVDTyAQIRIafer+uKKTBDYLNMIbslBQu2fjQr97yjVZDmnjniW
-ynDKVhEoHSqGoZCWTAj9QPnw6GD4rijuGOUwk/Ew3BCBehON0Cmz1vhFUXP8OEPS
-qNSlCtYIbz3f2fnUr4xNuiMwJ1obzTIZ8FPwJfD1S9Csv8S6/UkNGzQdYHCOGaz3
-PENxfTJLSlB2jpfABIxZsfxVazpGZKXPX8WuGknp3UY89GZMHBH8V8/EsvnNgLiI
-sj1Tc9cFSX+dLu2hWvQhVMfyH4YD4abjTQP+37a2806zJO4+er1xOmFJ8/dsSpEK
-NR4XqfJdPVSwgcQ3ymuXr142YAwoeow2Ou2WnGfBT6Dn3zId0AXz9ohaABtf92E8
-aCdxC2tarR67iYVkvQFLPE2WUA5iR3aJWbkS/oL2MamRBoAg5KM32/Y86258W7rP
-FLFclcr1GZRCOpuhz5AwyYg/OskWw0VMdTBRVlTIgy4rRoUkBfIdKvvp3hIyzdl+
-ZFOdkw3AI18gLuboEfGOtl8kqHcSEeI9rS136r8wpPRlfMsF67mtfsZjgU9At7tk
-GhuIUp1v81Z806/7nAJ6t2Iy+rXlNyrI3v1/I3lWpdvjtb8Fqg3Yx7G6v0a/tg3S
-Yj92ftYSlBVCOWc/fR6Vn3Q21yD/gNjaW+fR3oku++bVgJcFCROnXeuFYxDMqgHm
-Wf8Cp0uwFYiC1Kycxr5Lto8jTxuCW9/+FUBrE6zA+uFqPkfb+x9duw==
------END RSA PRIVATE KEY-----
View
15 test/nginx/ssl/client.key.nopass
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDUF4Oi2SGfG9paMqygl8AcYaQBShwXsJ4qky/4fSxi+EjCnpB8
-J4CSKAUiR1kjMjcLbQTIPn+mmKYCZzC4pw1ZXfBYqeN+eEhxH3kWbeNMkxj0aQyn
-pwgwXIscG08LF+X1eSxG5uPBEM31tGvb1Teyz12yo3x2fDQpm/UPoWom/QIDAQAB
-AoGAIUCKh+apCcwRIOEfBZKlMQ3x663V17T+J0iqVbgyVFX++lxn5L9Avil+HJC5
-HfaKgMUX4dlCpDj8lhXdoqW2Rspx3/oNgjB76D0h1nn1d8bG+jIcge0WcXkSOjoS
-ysB6Z5E7mTaH2nrbvj4M0AcQKHZnhsU8h2a0Sz5vdv1L+FUCQQD2F8NyTf2Wk916
-qaSEwxeBWj1j5cXel0akaHl8ovoD3k/PblhH7kBvPaJj2Uwpz6lDTeDFHgu07+Ih
-FCBcg0l7AkEA3KFWD0880IMAy3OLOqxo4tYisyoetbiHd50Cb079PUKR0KE+eEO8
-HCoFyO9K1TvZpZMqnwcf4OlkI4jTPvK75wJBAIgOQU9L0RzT6UosyjlgRvmL847s
-dQYIatTwseH3lPRYj5VmM0apcVRPJh3AAHzJQ5JjVxaD8wL48PNK1k1vhEsCQDQE
-5iGd8RzIhntxtrYSyHgTPQM5Pxxhca9GGbjcyuCSvYaPPgdlEQki71I8+UvlXJ+p
-Xzc3mtCQtseaDxScbqkCQAGRKWKaN2aarCxL7JivteRsZ11xY3pwHDx9vEu5J7BE
-7rX507ih8Qxsg1GLUwYvhyP0dYfct5L6CSi2vvcEENk=
------END RSA PRIVATE KEY-----
View
21 test/nginx/ssl/server.crt
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDhDCCAWwCAQEwDQYJKoZIhvcNAQEFBQAwSjELMAkGA1UEBhMCU0UxEjAQBgNV
-BAgTCVN0b2NraG9sbTESMBAGA1UEBxMJU3RvY2tob2xtMRMwEQYDVQQKEwpTcG90
-aWZ5IEFCMB4XDTEyMDMxMTE4MDI0NFoXDTEzMDMxMTE4MDI0NFowSjELMAkGA1UE
-BhMCU0UxEjAQBgNVBAgTCVN0b2NraG9sbTESMBAGA1UEBxMJU3RvY2tob2xtMRMw
-EQYDVQQKEwpTcG90aWZ5IEFCMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL
-RSn/u7bLNmF/2jWpdXXQNbPVyHzDKj9OM/1ObAm3Rfu7mqKBVic+bk8wbLtj4p9Q
-KyfmX2RVnvFkC6L5QUbMPEggRBFEqyuZutGwMSsdBaqNt0ppUNcbwtKt12WQeF3O
-sFa+ITimS/FJDk++W0sqLZ/ZG6QqI82lfT+zfSoYqQIDAQABMA0GCSqGSIb3DQEB
-BQUAA4ICAQBGUhZdknRrwrx4YaRRgQJx1bsGG/LMjHn8RV78gnIp/y2sLBO4Pe9M
-SjiVKtyoCXj0EG7NjgCuGnfaisFHckA9EhOCZT+QIl/cmGBdiCKbLGy03/uMK9dr
-xqNKbZLp0G1khOLY1p2mVrKGwzfGckfsR48K5WWy5e7KMMTfulOT0l2qYL+MRGEc
-zpOASY8QM5DEXAOZJCbTZgudFVHVJLJuGgUc30TlOqDfJozCe6qfIeYOI6LHRuWs
-b9zJwEHqFtpSf3A9cPGRwtv+yCPxHqCcNku+yBJfC0VAbRi6y49LcUaO7Ytu0LHd
-46Ng6TdmgYqczyzTwFw7g/fSI/EpPizmbmbyxzwVvNmIdbGZqHWWVG98Sq7169Vy
-P2JWq43fY4+N+bNl1gSTfq495q67noIJMjy7HPt1xugeB7av3qdqZE9mMVupJIco
-NQjlRf/XrFXF0X1+ryM1FsN9zY5QnxhQo1XITeAbV81x9gyIXYumII01Qsd7DVL7
-ku4obcnUsL5+k49EBI4uOeARqfMAD8VG8Ts2YvhuTyf23Gcjpm/GqKNLIXXj2Z1m
-ok0EetTvQo1JTnlx7TF0a1u/Ix0ycYaiLf5eIOPUW3ujfESX8dYOm7l7Xkq/pAVJ
-GaLtwhZwby+a0ai6LZkSJDawcWZC8NucnL/pJk857ZmJSiHsaT6NiQ==
------END CERTIFICATE-----
View
11 test/nginx/ssl/server.csr
@@ -1,11 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIBiTCB8wIBADBKMQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMRIw
-EAYDVQQHEwlTdG9ja2hvbG0xEzARBgNVBAoTClNwb3RpZnkgQUIwgZ8wDQYJKoZI
-hvcNAQEBBQADgY0AMIGJAoGBAMtFKf+7tss2YX/aNal1ddA1s9XIfMMqP04z/U5s
-CbdF+7uaooFWJz5uTzBsu2Pin1ArJ+ZfZFWe8WQLovlBRsw8SCBEEUSrK5m60bAx
-Kx0Fqo23SmlQ1xvC0q3XZZB4Xc6wVr4hOKZL8UkOT75bSyotn9kbpCojzaV9P7N9
-KhipAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQA4DVHDX58r/EXE52kFAh8bXt3P
-/s0Vvl3d3YYKHMXfZDbA0HWtUrJecctKS1vhM8jeZ6FGGVf1ygCGS54QGTyXx/d4
-wiZXjBnP5metL0WdMKNChiTbKjWGrpvqGYEwRm2Y4dkwLHsSWkldQkGJni7sGPoj
-7H6/5WXJ9wMgyYarvg==
------END CERTIFICATE REQUEST-----
View
18 test/nginx/ssl/server.key
@@ -1,18 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,557524862933186D
-
-EU3ty5QQCdvM4HwmT3qnlrFHX/bW3+be/iuqo8mMsxmnejuQ6e/7Blm4/YhpvKd3
-bpFky4JHmbwtvUFNlzyiPflX/IRF6ARJC6PDRb07zcY4HzyXmuPevbO+jHOWFUlW
-ITYYkcUONo1SJLEJsXWAKor80oDGI42dEjvB3xqJfSNE7WuEcnYmclWr4CFU7tPO
-StzDkzijWpXl78x4LrLWW+AkubbaCHyg8SFicw3yCAHzZsyYkpaArT3iZNvz+T6B
-50QvOz6g2g1cfAUqBz9TsX6N2aH3voEKnCWh8NQacnhWkhE46TvxhkD6U/oS1hht
-ooyYmzD9eSZJN2mfqFuoiDxHXCQgq/d9nqL3zoHR4NpmJIcG/yExOpZgjAVq6J99
-R+o2O0r3ul+JCdz7ptC6aGeArKTH2I2daeWv1UX7ZaMAHY8HETs6gOQSehPxJYPK
-QZP7m3tEqBB0hOSZ8LjTiyMsk2wg3LmfqnjyeVSZ3V0IUFfhI0VppFagXpJWBWqE
-be/m2WvsmBf6nPHgQIAcrWFmqDrPA5HEO1UHdMbA4plEWoAH0v8/ezRjM56q6m3X
-4Ii5bxfdAsnscyeXZSFZiRIK4lvsLviKwFU00Fk+JLfsgw+7y8BR0FxdT1/rF9Qf
-5qdpD3hoC76ItAPKll2PIbAl+gYArpi2Imd9gh/54r4KKNCpM8ZT0qSsj2PNc5Y+
-T1rgTy60QrMKMED7vksJzTLlBJso50LnBnT9FTBXbndTMyxT8hlq/UTLaWMm5abX
-Hx4kVML3/sigUTlm4enYw6r+2o4NDKx+rS6sF8PDI5iDYi8WCRhQ6g==
------END RSA PRIVATE KEY-----
View
15 test/nginx/ssl/server.key.nopass
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXwIBAAKBgQDLRSn/u7bLNmF/2jWpdXXQNbPVyHzDKj9OM/1ObAm3Rfu7mqKB
-Vic+bk8wbLtj4p9QKyfmX2RVnvFkC6L5QUbMPEggRBFEqyuZutGwMSsdBaqNt0pp
-UNcbwtKt12WQeF3OsFa+ITimS/FJDk++W0sqLZ/ZG6QqI82lfT+zfSoYqQIDAQAB
-AoGBAIHpMT9lHB1iIwwQTO2ByYsJV2cRXFNrQ8xaQ9EEEW2Vh55N5QAeLLSk0W2U
-gGsFImadPkHow3pJO+OB4GXHB1RKcyvpVVoT9YOKlbMcOgrdre+8xa2imw9kxysh
-pfyu71d7PkIw/V8PYkpFkK1Gi19dSG+TM+Cymf4458O7uJZ1AkEA7i9XTFKoBzIB
-7iYBSaH4p5rPqh96t3FBQ9ABc2Au98IRoIkEA/crYrK4X83Lv2LI+uqFegaTh+gj
-C4+81WlhOwJBANp5SncUbwZkeFDtGG2YRMTJYPlWggY94ybUXqESor33X29Y+sPl
-MfQ4J+L8Pj5K9EKn2cRSnQR6+pspzUGED2sCQQDDLfgTiwg2HTyHso0Lkbb5YVsa
-xYECIcShlSobKY5TaKyZKK4psbnTvzBnEXUM+B5JvzRxskHi9M8DAl5zY09RAkEA
-rhmuL8sG1u9FuifXKa8ITlG0h75ai6LR2+WtK2rwabYyRUeBPo7tChOeJZKh/343
-vM6KCZtUKXffMbtUKHuW1QJBAJFrq3UuwQGuwlAt5HAT2p5kiO5+ajal5Nh03fXx
-oeyHMbf+emqG3L3VKV4vjEXM+kL6sv0QEUegLOgnYXTTD5c=
------END RSA PRIVATE KEY-----
View
3  test/pyrange.conf
@@ -0,0 +1,3 @@
+port: 9191
+addr: localhost
+db: sqlite:////tmp/range.db

No commit comments for this range

Something went wrong with that request. Please try again.