Permalink
Browse files

initial import from previous repository

  • Loading branch information...
0 parents commit ae82e9555d6ca04026f34baeadaa23cf289b2933 Doug Winter committed Feb 28, 2010
No changes.
@@ -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
+
@@ -0,0 +1,2 @@
+__import__('pkg_resources').declare_namespace(__name__)
+
@@ -0,0 +1,2 @@
+__import__('pkg_resources').declare_namespace(__name__)
+
@@ -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)
@@ -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
@@ -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
No changes.
@@ -0,0 +1 @@
+$foo
@@ -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.

0 comments on commit ae82e95

Please sign in to comment.