From 28579b3f41688594b60b28e2e5f6ea98f0fdc71d Mon Sep 17 00:00:00 2001 From: James Thomas Date: Tue, 3 Feb 2015 16:03:49 +0000 Subject: Add support for deploying upgrades from rawdisk images This patch allows morph to upgrade a system from a pre-deployed rawdisk image. For this to work in your cluster, use "image:/path/to/image.img" instead of "morph: systems/foo.morph" --- morphlib/plugins/deploy_plugin.py | 127 ++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 45 deletions(-) diff --git a/morphlib/plugins/deploy_plugin.py b/morphlib/plugins/deploy_plugin.py index 6d506a21..3c074f29 100644 --- a/morphlib/plugins/deploy_plugin.py +++ b/morphlib/plugins/deploy_plugin.py @@ -437,14 +437,29 @@ class DeployPlugin(cliapp.Plugin): any(sys_id in deployment_filter for sys_id in sys_ids): return old_status_prefix = self.app.status_prefix - system_status_prefix = '%s[%s]' % (old_status_prefix, system['morph']) + source_type = 'morph' + image_path = None + + if 'image' in system: + if self.app.settings['upgrade']: + source_type='rawdisk' + system_status_prefix = '%s[%s]' % (old_status_prefix, system['image']) + image_path = system['image'] + else: + raise cliapp.AppException( + 'image type can only be used for upgrades') + else: + system_status_prefix = '%s[%s]' % (old_status_prefix, system['morph']) + self.app.status_prefix = system_status_prefix try: - # Find the artifact to build - morph = morphlib.util.sanitise_morphology_path(system['morph']) - srcpool = build_command.create_source_pool(build_repo, ref, morph) + artifact = None + if source_type == 'morph': + # Find the artifact to build + morph = morphlib.util.sanitise_morphology_path(system['morph']) + srcpool = build_command.create_source_pool(build_repo, ref, morph) - artifact = build_command.resolve_artifacts(srcpool) + artifact = build_command.resolve_artifacts(srcpool) deploy_defaults = system.get('deploy-defaults', {}) for system_id, deploy_params in system['deploy'].iteritems(): @@ -486,7 +501,8 @@ class DeployPlugin(cliapp.Plugin): root_repo_dir, ref, artifact, deployment_type, - location, final_env) + location, final_env, + source_type, image_path) for subsystem in system.get('subsystems', []): self.deploy_system(build_command, deploy_tempdir, root_repo_dir, build_repo, @@ -500,7 +516,8 @@ class DeployPlugin(cliapp.Plugin): self.run_deploy_commands(deploy_tempdir, final_env, artifact, root_repo_dir, ref, deployment_type, - system_tree, deploy_location) + system_tree, deploy_location, + source_type) finally: self.app.status_prefix = system_status_prefix finally: @@ -537,7 +554,8 @@ class DeployPlugin(cliapp.Plugin): pass def setup_deploy(self, build_command, deploy_tempdir, root_repo_dir, ref, - artifact, deployment_type, location, env): + artifact, deployment_type, location, env, source_type, + image_path): # deployment_type, location and env are only used for saving metadata # Create a tempdir to extract the rootfs in @@ -547,55 +565,72 @@ class DeployPlugin(cliapp.Plugin): # Unpack the artifact (tarball) to a temporary directory. self.app.status(msg='Unpacking system for configuration') - if build_command.lac.has(artifact): - f = build_command.lac.get(artifact) - elif build_command.rac.has(artifact): - build_command.cache_artifacts_locally([artifact]) - f = build_command.lac.get(artifact) - else: - raise cliapp.AppException('Deployment failed as system is' - ' not yet built.\nPlease ensure' - ' the system is built before' - ' deployment.') - tf = tarfile.open(fileobj=f) - tf.extractall(path=system_tree) - - self.app.status( - msg='System unpacked at %(system_tree)s', - system_tree=system_tree) - - self.app.status( - msg='Writing deployment metadata file') - metadata = self.create_metadata( - artifact, root_repo_dir, deployment_type, location, env) - metadata_path = os.path.join( - system_tree, 'baserock', 'deployment.meta') - with morphlib.savefile.SaveFile(metadata_path, 'w') as f: - json.dump(metadata, f, indent=4, - sort_keys=True, encoding='unicode-escape') + if source_type == 'morph': + if build_command.lac.has(artifact): + f = build_command.lac.get(artifact) + elif build_command.rac.has(artifact): + build_command.cache_artifacts_locally([artifact]) + f = build_command.lac.get(artifact) + else: + raise cliapp.AppException('Deployment failed as system is' + ' not yet built.\nPlease ensure' + ' the system is built before' + ' deployment.') + tf = tarfile.open(fileobj=f) + tf.extractall(path=system_tree) + self.app.status( + msg='System unpacked at %(system_tree)s', + system_tree=system_tree) + + self.app.status( + msg='Writing deployment metadata file') + metadata = self.create_metadata( + artifact, root_repo_dir, deployment_type, location, env) + metadata_path = os.path.join( + system_tree, 'baserock', 'deployment.meta') + with morphlib.savefile.SaveFile(metadata_path, 'w') as f: + json.dump(metadata, f, indent=4, + sort_keys=True, encoding='unicode-escape') + elif source_type == 'rawdisk': + cliapp.runcmd(['mount', '-t', 'btrfs', '-o', + 'ro,subvol=/systems/default/run', image_path, + system_tree]) + # Now that we've mounted the image, actually check to see if + # it's a baserock system + baserock_path = os.path.join(system_tree, 'baserock') + output = cliapp.runcmd(['sh', '-c', + 'test -d '+ baserock_path +' || echo -n dirnotfound']) + if output == 'dirnotfound': + raise cliapp.AppException('%s is not a baserock system!' + % image_path) + return system_tree except Exception: + if source_type == 'rawdisk': + cliapp.runcmd(['umount', system_tree]) shutil.rmtree(system_tree) raise def run_deploy_commands(self, deploy_tempdir, env, artifact, root_repo_dir, - ref, deployment_type, system_tree, location): + ref, deployment_type, system_tree, location, + source_type): # Extensions get a private tempdir so we can more easily clean # up any files an extension left behind deploy_private_tempdir = tempfile.mkdtemp(dir=deploy_tempdir) env['TMPDIR'] = deploy_private_tempdir try: - # Run configuration extensions. - self.app.status(msg='Configure system') - names = artifact.source.morphology['configuration-extensions'] - for name in names: - self._run_extension( - root_repo_dir, - name, - '.configure', - [system_tree], - env) + if source_type == 'morph': + # Run configuration extensions. + self.app.status(msg='Configure system') + names = artifact.source.morphology['configuration-extensions'] + for name in names: + self._run_extension( + root_repo_dir, + name, + '.configure', + [system_tree], + env) # Run write extension. self.app.status(msg='Writing to device') @@ -609,6 +644,8 @@ class DeployPlugin(cliapp.Plugin): finally: # Cleanup. self.app.status(msg='Cleaning up') + if source_type == 'rawdisk': + cliapp.runcmd(['umount', system_tree]) shutil.rmtree(deploy_private_tempdir) def _report_extension_stdout(self, line): -- cgit v1.2.1