diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cb76a2b..fcb3277 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,18 @@ Changelog ========= +2017-02-20 +---------- +Hugo Antoniio Sepulveda Manriquez: + * Updated puppet plugin module: + * Installs Puppetlabs 4 PC1 agent software from apt.puppetlabs.com + * Enables you to install modules from forge.puppetlabs.com in the image + + * Important limitations + * Only works for Wheezy and Jessie for now. + * If you need puppet 3, just add 'puppet' packages provider list. + * modules: When installing from forge, it assumes 'install --force' + * modules: When installing from forge, It assumes master version on forge 2016-06-04 ---------- diff --git a/bootstrapvz/plugins/puppet/README.rst b/bootstrapvz/plugins/puppet/README.rst index 530694f..86c3aa3 100644 --- a/bootstrapvz/plugins/puppet/README.rst +++ b/bootstrapvz/plugins/puppet/README.rst @@ -1,16 +1,25 @@ Puppet ------ -Installs `puppet `__ and optionally applies a +Installs `puppet version 4 ` PC1 From the site +repository `` and optionally applies a manifest inside the chroot. You can also have it copy your puppet configuration into the image so it is readily available once the image is booted. -Keep in mind that when applying a manifest, the system is in a chrooted -environment. This can prevent daemons from running properly (e.g. -listening to ports), they will also need to be shut down gracefully -(which bootstrap-vz cannot do) before unmounting the volume. It is -advisable to avoid starting any daemons inside the chroot at all. +Rationale and use case +~~~~~~~~~~~~~~~~~~~~~~ + +You want to use this plugin when you wish to create an image and to be able to +manage that image with Puppet. You have a Puppet 4 setup in mind and thus you +want the image to contain the puppet agent software from the puppetlabs repo. +You want it to almost contain everything you need to get it up and running +This plugin does just that! +While you're at it, throw in some modules from the forge as well! +Want to include your own modules? Include them as assets! + +For now this plugin is only compatible with Debian versions Wheezy and Jessie. +These are also the distributions supported by puppetlabs. Settings ~~~~~~~~ @@ -18,7 +27,61 @@ Settings - ``manifest``: Path to the puppet manifest that should be applied. ``optional`` - ``assets``: Path to puppet assets. The contents will be copied into - ``/etc/puppet`` on the image. Any existing files will be overwritten. - ``optional`` -- ``enable_agent``: Whether the puppet agent daemon should be enabled. + ``/etc/puppetlabs`` on the image. Any existing files will be overwritten. ``optional`` +- ``install_modules``: A list of modules you wish to install available from + `` inside the chroot. It will assume a FORCED + install of the modules. + This list is a list of tuples. Every tuple must at least contain the module + name. A version is optional, when no version is given, it will take the + latest version available from the forge. + Format: [module_name (required), version (optional)] +- ``enable_agent``: Whether the puppet agent daemon should be enabled. + ``optional - not recommended``. disabled by default. UNTESTED + +An example bootstrap-vz manifest is included in the ``KVM`` folder of the +manifests examples directory. + +Limitations +~~~~~~~~~~~ +(Help is always welcome, feel free to chip in!) +General: + +- This plugin only installs the PC1 package for now, needs to be extended to + be able to install the package of choice +- The puppetlabs agent is only available to i386 and amd64 architectures. ARM + is not available from repository at this time. If you need to have the agent + on ARM, you need to build the agent yourself, and install them through the + ``packages`` section of the bootstrap-vz manifest + +Manifests: + +- Running puppet manifests is not recommended and untested, see below + +Assets: + +- The assets path must be ABSOLUTE to your manifest file. + +install_modules: + +- It assumes installing the given list of tuples of modules with the following + command: + "... install --force $module_name (--version $version_number)" + The module name is mandatory, the version is optional. When no version is + given, it will pick the master version of the + module from `` +- It assumes the modules are installed into the "production" environment. + Installing into another environment e.g. develop, is currently not + implemented. +- You cannot include local modules this way, to include you homebrewn modules, + You need to inject them through the assets directive. + +UNTESTED: + +- Enabling the agent and applying the manifest inside the chrooted environment. + Keep in mind that when applying a manifest when enabling the agent option, + the system is in a chrooted environment. This can prevent daemons from + running properly (e.g. listening to ports), they will also need to be shut + down gracefully (which bootstrap-vz cannot do) before unmounting the + volume. It is advisable to avoid starting any daemons inside the chroot at + all. diff --git a/bootstrapvz/plugins/puppet/__init__.py b/bootstrapvz/plugins/puppet/__init__.py index a1f3765..55ac88f 100644 --- a/bootstrapvz/plugins/puppet/__init__.py +++ b/bootstrapvz/plugins/puppet/__init__.py @@ -7,12 +7,17 @@ def validate_manifest(data, validator, error): def resolve_tasks(taskset, manifest): - taskset.add(tasks.AddPackages) + taskset.add(tasks.CheckRequestedDebianRelease) + taskset.add(tasks.AddPuppetlabsPC1SourcesList) + taskset.add(tasks.InstallPuppetlabsPC1ReleaseKey) + taskset.add(tasks.InstallPuppetAgent) if 'assets' in manifest.plugins['puppet']: taskset.add(tasks.CheckAssetsPath) taskset.add(tasks.CopyPuppetAssets) if 'manifest' in manifest.plugins['puppet']: taskset.add(tasks.CheckManifestPath) taskset.add(tasks.ApplyPuppetManifest) + if 'install_modules' in manifest.plugins['puppet']: + taskset.add(tasks.InstallModules) if manifest.plugins['puppet'].get('enable_agent', False): taskset.add(tasks.EnableAgent) diff --git a/bootstrapvz/plugins/puppet/assets/gpg-keyrings-PC1/jessie/puppetlabs-pc1-keyring.gpg b/bootstrapvz/plugins/puppet/assets/gpg-keyrings-PC1/jessie/puppetlabs-pc1-keyring.gpg new file mode 100644 index 0000000..33a7a49 Binary files /dev/null and b/bootstrapvz/plugins/puppet/assets/gpg-keyrings-PC1/jessie/puppetlabs-pc1-keyring.gpg differ diff --git a/bootstrapvz/plugins/puppet/assets/gpg-keyrings-PC1/wheezy/puppetlabs-pc1-keyring.gpg b/bootstrapvz/plugins/puppet/assets/gpg-keyrings-PC1/wheezy/puppetlabs-pc1-keyring.gpg new file mode 100644 index 0000000..33a7a49 Binary files /dev/null and b/bootstrapvz/plugins/puppet/assets/gpg-keyrings-PC1/wheezy/puppetlabs-pc1-keyring.gpg differ diff --git a/bootstrapvz/plugins/puppet/manifest-schema.yml b/bootstrapvz/plugins/puppet/manifest-schema.yml index 3e4ff76..26f88fd 100644 --- a/bootstrapvz/plugins/puppet/manifest-schema.yml +++ b/bootstrapvz/plugins/puppet/manifest-schema.yml @@ -12,6 +12,19 @@ properties: assets: {$ref: '#/definitions/absolute_path'} enable_agent: {type: boolean} manifest: {$ref: '#/definitions/absolute_path'} + install_modules: + type: array + minItems: 1 + items: + type: array + items: + name: + anyOf: + - pattern: ^[^/]+(/[^/]+)?$ + version: + type: number + required: [name] + minItems: 1 minProperties: 1 additionalProperties: false definitions: diff --git a/bootstrapvz/plugins/puppet/tasks.py b/bootstrapvz/plugins/puppet/tasks.py index cd12973..96144d6 100644 --- a/bootstrapvz/plugins/puppet/tasks.py +++ b/bootstrapvz/plugins/puppet/tasks.py @@ -1,12 +1,32 @@ +import os from bootstrapvz.base import Task from bootstrapvz.common import phases -from bootstrapvz.common.tools import sed_i -import os +from bootstrapvz.common.exceptions import TaskError +from bootstrapvz.common.releases import jessie, wheezy +from bootstrapvz.common.tools import sed_i, log_check_call, rel_path +from __builtin__ import str + + +ASSETS_DIR_JESSIE = rel_path(__file__, 'assets/gpg-keyrings-PC1/jessie') +ASSETS_DIR_WHEEZY = rel_path(__file__, 'assets/gpg-keyrings-PC1/wheezy') + + +class CheckRequestedDebianRelease(Task): + description = 'Checking whether there is a release available for {info.manifest.release}' + phase = phases.validation + + @classmethod + def run(cls, info): + from bootstrapvz.common.exceptions import TaskError + if not info.manifest.release == (jessie or wheezy): + msg = 'Debian {info.manifest.release} is not (yet) available in the Puppetlabs.com APT repository.' + raise TaskError(msg) class CheckAssetsPath(Task): description = 'Checking whether the assets path exist' phase = phases.validation + predecessors = [CheckRequestedDebianRelease] @classmethod def run(cls, info): @@ -21,12 +41,12 @@ class CheckAssetsPath(Task): class CheckManifestPath(Task): - description = 'Checking whether the manifest path exist' + description = 'Checking whether the manifest file path exist inside the assets' phase = phases.validation + predecessors = [CheckAssetsPath] @classmethod def run(cls, info): - from bootstrapvz.common.exceptions import TaskError manifest = info.manifest.plugins['puppet']['manifest'] if not os.path.exists(manifest): msg = 'The manifest file {manifest} does not exist.'.format(manifest=manifest) @@ -36,28 +56,77 @@ class CheckManifestPath(Task): raise TaskError(msg) -class AddPackages(Task): - description = 'Add puppet package' +class InstallPuppetlabsPC1ReleaseKey(Task): + description = 'Install puppetlabs PC1 Release key into the keyring' + phase = phases.package_installation + + @classmethod + def run(cls, info): + from shutil import copy + if (info.manifest.release == jessie): + key_path = os.path.join(ASSETS_DIR_JESSIE, 'puppetlabs-pc1-keyring.gpg') + if (info.manifest.release == wheezy): + key_path = os.path.join(ASSETS_DIR_WHEEZY, 'puppetlabs-pc1-keyring.gpg') + destination = os.path.join(info.root, 'etc/apt/trusted.gpg.d/puppetlabs-pc1-keyring.gpg') + copy(key_path, destination) + + +class AddPuppetlabsPC1SourcesList(Task): + description = 'Adding Puppetlabs APT repo to the list of sources.' phase = phases.preparation @classmethod def run(cls, info): - info.packages.add('puppet') + if (info.manifest.release == jessie): + info.source_lists.add('puppetlabs', 'deb http://apt.puppetlabs.com jessie PC1') + if (info.manifest.release == wheezy): + info.source_lists.add('puppetlabs', 'deb http://apt.puppetlabs.com wheezy PC1') -class CopyPuppetAssets(Task): - description = 'Copying puppet assets' +class InstallPuppetAgent(Task): + description = 'Install puppet-agent from https://apt.puppetlabs.com for {system.release}' phase = phases.system_modification + @classmethod + def run(cls, info): + log_check_call(['chroot', info.root, 'apt-get', 'install', '--assume-yes', 'puppet-agent']) + + +class InstallModules(Task): + description = 'Installing Puppet modules' + phase = phases.system_modification + predecessors = [InstallPuppetAgent] + + @classmethod + def run(cls, info): + for module in info.manifest.plugins['puppet']['install_modules']: + command = ['chroot', info.root, '/opt/puppetlabs/bin/puppet', 'module', 'install', '--force'] + if (len(module) == 1): + [module_name] = module + command.append(str(module_name)) + if (len(module) == 2): + [module_name, module_version] = module + command.append(str(module_name)) + command.append('--version') + command.append(str(module_version)) + log_check_call(command) + + +class CopyPuppetAssets(Task): + description = 'Copying declared custom puppet assets.' + phase = phases.system_modification + predecessors = [InstallModules] + @classmethod def run(cls, info): from bootstrapvz.common.tools import copy_tree - copy_tree(info.manifest.plugins['puppet']['assets'], os.path.join(info.root, 'etc/puppet')) + copy_tree(info.manifest.plugins['puppet']['assets'], os.path.join(info.root, 'etc/puppetlabs/')) class ApplyPuppetManifest(Task): - description = 'Applying puppet manifest' - phase = phases.user_modification + description = 'Applying puppet manifest.' + phase = phases.system_modification + predecessors = [CopyPuppetAssets] @classmethod def run(cls, info): @@ -65,19 +134,14 @@ class ApplyPuppetManifest(Task): hostname = handle.read().strip() with open(os.path.join(info.root, 'etc/hosts'), 'a') as handle: handle.write('127.0.0.1\t{hostname}\n'.format(hostname=hostname)) - from shutil import copy pp_manifest = info.manifest.plugins['puppet']['manifest'] manifest_rel_dst = os.path.join('tmp', os.path.basename(pp_manifest)) manifest_dst = os.path.join(info.root, manifest_rel_dst) copy(pp_manifest, manifest_dst) - manifest_path = os.path.join('/', manifest_rel_dst) - from bootstrapvz.common.tools import log_check_call - log_check_call(['chroot', info.root, - 'puppet', 'apply', manifest_path]) + log_check_call(['chroot', info.root, 'puppet', 'apply', '--verbose', '--debug', manifest_path]) os.remove(manifest_dst) - hosts_path = os.path.join(info.root, 'etc/hosts') sed_i(hosts_path, '127.0.0.1\s*{hostname}\n?'.format(hostname=hostname), '') diff --git a/manifests/examples/kvm/jessie-puppet.yaml b/manifests/examples/kvm/jessie-puppet.yaml new file mode 100644 index 0000000..1757570 --- /dev/null +++ b/manifests/examples/kvm/jessie-puppet.yaml @@ -0,0 +1,45 @@ +--- +name: debian-{system.release}-{system.architecture}-{%Y}{%m}{%d} +provider: + name: kvm + virtio_modules: + - virtio_pci + - virtio_blk +bootstrapper: + workspace: /target +system: + release: jessie + architecture: amd64 + bootloader: grub + charmap: UTF-8 + locale: en_US + timezone: UTC +volume: + backing: raw + partitions: + type: msdos + root: + filesystem: ext4 + size: 10GiB +packages: + install_standard: true + mirror: http://httpredir.debian.org/debian + install: + # required to be pre-installed for proper puppet functioning of + # puppetlabs-apt, it is also the primary puppet module + - lsb-release +plugins: + # It is advisable to avoid running things as root, use a sudo account instead + admin_user: + username: administrator + password: something + # puppet plugin + puppet: + # The assets path MUST be ABSOLUTE on your project. + assets: /your/absolute/path/to/etc/puppetlabs + install_modules: + - [puppetlabs-accounts] + - [puppetlabs-apt] + - [puppetlabs-concat, 3.0.0] + - [puppetlabs-stdlib] + - [puppetlabs-apache, 1.11.0] \ No newline at end of file