commit 352a61ae10c76ece74e886ab07f727b56762e20c Author: brettlangdon Date: Mon Sep 23 10:35:10 2013 -0400 initial prototype commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7aaf171 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.coverage +.cache +.ropeproject +*.py[co] +*.log +build +dist +docs/_* +*.egg-info diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0a1aaa2 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Brett Langdon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..d9c5504 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include README.* setup.py setup.cfg +recursive-include employ *.py +global-exclude *.pyc +global-exclude *.pyo diff --git a/README.md b/README.md new file mode 100644 index 0000000..39f24f1 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +employ +====== + +A distributed one time ec2 command execution and result aggregation framework. + +What? Basically it is like [beeswithmachineguns](https://github.com/newsapps/beeswithmachineguns.git), but more generic. +This tool can be used for running commands other than `ab`. diff --git a/bin/employ b/bin/employ new file mode 100755 index 0000000..a84f5ad --- /dev/null +++ b/bin/employ @@ -0,0 +1,77 @@ +#!/usr/bin/env python +""" +Employ instance to do your bidding + +Usage: + employ help (commands | command | regions) + employ run [options] [ ...] + +Global Options: + -h --help Show this message + --version Show the version number + +Run Options: + -i NUM, --instances NUM Number of instances to start [default: 1] + -r REGION, --region REGION Which region to start the instances up on [default: us-east-1] + -n NAME, --name NAME Which name to assign to each new instance [default: employed] + +Help Commands: + commands List all available commands + command Print the docstring for the provided command + regions List all available regions names +""" +import sys + +from docopt import docopt + +import employ +import employ.manager + + +def command_doc(command): + all_commands = employ.manager.Manager.available_commands() + if all_commands.get(command): + print all_commands[command].__doc__ + else: + sys.exit("Unknown command: '%s'" % command) + + +def list_commands(command=None): + all_commands = employ.manager.Manager.available_commands() + print "List of Available Commands:" + for cls in all_commands: + print " %s" % cls + + +def list_regions(): + print "List of Available Regions:" + for region in sorted(employ.manager.Manager.available_regions()): + print " %s" % region + + +def run(arguments): + all_commands = employ.manager.Manager.available_commands() + if arguments[""] not in all_commands: + sys.exit("Unknown command '%s'" % command) + command_cls = all_commands[arguments[""]] + command = command_cls(*arguments[""]) + + manager = employ.manager.Manager( + num_instances=arguments["--instances"], + name=arguments["--name"], + region=arguments["--region"], + ) + with manager: + manager.run(command) + + +arguments = docopt(__doc__, help=True, version="employ %s" % employ.__version__) +if arguments["help"]: + if arguments["commands"]: + list_commands() + elif arguments["command"] and arguments[""]: + command_doc(arguments[""]) + elif arguments["regions"]: + list_regions() +elif arguments["run"]: + run(arguments) diff --git a/employ/__init__.py b/employ/__init__.py new file mode 100644 index 0000000..3dc1f76 --- /dev/null +++ b/employ/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/employ/commands/__init__.py b/employ/commands/__init__.py new file mode 100644 index 0000000..877df82 --- /dev/null +++ b/employ/commands/__init__.py @@ -0,0 +1,3 @@ +__all__ = ["ABCommand", "Command"] +from employ.commands.base import Command +from employ.commands.ab import ABCommand diff --git a/employ/commands/ab.py b/employ/commands/ab.py new file mode 100644 index 0000000..504c74e --- /dev/null +++ b/employ/commands/ab.py @@ -0,0 +1,29 @@ +from employ.commands import Command + + +class ABCommand(Command): + """ + Command used to run ApacheBench (ab) + + Command Options: + [ ] + + Examples: + employ run ab http://127.0.0.1/test.html 1000 + employ run ab https://127.0.0.1:8000/test.html 1000 10 + employ run ab https://127.0.0.1:8000/test.html 1000 10 "-k -C 'cookie=1234'" + """ + name = "ab" + + def __init__(self, target, requests, concurrency=1, extra_args=""): + self.target = target + self.requests = requests + self.concurrency = concurrency + self.extra_args = extra_args + super(ABCommand, self).__init__() + + def command(self): + return "ab %s -n %s -c %s %s" % (self.target, self.requests, self.concurrency, self.extra_args) + + def aggregate(self, results): + pass diff --git a/employ/commands/base.py b/employ/commands/base.py new file mode 100644 index 0000000..d0ce91f --- /dev/null +++ b/employ/commands/base.py @@ -0,0 +1,5 @@ +class Command(object): + name = "command" + + def run(self): + raise NotImplementedError() diff --git a/employ/manager.py b/employ/manager.py new file mode 100644 index 0000000..3e2e92f --- /dev/null +++ b/employ/manager.py @@ -0,0 +1,40 @@ +import boto.ec2 + +from employ import commands + + +class Manager(object): + def __init__(self, num_instances=1, name="deployed", region="us-east-1"): + self.instances = [] + self.num_instances = num_instances + self.name = name + self.region = region + + def setup_instances(self): + print "starting %s instances named %s in region %s" % (self.num_instances, self.name, self.region) + + __enter__ = setup_instances + + def cleanup_instances(self): + print "tearing down instances" + + def __exit__(self, type, value, traceback): + self.cleanup_instances() + + @classmethod + def available_regions(cls): + for region in boto.ec2.regions(): + yield region.name + + @classmethod + def available_commands(cls): + all_commands = {} + for cls in commands.__all__: + the_cls = getattr(commands, cls) + if the_cls.name == "command": + continue + all_commands[the_cls.name] = the_cls + return all_commands + + def run(self, command): + print "running command '%s'" % command.command() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..53de545 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +boto>=2.13.0 +docopt>=0.6.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6826d5a --- /dev/null +++ b/setup.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +from setuptools import setup, find_packages + +from employ import __version__ + +setup( + name="employ", + version=__version__, + author="Brett Langdon", + author_email="brett@blangdon.com", + packages=find_packages(), + install_requires=[ + "docopt>=0.6.0", + "boto>=2.13.0", + ], + setup_requires=[], + description="Distributed one time command execution and aggregation tool", + license="MIT", + url='https://github.com/brettlangdon/employ', + classifiers=[ + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "License :: OSI Approved :: MIT License", + ], +)