Giter Site home page Giter Site logo

ginkgo's People

Contributors

chadselph avatar davidwilemski avatar kelvl avatar kyleconroy avatar msabramo avatar objcode avatar progrium avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ginkgo's Issues

use runpy for loading configuration

runpy was made to make python -m <module> work. It is probably a better way to load a module and get its globals. It's also useful for looking up module service factories.

ProxyTypes dependency + __version__ import from setup.py

If ProxyTypes isn't already installed, setup.py will fail due to setup.py's "from ginkgo import __version__":

File "setup.py", line 39, in <module>
    from ginkgo import __version__
File "ginkgo/ginkgo/__init__.py", line 4, in <module>
    from .config import Config as _Config
File "ginkgo/config.py", line 3, in <module>
    from peak.util.proxies import ObjectWrapper
ImportError: No module named peak.util.proxies

Group should be/act as dict

As well its current interface, Group should be able to be used as a read-only dict. This way you can pass a config group into another framework as a dict quite easily:

logfile = "/var/log/foo.log"

class flask:
    debug = False
    testing = True
    secret_key = "woifh28fhw93fh"

Then in your app, setting up flask:

app.config.update(settings.group('flask'))

add namespaces to config module

Right now, config loads into a global registry. But it should namespace these configurations.

Also, there is a Namespace class used by load() now that is used for making sub configuration namespaces... it has nothing to do with this issue. Perhaps that should be renamed. Or maybe this should be called something else.

service init happens pre-daemonization

It should be safe to assume what you do in Service.init will not be affected by daemonizing. Opening files or creating ZMQ contexts should be safe. RIght now, this happens before daemonization. The application service needs to initialized after daemonizing.

services started by ginkgoctl don't always stop with ginkgoctl or SIGTERM

I've noticed several times that when you start a service with ginkoctl, it won't shut down or restart unless you send it SIGKILL.

This seems to have been particularly the case with one of my ginkgo services, which simply wraps flask using ginkgo's wrapper.

In one case, we were able to observe two ginkgoctl processes running for the same conf file -- one was the original start process, and the other was a restart process that we had kicked off to restart the service.

Context should abstract sleep()

Since you should be using a Service's interface to create greenthreads (spawn, spawn_later), we've almost abstracted the core gevent API. However, we still commonly call gevent.sleep() to yield or wait for a period. If we abstract this, we can later use a common interface whether you're using OS threads or greenlets. sleep() shouldn't live on Service because it's more global. However, I'd like to avoid having global functions on, say, the gservice module. So perhaps it makes most sense on Context.

default pidfile put in home directory

Putting the pidfile in the current directory by default feels messy. It should be in a global location by default (so you can run start/stop from anywhere). Inspired by this:

http://stackoverflow.com/questions/3957242/storing-pid-file-for-a-daemon-run-as-user

We can name the pidfile something more appropriate (myservice.conf.py -> myservice.pid or path.to.service.module -> path.to.service.module.pid) and then prepend with . and put in the current home directory. ie:

~/.myservice.pid
~/.path.to.service.module.pid

default logfile should be /dev/null

Leaving a file in the current directory is annoying and messy. It's usually never checked. If you want to see the output of running the daemon, you run it without daemonizing. If you want to daemonize and see the logs, you can explicitly define where to put them. Otherwise, we should just not care to log.

change RootService to Context, add configuration

Context should be the top level gservice object that a Runner will create to start an application. It should be responsible for parsing a configuration file if provided, or taking/overriding with a configuration dictionary (although the code should live in the config module). This means it should also run the service factory in the configuration.

It should also own the named global services. The _main_services in Service should live in Context. Services should have a reference to the Context (all services live within a context).

ultimately, you should be able to instantiate a gservice application by creating a Context instance. For example:

app1 = Context(file='/path/to/app1.conf.py')
app2 = Context(file='/path/to/app2.conf.py')

