diff --git a/bootstrapvz/plugins/file_copy/__init__.py b/bootstrapvz/plugins/file_copy/__init__.py new file mode 100644 index 0000000..aea8352 --- /dev/null +++ b/bootstrapvz/plugins/file_copy/__init__.py @@ -0,0 +1,16 @@ +import tasks + + +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.add(tasks.ValidateSourcePaths) + + if ('mkdirs' in manifest.plugins['file_copy']): + taskset.add(tasks.MkdirCommand) + if ('files' in manifest.plugins['file_copy']): + taskset.add(tasks.FileCopyCommand) diff --git a/bootstrapvz/plugins/file_copy/manifest-schema.yml b/bootstrapvz/plugins/file_copy/manifest-schema.yml new file mode 100644 index 0000000..25bdb12 --- /dev/null +++ b/bootstrapvz/plugins/file_copy/manifest-schema.yml @@ -0,0 +1,44 @@ +--- +$schema: http://json-schema.org/draft-04/schema# +properties: + plugins: + properties: + file_copy: + properties: + mkdirs: + items: + dir: + $ref: '#/definitions/absolute_path' + permissions: + type: string + owner: + type: string + group: + type: string + files: + items: + src: + $ref: '#/definitions/absolute_path' + dst: + $ref: '#/definitions/absolute_path' + permissions: + type: string + owner: + type: string + group: + type: string + minItems: 1 + type: array + required: + - src + - dst + required: + - files + type: object + required: + - file_copy + type: object +required: +- plugins +title: File copy plugin manifest +type: object diff --git a/bootstrapvz/plugins/file_copy/tasks.py b/bootstrapvz/plugins/file_copy/tasks.py new file mode 100644 index 0000000..06c5dd8 --- /dev/null +++ b/bootstrapvz/plugins/file_copy/tasks.py @@ -0,0 +1,65 @@ +from bootstrapvz.base import Task +from bootstrapvz.common import phases + +import os +import shutil + + +class ValidateSourcePaths(Task): + description = 'Check whether the files to be copied exist' + phase = phases.preparation + + @classmethod + def run(cls, info): + from bootstrapvz.common.exceptions import TaskError + for file_entry in info.manifest.plugins['file_copy']['files']: + srcfile = file_entry['src'] + if not os.path.isfile(srcfile): + msg = 'The source file %s does not exist.' % srcfile + raise TaskError(msg) + + +def modify_path(info, path, entry): + from bootstrapvz.common.tools import log_check_call + if 'permissions' in entry: + # We wrap the permissions string in str() in case + # the user specified a numeric bitmask + chmod_command = ['chroot', info.root, 'chmod', str(entry['permissions']), path] + log_check_call(chmod_command) + + if 'owner' in entry: + chown_command = ['chroot', info.root, 'chown', entry['owner'], path] + log_check_call(chown_command) + + if 'group' in entry: + chgrp_command = ['chroot', info.root, 'chgrp', entry['group'], path] + log_check_call(chgrp_command) + + +class MkdirCommand(Task): + description = 'copy files into the image' + phase = phases.system_modification + + @classmethod + def run(cls, info): + from bootstrapvz.common.tools import log_check_call + + for dir_entry in info.manifest.plugins['file_copy']['mkdirs']: + mkdir_command = ['chroot', info.root, 'mkdir', '-p', dir_entry['dir']] + log_check_call(mkdir_command) + modify_path(info, dir_entry['dir'], dir_entry) + + +class FileCopyCommand(Task): + description = 'copy files into the image' + phase = phases.system_modification + predecessors = [MkdirCommand] + + @classmethod + def run(cls, info): + for file_entry in info.manifest.plugins['file_copy']['files']: + # note that we don't use os.path.join because it can't + # handle absolute paths, which 'dst' most likely is. + final_destination = os.path.normpath("%s/%s" % (info.root, file_entry['dst'])) + shutil.copy(file_entry['src'], final_destination) + modify_path(info, file_entry['dst'], file_entry)