From 4a509aba76ef47e1016686df4250933fcb411335 Mon Sep 17 00:00:00 2001 From: Anders Ingemann Date: Wed, 9 Dec 2015 19:00:06 +0100 Subject: [PATCH] First stab at docker provider Huh... That was easy. --- bootstrapvz/base/fs/__init__.py | 4 ++- bootstrapvz/base/manifest-schema.yml | 8 +++++ bootstrapvz/common/fs/folder.py | 24 +++++++++++++ bootstrapvz/common/task_groups.py | 5 +++ bootstrapvz/common/tasks/bootstrap.py | 6 ++-- bootstrapvz/common/tasks/folder.py | 27 +++++++++++++++ bootstrapvz/providers/docker/__init__.py | 34 +++++++++++++++++++ .../providers/docker/manifest-schema.yml | 32 +++++++++++++++++ .../providers/docker/tasks/__init__.py | 0 .../providers/docker/tasks/commands.py | 13 +++++++ bootstrapvz/providers/docker/tasks/image.py | 20 +++++++++++ manifests/examples/docker/jessie.yml | 26 ++++++++++++++ 12 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 bootstrapvz/common/fs/folder.py create mode 100644 bootstrapvz/common/tasks/folder.py create mode 100644 bootstrapvz/providers/docker/__init__.py create mode 100644 bootstrapvz/providers/docker/manifest-schema.yml create mode 100644 bootstrapvz/providers/docker/tasks/__init__.py create mode 100644 bootstrapvz/providers/docker/tasks/commands.py create mode 100644 bootstrapvz/providers/docker/tasks/image.py create mode 100644 manifests/examples/docker/jessie.yml diff --git a/bootstrapvz/base/fs/__init__.py b/bootstrapvz/base/fs/__init__.py index 1b8c7ab..ea744ef 100644 --- a/bootstrapvz/base/fs/__init__.py +++ b/bootstrapvz/base/fs/__init__.py @@ -24,12 +24,14 @@ def load_volume(data, bootloader): from bootstrapvz.common.fs.virtualdiskimage import VirtualDiskImage from bootstrapvz.common.fs.virtualharddisk import VirtualHardDisk from bootstrapvz.common.fs.virtualmachinedisk import VirtualMachineDisk + from bootstrapvz.common.fs.folder import Folder volume_backing = {'raw': LoopbackVolume, 's3': LoopbackVolume, 'vdi': VirtualDiskImage, 'vhd': VirtualHardDisk, 'vmdk': VirtualMachineDisk, - 'ebs': EBSVolume + 'ebs': EBSVolume, + 'folder': Folder }.get(data['backing']) # Instantiate the partition map diff --git a/bootstrapvz/base/manifest-schema.yml b/bootstrapvz/base/manifest-schema.yml index c3f2bda..7869ced 100644 --- a/bootstrapvz/base/manifest-schema.yml +++ b/bootstrapvz/base/manifest-schema.yml @@ -31,6 +31,13 @@ properties: tarball: {type: boolean} workspace: $ref: '#/definitions/path' + variant: + type: string + enum: + - minbase + - buildd + - fakechroot + - scratchbox required: [workspace] additionalProperties: false image: @@ -50,6 +57,7 @@ properties: - pvgrub - grub - extlinux + - none charmap: {type: string} hostname: type: string diff --git a/bootstrapvz/common/fs/folder.py b/bootstrapvz/common/fs/folder.py new file mode 100644 index 0000000..6581cee --- /dev/null +++ b/bootstrapvz/common/fs/folder.py @@ -0,0 +1,24 @@ +from bootstrapvz.base.fs.volume import Volume + + +class Folder(Volume): + + # Override the states this volume can be in (i.e. we can't "format" or "attach" it) + events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'attached'}, + {'name': 'delete', 'src': 'attached', 'dst': 'deleted'}, + ] + + extension = 'chroot' + + def create(self, path): + self.fsm.create(path=path) + + def _before_create(self, e): + import os + self.path = e.path + os.mkdir(self.path) + + def _before_delete(self, e): + from shutil import rmtree + rmtree(self.path) + del self.path diff --git a/bootstrapvz/common/task_groups.py b/bootstrapvz/common/task_groups.py index ab463f0..6b134eb 100644 --- a/bootstrapvz/common/task_groups.py +++ b/bootstrapvz/common/task_groups.py @@ -16,6 +16,7 @@ from tasks import network from tasks import initd from tasks import ssh from tasks import kernel +from tasks import folder def get_standard_groups(manifest): @@ -94,6 +95,9 @@ ssh_group = [ssh.AddOpenSSHPackage, def get_network_group(manifest): + if manifest.bootstrapper.get('variant', None) == 'minbase': + # minbase has no networking + return [] group = [network.ConfigureNetworkIF, network.RemoveDNSInfo] if manifest.system.get('hostname', False): @@ -182,6 +186,7 @@ rollback_map = {workspace.CreateWorkspace: workspace.DeleteWorkspace, partitioning.MapPartitions: partitioning.UnmapPartitions, filesystem.CreateMountDir: filesystem.DeleteMountDir, filesystem.MountRoot: filesystem.UnmountRoot, + folder.Create: folder.Delete, } diff --git a/bootstrapvz/common/tasks/bootstrap.py b/bootstrapvz/common/tasks/bootstrap.py index e656f63..f9e0cbb 100644 --- a/bootstrapvz/common/tasks/bootstrap.py +++ b/bootstrapvz/common/tasks/bootstrap.py @@ -21,6 +21,8 @@ def get_bootstrap_args(info): executable = ['debootstrap'] arch = info.manifest.system.get('userspace_architecture', info.manifest.system.get('architecture')) options = ['--arch=' + arch] + if 'variant' in info.manifest.bootstrapper: + options.append('--variant=' + info.manifest.bootstrapper['variant']) if len(info.include_packages) > 0: options.append('--include=' + ','.join(info.include_packages)) if len(info.exclude_packages) > 0: @@ -53,8 +55,8 @@ class MakeTarball(Task): else: from ..tools import log_call status, out, err = log_call(executable + options + ['--make-tarball=' + info.tarball] + arguments) - if status != 1: - msg = 'debootstrap exited with status {status}, it should exit with status 1'.format(status=status) + if status not in [0, 1]: # variant=minbase exits with 0 + msg = 'debootstrap exited with status {status}, it should exit with status 0 or 1'.format(status=status) raise TaskError(msg) diff --git a/bootstrapvz/common/tasks/folder.py b/bootstrapvz/common/tasks/folder.py new file mode 100644 index 0000000..b576993 --- /dev/null +++ b/bootstrapvz/common/tasks/folder.py @@ -0,0 +1,27 @@ +from bootstrapvz.base import Task +from bootstrapvz.common import phases +import volume +import workspace + + +class Create(Task): + description = 'Creating volume folder' + phase = phases.volume_creation + successors = [volume.Attach] + + @classmethod + def run(cls, info): + import os.path + info.root = os.path.join(info.workspace, 'root') + info.volume.create(info.root) + + +class Delete(Task): + description = 'Deleting volume folder' + phase = phases.cleaning + successors = [workspace.DeleteWorkspace] + + @classmethod + def run(cls, info): + info.volume.delete() + del info.root diff --git a/bootstrapvz/providers/docker/__init__.py b/bootstrapvz/providers/docker/__init__.py new file mode 100644 index 0000000..63fa1a8 --- /dev/null +++ b/bootstrapvz/providers/docker/__init__.py @@ -0,0 +1,34 @@ +from bootstrapvz.common.tasks import folder +from bootstrapvz.common.tasks import filesystem +from bootstrapvz.common.tasks import locale +from bootstrapvz.common import task_groups +import tasks.commands +import tasks.image + + +def validate_manifest(data, validator, error): + import os.path + schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.yml')) + validator(data, schema_path) + + +def resolve_tasks(taskset, manifest): + taskset.update(task_groups.get_base_group(manifest)) + taskset.update([folder.Create, + filesystem.CopyMountTable, + locale.SetTimezone, + filesystem.RemoveMountTable, + folder.Delete, + ]) + taskset.update(task_groups.get_network_group(manifest)) + taskset.update(task_groups.get_apt_group(manifest)) + taskset.update(task_groups.security_group) + taskset.update(task_groups.cleanup_group) + + taskset.update([tasks.commands.AddRequiredCommands, + tasks.image.CreateImage, + ]) + + +def resolve_rollback_tasks(taskset, manifest, completed, counter_task): + taskset.update(task_groups.get_standard_rollback_tasks(completed)) diff --git a/bootstrapvz/providers/docker/manifest-schema.yml b/bootstrapvz/providers/docker/manifest-schema.yml new file mode 100644 index 0000000..d0f8140 --- /dev/null +++ b/bootstrapvz/providers/docker/manifest-schema.yml @@ -0,0 +1,32 @@ +--- +$schema: http://json-schema.org/draft-04/schema# +title: Docker manifest +type: object +properties: + provider: + type: object + properties: + repository: + type: string + tag: + type: string + required: [repository, tag] + system: + type: object + properties: + bootloader: + type: string + enum: [none] + volume: + type: object + properties: + backing: + type: string + enum: [folder] + partitions: + type: object + properties: + type: + type: string + enum: [none] + required: [backing] diff --git a/bootstrapvz/providers/docker/tasks/__init__.py b/bootstrapvz/providers/docker/tasks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bootstrapvz/providers/docker/tasks/commands.py b/bootstrapvz/providers/docker/tasks/commands.py new file mode 100644 index 0000000..2ee74c2 --- /dev/null +++ b/bootstrapvz/providers/docker/tasks/commands.py @@ -0,0 +1,13 @@ +from bootstrapvz.base import Task +from bootstrapvz.common import phases +from bootstrapvz.common.tasks import host + + +class AddRequiredCommands(Task): + description = 'Adding commands required for docker' + phase = phases.preparation + successors = [host.CheckExternalCommands] + + @classmethod + def run(cls, info): + info.host_dependencies['docker'] = 'docker.io' diff --git a/bootstrapvz/providers/docker/tasks/image.py b/bootstrapvz/providers/docker/tasks/image.py new file mode 100644 index 0000000..dafe6ff --- /dev/null +++ b/bootstrapvz/providers/docker/tasks/image.py @@ -0,0 +1,20 @@ +from bootstrapvz.base import Task +from bootstrapvz.common import phases +from bootstrapvz.common.tasks import image +from bootstrapvz.common.tools import log_check_call + + +class CreateImage(Task): + description = 'Creating docker image' + phase = phases.image_registration + predecessors = [image.MoveImage] + + @classmethod + def run(cls, info): + from pipes import quote + tar_cmd = ['tar', '--create', '--numeric-owner', + '--directory', info.volume.path, '.'] + docker_cmd = ['docker', 'import', '-', + info.manifest.provider['repository'] + ':' + info.manifest.provider['tag']] + cmd = ' '.join(map(quote, tar_cmd)) + ' | ' + ' '.join(map(quote, docker_cmd)) + [info._docker['container_id']] = log_check_call(cmd, shell=True) diff --git a/manifests/examples/docker/jessie.yml b/manifests/examples/docker/jessie.yml new file mode 100644 index 0000000..4eee878 --- /dev/null +++ b/manifests/examples/docker/jessie.yml @@ -0,0 +1,26 @@ +--- +provider: + name: docker + repository: bootstrap-vz + tag: latest +bootstrapper: + workspace: /target + variant: minbase +image: + name: debian-{system.release}-{system.architecture}-{%y}{%m}{%d} + description: Debian {system.release} {system.architecture} +system: + release: jessie + architecture: amd64 + bootloader: none + charmap: UTF-8 + locale: en_US + timezone: UTC +packages: {} +volume: + backing: folder + partitions: + type: none + root: + filesystem: ext4 + size: 1GiB