Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #7

Merged
merged 8 commits into from
Dec 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,32 @@ pip install -r requirements
python setup.py develop
```

Then, you can find example in examples dir. In simple case, ``run-maria`` will start Maria System.
You also can specify options by yourself like ``run-maria --debug`` or ``run-maria -p 22 --host 0.0.0.0``.
Get options define use this command ``run-maria -h``.
And ``run-maria --host-key=./examples/host.key -i file:./examples/ssh_interface.py#NGSSHInterface`` will start maria system with all examples.
Then, you can find example in examples dir. In simple case, ``maria`` will start Maria System.
You also can specify options by yourself like ``maria --debug`` or ``maria -b 0.0.0.0:2200``.
Get options define use this command ``maria -h``.
And ``maria -k host.key -b 127.0.0.1:2200 -w async run_socket:app`` will start maria system with all examples.

Anyway, I think single process will be ok in production environment with supervisord or something like that.

## Test

### with unittest
```bash
$ cd /path/to/maria/tests
$ python test_maria.py
```

### or with nose
First, nosetests is required. Get it:
```bash
# pip install nose
```
Then, this will run tests:
```bash
$ cd /path/to/maria/tests
$ nosetests -v
```

## Maybe a bug

I disable gevent subprocess monkey patch because I found the execute command function can not exit as I expect, can anyone test it?
Expand Down
2 changes: 2 additions & 0 deletions examples/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
maria -k host.key -b 127.0.0.1:2200 -w async run_socket:app
78 changes: 72 additions & 6 deletions maria/__main__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,81 @@
# -*- coding: utf-8 -*-
from SocketServer import TCPServer

import logging
import paramiko
from maria import Maria
from maria.config import config
from maria.config import Config
from maria.loader import load, load_class
from maria.colorlog import ColorizingStreamHandler

__all__ = ['main']


class Application(object):

def __init__(self, usage=None, prog=None):
self.usage = usage
self.prog = prog
self.config = None
self.app = None
self.logger = logging.getLogger(self.__class__.__name__)
self.load_config()

def init_log(self):
logging.StreamHandler = ColorizingStreamHandler
level = logging.DEBUG
if not self.config.debug:
level = logging.INFO
logging.BASIC_FORMAT = "%(asctime)s [%(name)s] %(message)s"
logging.basicConfig(level=level)
if self.config.log_file:
paramiko.util.log_to_file(self.config.log_file, level=level)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paramiko 的 log 能否都统一输出到 logging ,然后输出文件与否由 logging 的配置决定? cc @CMGS

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以的,直接上报到logging.root


def load_config(self):
self.config = Config(usage=self.usage, prog=self.prog)
parser = self.config.parse()
args = parser.parse_args()
self.config.load_options(args)
self.init_log()

# load xxx.xxx:app
if len(args.apps) < 1:
self.logger.info('No application module specified, using default setting')
app = load('maria.gssh.GSSHServer')
self.app = app(config=self.config)
else:
app = load(args.apps[0])
# command line options has priority over the app's
for key in dir(args):
if key.startswith('_') or key == 'apps':
continue
cmd_conf = getattr(args, key)
app_conf = getattr(app.config, key)
if cmd_conf == app_conf:
continue
setattr(app.config, key, cmd_conf)
if key == 'host_key_path':
self.logger.info('host key path got changed by command line')
app.init_key()
self.app = app
self.config.worker = app.config.worker

# choose worker
def load_worker(self):
if self.config.worker == 'sync':
return load('maria.worker.socket.SocketServer')
elif self.config.worker == 'async':
return load('maria.worker.ggevent.GeventServer')
else:
raise Exception('Invalid Worker!')

def run(self):
server = self.load_worker()
addr = self.app.config.get_addr()
return server(addr, self.app).run()


def main():
config.host_key_path = 'host.key'
app = Maria(config=config)
server = TCPServer(('0.0.0.0', 2022), app)
server.serve_forever()
Application("%(prog)s [OPTIONAL_ARGUMENTS] [APP_MODULE]").run()


if __name__ == '__main__':
Expand Down
82 changes: 69 additions & 13 deletions maria/config.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,87 @@
# -*- coding: utf-8 -*-

import os
import sys
import argparse


def populate_argument_parser(parser):
parser.add_argument("-b", "--bind", default="127.0.0.1:2200", dest="bind",
help="bind host:port")
parser.add_argument("-k", "--key", default="host.key", dest="host_key_path",
help="key file path")
parser.add_argument("-w", "--worker", default="sync", dest="worker",
help="worker async(gevent), sync(socketserver)")
parser.add_argument("--git-path", default="/usr/bin", dest="git_path",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同理

help="git command path")
parser.add_argument("--repo-path", default="", dest="project_root",
help="git repository root path")
parser.add_argument("--debug", default=False, dest="debug",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个参数有用么?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug吗? 在init_log 里面用过一次

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

action="store_true",
help="debug")
parser.add_argument("--log-file", default="./maria.log", dest="log_file",
help="log file path")
parser.add_argument("--auth-timeout", default=20, dest="auth_timeout",
type=int,
help="auth timeout")
parser.add_argument("--check-timeout", default=10, dest="check_timeout",
type=int,
help="check timeout")
parser.add_argument("--select-timeout", default=10, dest="select_timeout",
type=int,
help="select timeout")


class Config(object):

def __init__(self):
self.debug = False
self.log_file = '/tmp/maria.log'
self.host_key = ''
def __init__(self, usage=None, prog=None):
self.usage = usage
self.prog = prog or os.path.basename(sys.argv[0])
self.host_key = None

self.bind = '0.0.0.0:2200'
self.host_key_path = 'host.key'
self.worker = 'sync'
self.git_path = '/usr/bin'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个默认应该是指定 git 命令的地方,给个 /usr/bin 可能有点困惑。
默认最好是 git 或者是 /usr/bin/git

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好的

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

develop分支 gssh.py 改成了 command 加上路径
if self.config.git_path:
command[0] = os.path.join(self.config.git_path, command[0])

默认如果是 git 或者是 /usr/bin/git,不是执行不了了吗?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

执行不了指的是什么?没有理解。。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/CMGS/maria/blob/develop/maria/gssh.py#L111-L119

他说这里,是想要拼接git命令,不过似乎都没返回comannd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗯。是这个意思。现在 gssh.py 里面 check_git_command 的处理方式是把 git_path 拼接到commmand[0] 前头了。不用返回command,它保存在command[0]里头了

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

要不然就先去掉 git_path 这个选项吧,或者就先这样吧

self.project_root = ''
self.debug = False
self.log_file = './maria.log'
self.auth_timeout = 20
self.check_timeout = 10
self.select_timeout = 10
self.host = '0.0.0.0'
self.port = 2200
self.worker = ''
self.project_root = ''
self.git_path = ''
self._ = None

def parser(self, args):

# set the config options by args
def load_options(self, args):
for key in dir(args):
if key.startswith('_'):
if key.startswith('_') or key == 'apps':
continue
new_conf = getattr(args, key)
orig_conf = getattr(self, key, None)
if orig_conf is None or orig_conf == new_conf:
continue
setattr(self, key, new_conf)

# construct a ArgumentParser
def parse(self):
parser = argparse.ArgumentParser(
prog = self.prog,
usage = self.usage,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
populate_argument_parser(parser)
parser.add_argument("apps", nargs="*", help=argparse.SUPPRESS)
return parser

# return (host, port)
def get_addr(self):
addr = self.bind.split(':')
if len(addr) is not 2:
raise ValueError('Unrecognized argument value: "%s"' % args.bind)
return (addr[0], int(addr[1]))

def get(self, key):
if key not in self:
raise AttributeError("No configuration setting for: %s" % key)
return settings.__getattribute__(self, key)


config = Config()
1 change: 0 additions & 1 deletion maria/ghttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from os.path import join, exists, getmtime, getsize
from urllib import unquote
from .git import Git
from .config import config
from .date import format_date_time

HTTP_STATUS = {
Expand Down
68 changes: 32 additions & 36 deletions maria/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,46 +43,21 @@ def import_module(name, package=None):
__import__(name)
return sys.modules[name]

def load_class(uri, default="GSSHServer", section="maria.gssh"):
def load(uri):
parts = uri.split(":", 1) #str.split([sep[, maxsplit]])
if len(parts) == 1:
return load_class(parts[0])
elif len(parts) == 2:
module, obj = parts[0], parts[1]
return load_app(module, obj)
else:
raise Exception("load error: uri is invalid")

def load_class(uri, default="maria.worker.socket.SocketServer"):
if inspect.isclass(uri):
return uri
if uri.startswith("egg:"):
# uses entry points
entry_str = uri.split("egg:")[1]
try:
dist, name = entry_str.rsplit("#", 1)
except ValueError:
dist = entry_str
name = default

try:
return pkg_resources.load_entry_point(dist, section, name)
except:
exc = traceback.format_exc()
raise RuntimeError("class uri %r invalid "
"or not found: \n\n[%s]" % (uri,
exc))
elif uri.startswith("file:"):
path, klass = uri.split('file:')[1].rsplit('#')
path = os.path.realpath(path)
module = path.rsplit('/',1)[-1].strip('.py')
mod = imp.load_source('%s.%s' % (module, klass), path)
return getattr(mod, klass)
else:
components = uri.split('.')
if len(components) == 1:
try:
if uri.startswith("#"):
uri = uri[1:]

return pkg_resources.load_entry_point("maria",
section, uri)
except:
exc = traceback.format_exc()
raise RuntimeError("class uri %r invalid "
"or not found: \n\n[%s]" % (uri,
exc))

klass = components.pop(-1)
try:
mod = import_module('.'.join(components))
Expand All @@ -93,3 +68,24 @@ def load_class(uri, default="GSSHServer", section="maria.gssh"):
exc))

return getattr(mod, klass)

def load_app(module, obj):
sys.path.insert(0, os.getcwd())
try:
__import__(module)
except:
raise ImportError("Failed to import application")

mod = sys.modules[module]

try:
app = eval(obj, mod.__dict__)
except NameError:
raise Exception("Failed to find application: %r" % module)

if app is None:
raise Exception("Failed to find application object: %r" % obj)

if not callable(app):
raise Exception("Application object must be callable.")
return app
1 change: 1 addition & 0 deletions maria/worker/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
14 changes: 14 additions & 0 deletions maria/worker/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-

import logging


class WorkerClass(object):

def __init__(self, addr, app):
self.addr = addr
self.app = app
self.logger = logging.getLogger(self.__class__.__name__)

def run(self):
raise NotImplementedError()
21 changes: 21 additions & 0 deletions maria/worker/ggevent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -

try:
from gevent.monkey import patch_all
patch_all(subprocess=False, aggressive=False)
from gevent.server import StreamServer
except ImportError:
raise RuntimeError("You need install gevent")

from maria.worker.base import WorkerClass


class GeventServer(WorkerClass):

def run(self):
server = StreamServer(self.addr, self.app)
try:
self.logger.info('Maria System Start at %s:%s' % self.addr)
server.serve_forever()
except KeyboardInterrupt:
self.logger.info('Maria System Stopped')
15 changes: 15 additions & 0 deletions maria/worker/socket.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- coding: utf-8 -

from SocketServer import TCPServer
from maria.worker.base import WorkerClass


class SocketServer(WorkerClass):

def run(self):
server = TCPServer(self.addr, self.app)
try:
self.logger.info('Maria System Start at %s:%s' % self.addr)
server.serve_forever()
except KeyboardInterrupt:
self.logger.info('Maria System Stopped')
12 changes: 12 additions & 0 deletions tests/git_init_bare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

git_bare=$1

if [ -d $git_bare ]
then
exit 1
fi

mkdir $git_bare
cd $git_bare
git init --bare
Loading