summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--morphlib/gitdir.py7
-rw-r--r--morphlib/gitdir_tests.py11
-rw-r--r--morphlib/plugins/deploy_plugin.py54
-rwxr-xr-xtests.deploy/deploy-cluster.script17
4 files changed, 86 insertions, 3 deletions
diff --git a/morphlib/gitdir.py b/morphlib/gitdir.py
index be2137b2..f5ef0061 100644
--- a/morphlib/gitdir.py
+++ b/morphlib/gitdir.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 Codethink Limited
+# Copyright (C) 2013-2014 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -610,6 +610,11 @@ class GitDirectory(object):
except Exception, e:
raise RefDeleteError(self, ref, old_sha1, e)
+ def describe(self):
+ version = self._runcmd(
+ ['git', 'describe', '--always', '--dirty=-unreproducible'])
+ return version.strip()
+
def init(dirname):
'''Initialise a new git repository.'''
diff --git a/morphlib/gitdir_tests.py b/morphlib/gitdir_tests.py
index 21a6b5b8..8c312c1b 100644
--- a/morphlib/gitdir_tests.py
+++ b/morphlib/gitdir_tests.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 Codethink Limited
+# Copyright (C) 2013-2014 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -202,6 +202,15 @@ class GitDirectoryContentsTests(unittest.TestCase):
)
self.assertEqual(expected, gd.get_commit_contents(commit).split('\n'))
+ def test_describe(self):
+ gd = morphlib.gitdir.GitDirectory(self.dirname)
+
+ gd._runcmd(['git', 'tag', '-a', '-m', 'Example', 'example', 'HEAD'])
+ self.assertEqual(gd.describe(), 'example-unreproducible')
+
+ gd._runcmd(['git', 'reset', '--hard'])
+ self.assertEqual(gd.describe(), 'example')
+
class GitDirectoryRefTwiddlingTests(unittest.TestCase):
diff --git a/morphlib/plugins/deploy_plugin.py b/morphlib/plugins/deploy_plugin.py
index c8538a8d..61e1b5a4 100644
--- a/morphlib/plugins/deploy_plugin.py
+++ b/morphlib/plugins/deploy_plugin.py
@@ -16,6 +16,7 @@
import cliapp
import contextlib
+import json
import os
import shutil
import stat
@@ -261,6 +262,13 @@ class DeployPlugin(cliapp.Plugin):
are set as environment variables when either the configuration or the
write extension runs (except `type` and `location`).
+ Deployment configuration is stored in the deployed system as
+ /baserock/deployment.meta. THIS CONTAINS ALL ENVIRONMENT VARIABLES SET
+ DURINGR DEPLOYMENT, so make sure you have no sensitive information in
+ your environment that is being leaked. As a special case, any
+ environment/deployment variable that contains 'PASSWORD' in its name is
+ stripped out and not stored in the final system.
+
'''
if not args:
@@ -419,6 +427,14 @@ class DeployPlugin(cliapp.Plugin):
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:
+ f.write(json.dumps(metadata, indent=4, sort_keys=True))
# Run configuration extensions.
self.app.status(msg='Configure system')
@@ -496,3 +512,41 @@ class DeployPlugin(cliapp.Plugin):
st = os.stat(filename)
mask = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
return (stat.S_IMODE(st.st_mode) & mask) != 0
+
+ def create_metadata(self, system_artifact, root_repo_dir, deployment_type,
+ location, env):
+ '''Deployment-specific metadata.
+
+ The `build` and `deploy` operations must be from the same ref, so full
+ info on the root repo that the system came from is in
+ /baserock/${system_artifact}.meta and is not duplicated here. We do
+ store a `git describe` of the definitions.git repo as a convenience for
+ post-upgrade hooks that we may need to implement at a future date:
+ the `git describe` output lists the last tag, which will hopefully help
+ us to identify which release of a system was deployed without having to
+ keep a list of SHA1s somewhere or query a Trove.
+
+ '''
+
+ def remove_passwords(env):
+ def is_password(key):
+ return 'PASSWORD' in key
+ return { k:v for k, v in env.iteritems() if not is_password(k) }
+
+ meta = {
+ 'system-artifact-name': system_artifact.name,
+ 'configuration': remove_passwords(env),
+ 'deployment-type': deployment_type,
+ 'location': location,
+ 'definitions-version': {
+ 'describe': root_repo_dir.describe(),
+ },
+ 'morph-version': {
+ 'ref': morphlib.gitversion.ref,
+ 'tree': morphlib.gitversion.tree,
+ 'commit': morphlib.gitversion.commit,
+ 'version': morphlib.gitversion.version,
+ },
+ }
+
+ return meta
diff --git a/tests.deploy/deploy-cluster.script b/tests.deploy/deploy-cluster.script
index 0efc8d3c..3ef60479 100755
--- a/tests.deploy/deploy-cluster.script
+++ b/tests.deploy/deploy-cluster.script
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright (C) 2013 Codethink Limited
+# Copyright (C) 2013-2014 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -29,8 +29,11 @@ cd "$DATADIR/workspace/branch1"
"$SRCDIR/scripts/test-morph" build linux-system
+GIT_DIR=test:morphs/.git git tag -a my-test-tag -m "Example tag" HEAD
+
"$SRCDIR/scripts/test-morph" --log "$DATADIR/deploy.log" \
deploy test_cluster \
+ linux-system-2.EXAMPLE_PASSWORD="secret" \
linux-system-2.HOSTNAME="baserock-rocks-even-more" \
> /dev/null
@@ -44,3 +47,15 @@ hostname2=$(tar -xf $outputdir/linux-system-2.tar ./etc/hostname -O)
[ "$hostname1" = baserock-rocks ]
[ "$hostname2" = baserock-rocks-even-more ]
+
+tar -xf $outputdir/linux-system-2.tar ./baserock/deployment.meta
+metadata=baserock/deployment.meta
+
+# Check that 'git describe' of definitions repo was stored correctly
+echo -n "definitions-version: "
+"$SRCDIR/scripts/yaml-extract" $metadata definitions-version
+
+echo -n "configuration.HOSTNAME: "
+"$SRCDIR/scripts/yaml-extract" $metadata configuration HOSTNAME
+
+! (grep -q "EXAMPLE_PASSWORD" $metadata)