diff --git a/build-debian-cloud b/build-debian-cloud new file mode 100755 index 0000000..6c373a8 --- /dev/null +++ b/build-debian-cloud @@ -0,0 +1,6 @@ +#!/usr/bin/env python + + +if __name__ == '__main__' and __package__ is None: + from common import main + main.main() diff --git a/common/__init__.py b/common/__init__.py index 68bccdc..3c5ce38 100644 --- a/common/__init__.py +++ b/common/__init__.py @@ -2,3 +2,4 @@ from bootstrapinfo import BootstrapInformation from manifest import Manifest from task import Task from tasklist import TaskList +from plugin import Plugin diff --git a/common/bootstrapinfo.py b/common/bootstrapinfo.py index 445161d..138a537 100644 --- a/common/bootstrapinfo.py +++ b/common/bootstrapinfo.py @@ -3,4 +3,4 @@ class BootstrapInformation(object): def __init__(self, manifest=None, args=None): self.manifest = manifest - self.args = args + self.args = args diff --git a/common/main.py b/common/main.py new file mode 100644 index 0000000..8c8899c --- /dev/null +++ b/common/main.py @@ -0,0 +1,31 @@ +from providers import providers + + +def main(): + from argparse import ArgumentParser + parser = ArgumentParser(description='Bootstrap Debian for the cloud.') + parser.add_argument('--debug', action='store_true', + help='Print debugging information') + parser.set_defaults(func=run) + + subparsers = parser.add_subparsers(title='providers', description='supported providers', dest='provider') + + for provider in providers.values(): + provider.init_subparser(subparsers) + + args = parser.parse_args() + args.func(args) + + +def run(args): + provider = providers[args.provider] + manifest = provider.Manifest(args.manifest) + manifest.validate() + manifest.load_plugins() + + tasklist = provider.tasklist(manifest) + tasklist.plugins(manifest) + + from common import BootstrapInformation + bootstrap_info = BootstrapInformation(manifest, args) + tasklist.run(bootstrap_info) diff --git a/common/manifest.py b/common/manifest.py index d78c333..6aed6a9 100644 --- a/common/manifest.py +++ b/common/manifest.py @@ -7,8 +7,16 @@ class Manifest(object): self.parse(json.load(open(self.path))) def parse(self, data): - self.volume = data['volume'] - self.system = data['system'] + self.volume = data['volume'] + self.system = data['system'] + self.plugins = data['plugins'] def validate(self): pass + + def load_plugins(self): + self.loaded_plugins = [] + for modname in self.plugins.keys(): + if self.plugins[modname]['enabled']: + plugin = __import__('plugins.%s' % modname, fromlist=['plugins']) + self.loaded_plugins.append(plugin) diff --git a/common/tasklist.py b/common/tasklist.py index 5f34fe4..8472678 100644 --- a/common/tasklist.py +++ b/common/tasklist.py @@ -1,15 +1,24 @@ class TaskList(list): - def run(self, info): + + def plugins(self, manifest): + for plugin in manifest.loaded_plugins: + plugin.modify_tasklist(self, manifest) + + def run(self, bootstrap_info): for task in self: - task.run(info) + task.run(bootstrap_info) - def before(self, task): - pass + def before(self, ref, task): + i = next(i for i, task in enumerate(self) if type(task) is ref) + self.insert(i, task) - def replace(self, task): - pass + def replace(self, ref, task): + i = next(i for i, task in enumerate(self) if type(task) is ref) + self.pop(i) + self.insert(i, task) - def after(self, task): - pass + def after(self, ref, task): + i = next(i for i, task in enumerate(self) if type(task) is ref) + self.insert(i+1, task) diff --git a/debian-build-cloud b/debian-build-cloud deleted file mode 100755 index fcb420b..0000000 --- a/debian-build-cloud +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python - - -def main(): - from argparse import ArgumentParser - import ec2 - parser = ArgumentParser(description='Bootstrap Debian for the cloud.') - parser.add_argument('--debug', action='store_true', - help='Print debugging information') - - subparsers = parser.add_subparsers(title='providers', description='supported providers', dest='provider') - ec2.init_subparser(subparsers) - - args = parser.parse_args() - args.func(args) - -if __name__ == '__main__' and __package__ is None: - main() diff --git a/ec2/__init__.py b/ec2/__init__.py deleted file mode 100644 index 344904b..0000000 --- a/ec2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from main import init_subparser diff --git a/ec2/main.py b/ec2/main.py deleted file mode 100644 index 6509043..0000000 --- a/ec2/main.py +++ /dev/null @@ -1,37 +0,0 @@ - - -def init_subparser(subparsers): - cmd = subparsers.add_parser('ec2', help='Bootstrap Debian for EC2') - cmd.add_argument('--access-key', help='AWS Access Key', metavar='ID') - cmd.add_argument('--secret-key', help='AWS Secret Key', metavar='KEY') - cmd.add_argument('manifest', help='Manifest file to use for bootstrapping', metavar='MANIFEST') - cmd.set_defaults(func=run) - - -def run(args): - from manifest import Manifest - from common import BootstrapInformation - manifest = Manifest(args.manifest) - manifest.validate() - - task_list = get_tasklist(manifest) - - info = BootstrapInformation(manifest=manifest, args=args) - task_list.run(info) - - -def get_tasklist(manifest): - from common import TaskList - import packages - import ec2 - import host - task_list = TaskList() - task_list.extend([packages.HostPackages(), - packages.ImagePackages(), - ec2.GetCredentials(), - host.GetInfo(), - ec2.Connect(), - host.InstallPackages() - ]) - - return task_list diff --git a/manifests/ec2-ebs-pvm.manifest.json b/manifests/ec2-ebs-pvm.manifest.json index 266a2eb..b122581 100644 --- a/manifests/ec2-ebs-pvm.manifest.json +++ b/manifests/ec2-ebs-pvm.manifest.json @@ -25,12 +25,12 @@ "size" : "1G" }, "plugins": { - "admin-user": { + "admin_user": { "enabled": true }, - "build-metadata": { - "enabled": false, - "path": "/root/build-metadata-{ami_name}" + "build_metadata": { + "enabled": true, + "path" : "/root/build-metadata-{ami_name}" } } } diff --git a/plugins/__init__.py b/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/admin_user/__init__.py b/plugins/admin_user/__init__.py new file mode 100644 index 0000000..e221b6c --- /dev/null +++ b/plugins/admin_user/__init__.py @@ -0,0 +1,6 @@ + + +def modify_tasklist(tasklist, manifest): + from providers.ec2.packages import ImagePackages + from adminuser import AddSudoPackage + tasklist.after(ImagePackages, AddSudoPackage()) diff --git a/plugins/admin_user/adminuser.py b/plugins/admin_user/adminuser.py new file mode 100644 index 0000000..592c936 --- /dev/null +++ b/plugins/admin_user/adminuser.py @@ -0,0 +1,7 @@ +from common import Task + + +class AddSudoPackage(Task): + def run(self, info): + super(AddSudoPackage, self).run(info) + info.img_packages[0].add('sudo') diff --git a/plugins/build_metadata/__init__.py b/plugins/build_metadata/__init__.py new file mode 100644 index 0000000..18bc0f1 --- /dev/null +++ b/plugins/build_metadata/__init__.py @@ -0,0 +1,5 @@ + + +def modify_tasklist(tasklist, manifest): + from buildmetadata import PrintInfo + tasklist.append(PrintInfo()) diff --git a/plugins/build_metadata/buildmetadata.py b/plugins/build_metadata/buildmetadata.py new file mode 100644 index 0000000..bf894c6 --- /dev/null +++ b/plugins/build_metadata/buildmetadata.py @@ -0,0 +1,7 @@ +from common import Task + + +class PrintInfo(Task): + def run(self, info): + super(PrintInfo, self).run(info) + print('info') diff --git a/providers/__init__.py b/providers/__init__.py new file mode 100644 index 0000000..179b21d --- /dev/null +++ b/providers/__init__.py @@ -0,0 +1,4 @@ +import ec2 + + +providers = {'ec2': ec2} diff --git a/providers/ec2/__init__.py b/providers/ec2/__init__.py new file mode 100644 index 0000000..aae1fa6 --- /dev/null +++ b/providers/ec2/__init__.py @@ -0,0 +1,25 @@ +from manifest import Manifest + + +def init_subparser(subparsers): + sub = subparsers.add_parser('ec2', help='Bootstrap Debian for EC2') + sub.add_argument('--access-key', help='AWS Access Key', metavar='ID') + sub.add_argument('--secret-key', help='AWS Secret Key', metavar='KEY') + sub.add_argument('manifest', help='Manifest file to use for bootstrapping', metavar='MANIFEST') + + +def tasklist(manifest): + from common import TaskList + import packages + import ec2 + import host + task_list = TaskList() + task_list.extend([packages.HostPackages(), + packages.ImagePackages(), + ec2.GetCredentials(), + host.GetInfo(), + ec2.Connect(), + host.InstallPackages() + ]) + + return task_list diff --git a/ec2/ec2.py b/providers/ec2/ec2.py similarity index 97% rename from ec2/ec2.py rename to providers/ec2/ec2.py index e7ce082..2931a71 100644 --- a/ec2/ec2.py +++ b/providers/ec2/ec2.py @@ -5,7 +5,6 @@ class GetCredentials(Task): def run(self, info): super(GetCredentials, self).run(info) info.ec2_credentials = self.get_ec2_credentials(info.args, info.manifest) - return info def get_ec2_credentials(self, args, manifest): from os import getenv @@ -37,4 +36,4 @@ class Connect(Task): # info.ec2_connection = boto.ec2.connect_to_region(info.host['region'], # aws_access_key_id=info.ec2_credentials['access_key'], # aws_secret_access_key=info.ec2_credentials['secret_key']) - return info + # return 'ec2_connection', ec2_connection diff --git a/ec2/host.py b/providers/ec2/host.py similarity index 67% rename from ec2/host.py rename to providers/ec2/host.py index fec31c5..65ffd9c 100644 --- a/ec2/host.py +++ b/providers/ec2/host.py @@ -4,13 +4,15 @@ from common import Task class GetInfo(Task): def run(self, info): super(GetInfo, self).run(info) - import urllib2 - import json + # import urllib2 + # import json # response = urllib2.urlopen('http://169.254.169.254/latest/dynamic/instance-identity/document') # info.host = json.load(response.read()) - return info + # return info class InstallPackages(Task): def run(self, info): + # Check if packages are installed with + # /usr/bin/dpkg -s ${name} | grep -q 'Status: install' pass diff --git a/ec2/manifest.py b/providers/ec2/manifest.py similarity index 82% rename from ec2/manifest.py rename to providers/ec2/manifest.py index bccbba7..66b7aaa 100644 --- a/ec2/manifest.py +++ b/providers/ec2/manifest.py @@ -4,7 +4,7 @@ import common class Manifest(common.Manifest): def parse(self, data): super(Manifest, self).parse(data) - self.credentials = data["credentials"] + self.credentials = data["credentials"] self.virtualization = data["virtualization"] def validate(self): diff --git a/ec2/packages.py b/providers/ec2/packages.py similarity index 80% rename from ec2/packages.py rename to providers/ec2/packages.py index 803a7ca..bd13560 100644 --- a/ec2/packages.py +++ b/providers/ec2/packages.py @@ -4,27 +4,20 @@ from common import Task class HostPackages(Task): def run(self, info): super(HostPackages, self).run(info) - info.host_pkg = self.get_host_packages(info.manifest) - return info - - def get_host_packages(self, manifest): packages = set(['debootstrap', # To make sure a volume is not busy before unmounting we need lsof 'lsof', ]) - if manifest.volume['filesystem'] == 'xfs': + if info.manifest.volume['filesystem'] == 'xfs': packages.add('xfsprogs') - return packages + info.host_packages = packages class ImagePackages(Task): def run(self, info): super(ImagePackages, self).run(info) - info.image_pkg_include, info.image_pkg_exclude = self.get_image_packages(info.manifest) - return info - - def get_image_packages(self, manifest): + manifest = info.manifest # Add some basic packages we are going to need include = set(['udev', 'openssh-server', @@ -52,4 +45,4 @@ class ImagePackages(Task): include = include.union(manifest.system['packages']) - return include, exclude + info.img_packages = include, exclude