This repository has been archived by the owner on Jan 25, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial import from previous repository
- Loading branch information
Doug Winter
committed
Feb 28, 2010
0 parents
commit ae82e95
Showing
11 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
GoCaptain start and stop scripts | ||
==================================== | ||
|
||
The GoCaptain [#]_ buildout recipe produces a script to start and stop daemons, | ||
similar to those you find in /etc/init.d. By default it will inspect your | ||
system and either write a "simple" script, such as you might produce yourself | ||
or produce a LinuxStandard Base variation, that provides more tooling. | ||
|
||
In particular the LSB scripts will try multiple times to shut down your daemon, and will not start it if it is already running. | ||
|
||
This package also provides a simple way to produce these scripts from other | ||
buildout recipes - see `isotoma.recipe.varnish`_ for an example. | ||
|
||
.. _`isotoma.recipe.varnish`: http://pypi.python.org/pypi/isotoma.recipe.varnish | ||
|
||
The buildout recipe | ||
------------------- | ||
|
||
A simple example would be:: | ||
|
||
[example] | ||
recipe = isotoma.recipe.gocaptain | ||
daemon = /usr/bin/example | ||
name = example | ||
description = example daemon for that thing i did that time | ||
pidfile = /var/tmp/example.pid | ||
args = | ||
-P ${example:pidfile} | ||
-w /var/tmp/example.log | ||
|
||
This will produce a script in bin/example that launches your daemon, and shuts | ||
it down again later, using the PID in the pidfile. | ||
|
||
Options | ||
~~~~~~~ | ||
|
||
The mandatory options this recipe accepts are: | ||
|
||
daemon | ||
The path to the daemon executable file | ||
name | ||
The name of the daemon, displayed in log messages | ||
description | ||
A longer description, shown on the console during start and stop | ||
pidfile | ||
A path to a file to store the PID of the new daemon in | ||
args | ||
The arguments for the daemon. These will be formatted in the output script as you provide them, with continuations provided as needed | ||
|
||
In addition you can provide: | ||
|
||
template | ||
A path to the template for your start/stop script. This will be used in preference to the templates provided with this package. | ||
|
||
Calling from other code | ||
----------------------- | ||
|
||
If you wish to use this from one of your own recipes, I suggest you do | ||
something like:: | ||
|
||
from isotoma.recipe import gocaptain | ||
gc = gocaptain.Automatic() | ||
f = open("/path/to/script", "w") | ||
gc.write(f, daemon="/usr/sbin/thing", | ||
args="-D -P /path/to/pid", | ||
name="my thing", description="thing") | ||
f.close() | ||
os.chmod(target, 0755) | ||
|
||
The Automatic module will select the Simple or LinuxStandardBase variants, by | ||
inspecting your system (very simplisticly!). | ||
|
||
License | ||
------- | ||
|
||
Copyright 2010 Doug Winter | ||
|
||
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. | ||
|
||
.. [#] The name comes from Cordwainer Smith | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
__import__('pkg_resources').declare_namespace(__name__) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
__import__('pkg_resources').declare_namespace(__name__) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
|
||
import os | ||
import logging | ||
|
||
from Cheetah.Template import Template | ||
|
||
here = os.path.dirname(__file__) | ||
|
||
class GoCaptain(object): | ||
|
||
""" Use this class from other buildout recipes - it is easier to call than | ||
the buildout recipe itself. """ | ||
|
||
template = None | ||
template_str = None | ||
defaults = {} | ||
required = [] | ||
|
||
def __init__(self, template=None, template_str=None, defaults={}, required=[]): | ||
self.template = template | ||
self.template_str = template_str | ||
self.defaults = defaults | ||
self.required = required | ||
|
||
def write(self, stream, **kw): | ||
for i in self.required: | ||
if not i in kw: | ||
raise KeyError("Missing option: %s" % i) | ||
for k, v in self.defaults.items(): | ||
kw.setdefault(k, v) | ||
if self.template is not None: | ||
t = open(self.template).read() | ||
else: | ||
t = self.template_str | ||
c = Template(t, searchList=kw) | ||
stream.write(str(c)) | ||
|
||
class Simple(GoCaptain): | ||
template = os.path.join(here, "simple.tmpl") | ||
defaults = { | ||
'preamble': '', | ||
} | ||
required = [ | ||
'daemon', | ||
'description', | ||
'pidfile', | ||
'args', | ||
] | ||
|
||
def __init__(self): | ||
pass | ||
|
||
class LinuxStandardBase(GoCaptain): | ||
template = os.path.join(here, "lsb.tmpl") | ||
defaults = { | ||
'preamble': '', | ||
} | ||
required = [ | ||
'name', | ||
'description', | ||
'daemon', | ||
'pidfile', | ||
'args', | ||
] | ||
|
||
def __init__(self): | ||
pass | ||
|
||
class Automatic(object): | ||
|
||
def __init__(self, *a, **kw): | ||
if os.path.exists('/lib/lsb/init-functions'): | ||
self.__class__ = LinuxStandardBase | ||
else: | ||
self.__class__ = Simple | ||
|
||
|
||
class Buildout(GoCaptain): | ||
|
||
""" This is the base class for the buildout recipes below """ | ||
|
||
def __init__(self, buildout, name, options): | ||
self.name = name | ||
self.options = options | ||
self.buildout = buildout | ||
self.logger = logging.getLogger(self.name) | ||
if self.template is not None: | ||
self.options.setdefault("template", self.template) | ||
|
||
def install(self): | ||
if 'template' in self.options: | ||
self.template = self.options['template'] | ||
target = os.path.join(self.buildout['buildout']['bin-directory'], self.name) | ||
try: | ||
self.write(open(target, "w"), **self.options) | ||
except KeyError: | ||
os.unlink(target) | ||
raise | ||
os.chmod(target, 0755) | ||
self.options.created(target) | ||
return self.options.created() | ||
|
||
def update(self): | ||
pass | ||
|
||
class SimpleBuildout(Buildout, Simple): | ||
pass | ||
|
||
class LinuxStandardBaseBuildout(LinuxStandardBase, Buildout): | ||
pass | ||
|
||
class AutomaticBuildout(object): | ||
|
||
def __init__(self, *a, **kw): | ||
if os.path.exists('/lib/lsb/init-functions'): | ||
self.__class__ = LinuxStandardBaseBuildout | ||
else: | ||
self.__class__ = SimpleBuildout | ||
Buildout.__init__(self, *a, **kw) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#! /bin/bash | ||
|
||
. /lib/lsb/init-functions | ||
|
||
${preamble} | ||
|
||
case "\$1" in | ||
start) | ||
output=\$(/bin/tempfile -s.$name) | ||
log_daemon_msg "Starting $description" | ||
log_progress_msg "$name" | ||
if start-stop-daemon \ | ||
--start --quiet --pidfile $pidfile --exec $daemon -- \ | ||
#echo " \\\n".join([12*" " + x.strip() for x in $args.strip()split("\n")]) | ||
> \${output} 2>&1; then | ||
log_end_msg 0 | ||
else | ||
log_end_msg 1 | ||
cat \$output | ||
exit 1 | ||
fi | ||
rm \$output | ||
;; | ||
stop) | ||
log_daemon_msg "Stopping $description" | ||
log_progress_msg "$name" | ||
if start-stop-daemon \ | ||
--stop --quiet --pidfile $pidfile --retry 10 \ | ||
--exec $daemon; then | ||
log_end_msg 0 | ||
else | ||
log_end_msg 1 | ||
fi | ||
;; | ||
*) | ||
log_success_msg "Usage: $0 {start|stop}" | ||
exit 1 | ||
;; | ||
esac | ||
|
||
exit 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#!/bin/sh | ||
|
||
${preamble} | ||
|
||
case "$1" in | ||
start) | ||
echo "Starting $description" | ||
exec ${daemon} \ | ||
#echo " \\\n".join([12*" " + x.strip() for x in $args.strip()split("\n")]) + "\n" | ||
;; | ||
stop) | ||
kill `cat ${pidfile}` | ||
;; | ||
*) | ||
echo "Usage $0 {start|stop}" | ||
exit 1 | ||
;; | ||
esac | ||
|
||
exit 0 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
$foo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import unittest | ||
from StringIO import StringIO | ||
import tempfile | ||
import os | ||
import UserDict | ||
|
||
from isotoma.recipe.gocaptain import GoCaptain, Buildout | ||
|
||
class MockOptions(UserDict.IterableUserDict): | ||
def __init__(self, *a, **kw): | ||
UserDict.IterableUserDict.__init__(self, *a, **kw) | ||
self._created = [] | ||
def created(self, *p): | ||
self._created.extend(p) | ||
|
||
class TestGoCaptain(unittest.TestCase): | ||
|
||
def test_basic(self): | ||
t = "$foo" | ||
g = GoCaptain(template_str=t) | ||
o = StringIO() | ||
g.write(o, foo="bar") | ||
self.assertEqual(o.getvalue(), "bar") | ||
|
||
def test_defaults(self): | ||
t = "$foo" | ||
g = GoCaptain(template_str=t, defaults={'foo': 'bar'}) | ||
o = StringIO() | ||
g.write(o) | ||
self.assertEqual(o.getvalue(), "bar") | ||
|
||
def test_required(self): | ||
t = "$foo" | ||
g = GoCaptain(template_str=t, required=['foo']) | ||
o = StringIO() | ||
self.assertRaises(KeyError, g.write, o) | ||
|
||
class X(Buildout): | ||
template = os.path.join(os.path.dirname(__file__), "basic.tmpl") | ||
defaults = {'foo': 'bar'} | ||
required = ['baz'] | ||
|
||
class TestBuildout(unittest.TestCase): | ||
|
||
def setUp(self): | ||
self.td = tempfile.mkdtemp() | ||
self.buildout = { | ||
'buildout': { | ||
'bin-directory': self.td, | ||
} | ||
} | ||
|
||
def test_basic(self): | ||
options = MockOptions({'foo': 'quux', 'baz': 'quuux'}) | ||
x = X(self.buildout, "name", options) | ||
x.install() | ||
target = os.path.join(self.td, 'name') | ||
self.assertEqual(options._created, [target]) | ||
d = open(target).read() | ||
self.assertEqual(d, "quux") | ||
os.unlink(target) | ||
os.rmdir(self.td) | ||
|
||
def test_defaults(self): | ||
options = MockOptions({'baz': 'quuux'}) | ||
x = X(self.buildout, "name", options) | ||
x.install() | ||
target = os.path.join(self.td, 'name') | ||
self.assertEqual(options._created, [target]) | ||
d = open(target).read() | ||
self.assertEqual(d, "bar") | ||
os.unlink(target) | ||
os.rmdir(self.td) | ||
|
||
def test_required(self): | ||
options = MockOptions() | ||
x = X(self.buildout, "name", options) | ||
self.assertRaises(KeyError, x.install) | ||
os.rmdir(self.td) | ||
|
||
|
||
|
Oops, something went wrong.