Allowing you to write functional tests of interacting applications in the same process.

Ginkgo on PyPI

Please put Ginkgo on PyPI to make it easier to deploy!

change use of "name"; make setproctitle optional

setproctitle is cool but is not a necessary dependency. Let's use it if we have it, but otherwise just skip it. However, it's useful to have a name for a service instance. For example, what we name the pidfile. So we should have a good default name value that can be overwritten by the name option if present. And if setproctitle is present, we'll use this name to set the proc title.

The default name will be the module path of the service returned by the service factory: mypackage.myservice

If a configuration file is used, we'll use the filename prefix as the name suffix: dev.conf.py -> mypackage.myservice-dev

However, it's likely we won't know the service module path until we actually run because of the lazy eval nature of the service factory. In that case, we'll use the module path provided by the service lookup (which should be close if not that right module path for the service). Ie:

gservice mypackage.myservice start

Would mean we use "mypackage.myservice" despite what the service factory returns in that module.

Alternatively, if just a configuration file is used, we'll name it after the configuration file:

gservice -C /path/to/myservice.conf.py start

Would mean we use "myservice".

ginkgoctl command should block until service is "ready"

It would be great for gingkoctl to provide a command that asked a service if it was ready.

The reasoning behind this is for startup scripts that use ginkgo start. Since the process backgrounds itself, it will return whether or not the service came up cleanly. The command should either return when it gets a response from the service, timeout, or notice that the service's pid is no longer running.

locking pid file failure still returns 0

When starting gservice, if the pidfile lock fails gservice still returns 0. This should be an error return.

Traceback (most recent call last):
File "/usr/local/python/bin/gservice", line 8, in
load_entry_point('gservice==0.2.0', 'console_scripts', 'gservice')()
File "/usr/local/python/lib/python2.7/site-packages/gservice-0.2.0-py2.7.egg/gservice/runner.py", line 22, in main
Runner().do_action()
File "/usr/local/python/lib/python2.7/site-packages/gservice-0.2.0-py2.7.egg/gservice/runner.py", line 255, in do_action
getattr(self, func)(_args, *_kwargs)
File "/usr/local/python/lib/python2.7/site-packages/gservice-0.2.0-py2.7.egg/gservice/runner.py", line 247, in _start
super(Runner, self)._start()
File "/usr/local/python/lib/python2.7/site-packages/python_daemon-1.6-py2.7.egg/daemon/runner.py", line 124, in _start
self.daemon_context.open()
File "/usr/local/python/lib/python2.7/site-packages/python_daemon-1.6-py2.7.egg/daemon/daemon.py", line 346, in open
self.pidfile.enter()
File "build/bdist.linux-i686/egg/lockfile/init.py", line 226, in enter
File "/usr/local/python/lib/python2.7/site-packages/python_daemon-1.6-py2.7.egg/daemon/pidfile.py", line 42, in acquire
super(TimeoutPIDLockFile, self).acquire(timeout, _args, *_kwargs)
File "build/bdist.linux-i686/egg/lockfile/pidlockfile.py", line 85, in acquire
lockfile.LockTimeout

reload should reload configuration

Reload currently gets caught by the runner, reloads logging configuration then calls reload on the top level service (RootService). When RootService becomes Context and Context is responsible for configuration, then it would be nice for Context to reload configuration.

replace asserts with better exceptions

We use asserts in various places, but we should reserve asserts for conditions a user should never hit. Most of our asserts are conditions the user can easily run into. Then we might also want to place asserts to actually assert assumptions about state. If a user ever reports seeing these, we will know there is a bug.

The name ginkgo on Pypi

Hi all,

I am looking to upload my project 'ginkgo' to Pypi. I found this package is already there and occupy the name.
However, I find this package hasn't been updated for 11 years and perhaps been deprecated.

Would you mind transferring the name 'ginkgo' to me?

Thanks, and please let me know if you have concerns.

Kang

Imported 3rd party modules equals None

