From 5dada603ebe9f2347235a2618360dd53bb0dfe13 Mon Sep 17 00:00:00 2001 From: Anders Ingemann Date: Sun, 13 Dec 2015 15:36:00 +0100 Subject: [PATCH] Add dockerfile to image, support labels, add documentation --- bootstrapvz/providers/docker/README.rst | 53 +++++++++++++++++++ bootstrapvz/providers/docker/__init__.py | 2 + .../providers/docker/manifest-schema.yml | 15 ++++++ bootstrapvz/providers/docker/tasks/image.py | 48 ++++++++++++++++- .../{jessie.yml => minimized-jessie.yml} | 4 ++ setup.py | 1 + 6 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 bootstrapvz/providers/docker/README.rst rename manifests/examples/docker/{jessie.yml => minimized-jessie.yml} (85%) diff --git a/bootstrapvz/providers/docker/README.rst b/bootstrapvz/providers/docker/README.rst new file mode 100644 index 0000000..155ebf9 --- /dev/null +++ b/bootstrapvz/providers/docker/README.rst @@ -0,0 +1,53 @@ +Docker +====== + +The `Docker `__ provider creates a docker image +from scratch, creates a Dockerfile for it and imports the image to a repo +specified in the manifest. + +In order to reduce the size of the image, it is highly recommend +to make use of the `minimize_size <../../plugins/minimize_size>`__ plugin. +With optimal settings a 64-bit jessie image can be whittled down to 81.95 MB +(built on Dec 13th 2015 with ``manifests/examples/docker/minimized-jessie.yml``). + + +Manifest settings +----------------- + +Provider +~~~~~~~~ + +- ``repository``: Repository to which the image should be imported. + ``required`` + +- ``tag``: Name with which the image should be tagged. + ``required`` + +- ``dockerfile``: Inline dockerfile that should be appended to + the one created by the bootstrapper. + ``optional`` + +- ``labels``: Labels that should be added to the dockerfile. + The image name specified at the top of the manifest + will be added as the label ``name``. + Check out the `docker docs `__ + for more information about custom labels. + `Project atomic `__ + also has some `useful recommendations `__ + for generic container labels. + ``optional`` + ``manifest vars`` + +Example: + +.. code:: yaml + + --- + provider: + name: docker + repository: bootstrap-vz + tag: latest + dockerfile: > + CMD /bin/bash + labels: + description: Debian {system.release} {system.architecture} diff --git a/bootstrapvz/providers/docker/__init__.py b/bootstrapvz/providers/docker/__init__.py index a02eede..452c1fd 100644 --- a/bootstrapvz/providers/docker/__init__.py +++ b/bootstrapvz/providers/docker/__init__.py @@ -25,6 +25,8 @@ def resolve_tasks(taskset, manifest): taskset.update(task_groups.cleanup_group) taskset.update([tasks.commands.AddRequiredCommands, + tasks.image.PopulateDockerfileLabels, + tasks.image.CreateDockerfile, tasks.image.CreateImage, ]) diff --git a/bootstrapvz/providers/docker/manifest-schema.yml b/bootstrapvz/providers/docker/manifest-schema.yml index d0f8140..dd50ff6 100644 --- a/bootstrapvz/providers/docker/manifest-schema.yml +++ b/bootstrapvz/providers/docker/manifest-schema.yml @@ -10,6 +10,21 @@ properties: type: string tag: type: string + labels: + type: object + properties: + # https://github.com/projectatomic/ContainerApplicationGenericLabels + distribution-scope: + type: string + enum: + - private + - authoritative-source-only + - restricted + - public + patternProperties: + ^.+$: {type: string} + dockerfile: + type: string required: [repository, tag] system: type: object diff --git a/bootstrapvz/providers/docker/tasks/image.py b/bootstrapvz/providers/docker/tasks/image.py index 0b074bb..2ce99b9 100644 --- a/bootstrapvz/providers/docker/tasks/image.py +++ b/bootstrapvz/providers/docker/tasks/image.py @@ -3,16 +3,62 @@ from bootstrapvz.common import phases from bootstrapvz.common.tools import log_check_call +class PopulateDockerfileLabels(Task): + description = 'Populating dockerfile labels' + phase = phases.image_registration + + @classmethod + def run(cls, info): + import pyrfc3339 + from datetime import datetime + import pytz + labels = {} + labels['name'] = info.manifest.name.format(**info.manifest_vars) + # Inspired by https://github.com/projectatomic/ContainerApplicationGenericLabels + # See here for the discussion on the debian-cloud mailing list + # https://lists.debian.org/debian-cloud/2015/05/msg00071.html + labels['architecture'] = info.manifest.system['architecture'] + labels['build-date'] = pyrfc3339.generate(datetime.utcnow().replace(tzinfo=pytz.utc)) + if 'labels' in info.manifest.provider: + for label, value in info.manifest.provider['labels'].items(): + labels[label] = value.format(**info.manifest_vars) + info._docker['dockerfile_labels'] = labels + + +class CreateDockerfile(Task): + description = 'Creating dockerfile' + phase = phases.image_registration + predecessors = [PopulateDockerfileLabels] + + @classmethod + def run(cls, info): + # pipes.quote converts newlines into \n rather than just prefixing + # it with a backslash, so we need to escape manually + def escape(value): + value = value.replace('"', '\\"') + value = value.replace('\n', '\\\n') + value = '"' + value + '"' + return value + labels = [] + for label, value in info._docker['dockerfile_labels'].items(): + labels.append(label + '=' + escape(value)) + # Add some nice newlines and indentation + info._docker['dockerfile'] = 'LABEL ' + ' \\\n '.join(labels) + '\n' + if 'dockerfile' in info.manifest.provider: + info._docker['dockerfile'] += info.manifest.provider['dockerfile'] + '\n' + + class CreateImage(Task): description = 'Creating docker image' phase = phases.image_registration + predecessors = [CreateDockerfile] @classmethod def run(cls, info): from pipes import quote tar_cmd = ['tar', '--create', '--numeric-owner', '--directory', info.volume.path, '.'] - docker_cmd = ['docker', 'import', '-', + docker_cmd = ['docker', 'import', '--change', info._docker['dockerfile'], '-', 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/minimized-jessie.yml similarity index 85% rename from manifests/examples/docker/jessie.yml rename to manifests/examples/docker/minimized-jessie.yml index b383766..71be5da 100644 --- a/manifests/examples/docker/jessie.yml +++ b/manifests/examples/docker/minimized-jessie.yml @@ -4,6 +4,10 @@ provider: name: docker repository: bootstrap-vz tag: latest + labels: + description: Debian {system.release} {system.architecture} + dockerfile: > + CMD /bin/bash bootstrapper: workspace: /target variant: minbase diff --git a/setup.py b/setup.py index a0e4854..829388c 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ setup(name='bootstrap-vz', 'pyyaml >= 3.10', 'boto >= 2.14.0', 'docopt >= 0.6.1', + 'pyrfc3339 >= 1.0', ], license='Apache License, Version 2.0', description='Bootstrap Debian images for virtualized environments',