import random
from ginkgo.core import Service
from ginkgo.runner import ControlInterface

class SomeService(Service):

    def do_start(self):
        print random.randint(0,1)


ginkgo test_managed_app2.conf.py                           [ruby-1.9.2-p320]
Starting process with test_managed_app2.conf.py...
2012-07-27 15:15:44,946    INFO zmq_services: Connecting to REP socket with address tcp://0.0.0.0:1112
2012-07-27 15:15:44,946    INFO zmq_services: Binding to REP socket with address tcp://0.0.0.0:2632
Traceback (most recent call last):
  File "/Users/darvin/Projects/ss9/Python/Heyghoge/pyenv/lib/python2.7/site-packages/gevent/greenlet.py", line 390, in run
    result = self._run(*self.args, **self.kwargs)
  File "/Users/darvin/Projects/ss9/Python/Heyghoge/src/base_application.py", line 223, in _ticking
    self.handle_tick()
  File "/Users/darvin/Projects/ss9/Python/Heyghoge/src/test_managed_app2.conf.py", line 10, in handle_tick
    self.health.load += random.randint(0,10)
AttributeError: 'NoneType' object has no attribute 'randint'
<Greenlet at 0x10fff4550: <bound method TestManagedApp2._ticking of <<run_path>.TestManagedApp2 object at 0x10fffba90>>> failed with AttributeError

should Service be a state machine?

I've gone back and forth about this. Imagine a service with these states:

  • idle
  • starting
  • ready
  • stopping
  • reloading

The require_ready decorator would wait for the ready state (although it could already do this). There could be a wait_for(state) method. Services could go out of ready state.

This is a more general solution to the issue here: https://github.com/progrium/gservice/issues/21

ginkgoctl gives 0 exit code and no errors if services fail to start

When starting a broken service with ginkgoctl, everything looks hunky dory:

# ginkgoctl ../collector.conf.py start || echo fail
Starting process with ../collector.conf.py...

But in fact, an exception had occurred somewhere inside the user's service:

# ginkgo ../collector.conf.py  
Starting process with ../collector.conf.py...
Traceback (most recent call last):
  File "/usr/local/python/bin/ginkgo", line 9, in <module>
    load_entry_point('Ginkgo==0.5.0dev', 'console_scripts', 'ginkgo')()
  File "/usr/local/python-2.7/lib/python2.7/site-packages/Ginkgo-0.5.0dev-py2.7.egg/ginkgo/runner.py", line 44, in run_ginkgo
    ControlInterface().start(args.target)
  File "/usr/local/python-2.7/lib/python2.7/site-packages/Ginkgo-0.5.0dev-py2.7.egg/ginkgo/runner.py", line 142, in start
    app.serve_forever()
  File "/usr/local/python-2.7/lib/python2.7/site-packages/Ginkgo-0.5.0dev-py2.7.egg/ginkgo/core.py", line 166, in serve_forever
    self.start()
  File "/usr/local/python-2.7/lib/python2.7/site-packages/Ginkgo-0.5.0dev-py2.7.egg/ginkgo/core.py", line 107, in start
    child.start(block_until_ready)
  File "/usr/local/python-2.7/lib/python2.7/site-packages/Ginkgo-0.5.0dev-py2.7.egg/ginkgo/core.py", line 107, in start
    child.start(block_until_ready)
  File "/usr/local/python-2.7/lib/python2.7/site-packages/Ginkgo-0.5.0dev-py2.7.egg/ginkgo/core.py", line 107, in start
    child.start(block_until_ready)
  File "/usr/local/python-2.7/lib/python2.7/site-packages/Ginkgo-0.5.0dev-py2.7.egg/ginkgo/core.py", line 109, in start
    ready = not self.do_start()
  ...
  and on down into the user's code.

This is a problem, since it's not possible for init.d scripts to detect whether or not they successfully started a ginkgo service.

core.require_ready() should block with a timeout before raising

If we can wait() on ready state, then we can make require_ready block within a timeout until ready. This makes it more useful in that you can continue to call that method without handling an exception, assuming the service will soon return to a ready state.

Add a license?

Gservice has no license at the moment, which keeps me from using it professionally. Are there plans to a a license?

support module service lookup

You should be able to specify a module instead of a configuration file such as:

gservice path.to.service.module start

This module will contain a service factory function (service()). Note that -C is not necessary, as it will attempt to use default configuration values. If -C is used, the configuration file will be used for configuration. However, if a service factory is in the configuration file, it will use that factory instead.

The canonical way to define a service factory is in the module that defines the top level service. You should always prefer to use the module lookup unless you need to override the service factory for some reason (however, if configuration is done correctly, you shouldn't have to).

Here is an example service module called package.myservice (in, say, package/myservice.py):

from gservice.core import Service

class MyService(Service):
    def do_start(self):
        pass

def service():
    return MyService()

Now you can run gservice package.myservice start

Make ginkgo support Twisted programs

Looking at this, I see two paths:

  1. Make gservice.core.Service polymorphic on Setting('twisted'). This is in the realm of possible, but will lead to some very weird initialization code or even weirder code in the body.
  2. Make gservice.core.TwistedService exist, move all the gevent-specific logic out of gservice.core.Service and into gservice.core.GeventService. This is significantly less optimal because it will make a mess of the class hierarchy.

I'll take a look at going down option 1 later this week in a branch and see if clean code can be made.

Sean

config.Setting descriptors should allow setting their value

Right now we don't let you because ... well, why would you? The value is managed by configuration. However, it makes sense for easy dependency injection to just set it directly instead of going through configuration. It seems like that's part of the advantage of having a class variable there anyway...

avoid printing exceptions (traceback.print_exc)

In Service.start() and .serve_forever() we catch all exceptions and call stop(). However, we added traceback.print_exc() so that if stop() raises, we can see the original exception. It seems like there is a better way to do this. Perhaps an inner try/except? Even though tests pass, we still get nasty, confusing output:

progrium-twilio:gservice progrium$ python setup.py test
running test
............................Traceback (most recent call last):
  File "/Users/progrium/Projects/gservice/gservice/core.py", line 179, in start
    ready = self.do_start()
  File "/Users/progrium/Projects/gservice/gservice/tests/test_service.py", line 49, in do_start
    raise Exception("Error")
Exception: Error
.Traceback (most recent call last):
  File "/Users/progrium/Projects/gservice/gservice/core.py", line 181, in start
    self._ready_event.wait(self.ready_timeout)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/gevent-0.13.3-py2.7-macosx-10.6-x86_64.egg/gevent/event.py", line 74, in wait
    result = get_hub().switch()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/gevent-0.13.3-py2.7-macosx-10.6-x86_64.egg/gevent/hub.py", line 163, in switch
    return greenlet.switch(self)
NotImplementedError: Second Error
...............
----------------------------------------------------------------------
Ran 44 tests in 7.403s

OK

ImportError: No module named resource

I have problem installing ginkgo. When I run setup.py following exception occurs.

C:\git\ginkgo>python setup.py install
Traceback (most recent call last):
File "setup.py", line 5, in
from ginkgo import version
File "C:\git\ginkgo\ginkgo__init__.py", line 25, in
from .core import Service
File "C:\git\ginkgo\ginkgo\core.py", line 19, in
from .util import AbstractStateMachine
File "C:\git\ginkgo\ginkgo\util.py", line 7, in
import resource
ImportError: No module named resource

gservice pid issue

When gservice programs crash they leave a pid file. On startup, they fail to lock the pid file instead of correctly determining there is no process at that pid and removing it.

Sean

you should be able to wait() on ready after service start

Currently there is no way to wait() on ready state because it's assumed the only time you have to wait for this is on start. This is why start has the default option to block_until_ready. However, it's likely a service will become unavailable (for example, a client loses its connection), so it makes sense it should drop out of ready state. Code that uses this service should then be able to block until it becomes ready again.

The simplest solution is to add a method to Service such as wait() or wait_until_ready(). However, changes to the Service interface should be done carefully.

any *.conf.py file in source tree breaks nosetests

During a recent code review, I mentioned that Ginkgo's current configuration file standard, where configuration files end in .conf.py, causes nose tests to break. Here's how you may reproduce it:

$ nosetests

----------------------------------------------------------------------
Ran 0 tests in 0.009s

OK
$ touch ginkgo/test.conf.py
$ nosetests
E
======================================================================
ERROR: Failure: ImportError (No module named ginkgo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/hblanks/virtualenv/main/lib/python2.7/site-packages/nose/loader.py", line 390, in loadTestsFromName
    addr.filename, addr.module)
  File "/Users/hblanks/virtualenv/main/lib/python2.7/site-packages/nose/importer.py", line 39, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/Users/hblanks/virtualenv/main/lib/python2.7/site-packages/nose/importer.py", line 71, in importFromDir
    fh, filename, desc = find_module(part, path)
ImportError: No module named ginkgo

----------------------------------------------------------------------
Ran 1 test in 0.010s

FAILED (errors=1)
$ 

This example uses the current ginkgo source tree and the latest version of nose. It's worth noting that any source tree should cause this behavior, provided you've put a *.conf.py file inside a directory that nose is going to search.

(My unsolicited two cents here is that Ginkgo files would be better either just as .py files, or else as INI-formatted .conf files, akin to CherryPy's.)

Calling self.stop() from within a service's greenlet results in incomplete execution of self.stop()

If one of a service's greenlets calls self.stop(), that greenlet will be killed (as part of shutting down the gevent AsyncManger -- cf. gevent.py's do_stop() method) before self.stop() finishes execution.

This means that the service will never actually reach the "stopped" state, with the result that anything waiting on the service to stop before returning (such as serve_forever()) will wait indefinitely.

ginkgo should return 0 when it fails

When gservice starts up it incorrectly returns a 0 return code for the following error cases:

  1. Logging permissions aren't met, and log file can't be opened
  2. gevent cannot be imported due to libevent configuration error
  3. PIDfile can't be locked.

I've included the traceback for issue #3 below.

Traceback (most recent call last):
File "/usr/local/python/bin/gservice", line 8, in
load_entry_point('gservice==0.2.0', 'console_scripts', 'gservice')()
File "/usr/local/python/lib/python2.7/site-packages/gservice-0.2.0-py2.7.egg/gservice/runner.py", line 22, in main
Runner().do_action()
File "/usr/local/python/lib/python2.7/site-packages/gservice-0.2.0-py2.7.egg/gservice/runner.py", line 255, in do_action
getattr(self, func)(_args, *_kwargs)
File "/usr/local/python/lib/python2.7/site-packages/gservice-0.2.0-py2.7.egg/gservice/runner.py", line 247, in _start
super(Runner, self)._start()
File "/usr/local/python/lib/python2.7/site-packages/python_daemon-1.6-py2.7.egg/daemon/runner.py", line 124, in _start
self.daemon_context.open()
File "/usr/local/python/lib/python2.7/site-packages/python_daemon-1.6-py2.7.egg/daemon/daemon.py", line 346, in open
self.pidfile.enter()
File "build/bdist.linux-i686/egg/lockfile/init.py", line 226, in enter
File "/usr/local/python/lib/python2.7/site-packages/python_daemon-1.6-py2.7.egg/daemon/pidfile.py", line 42, in acquire
super(TimeoutPIDLockFile, self).acquire(timeout, _args, *_kwargs)
File "build/bdist.linux-i686/egg/lockfile/pidlockfile.py", line 85, in acquire
lockfile.LockTimeout

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.