Merge lp:~chris-gondolin/charms/trusty/storage/trunk into lp:charms/storage

Proposed by Chris Stratford
Status: Merged
Merged at revision: 39
Proposed branch: lp:~chris-gondolin/charms/trusty/storage/trunk
Merge into: lp:charms/storage
Diff against target: 988 lines (+732/-40)
9 files modified
config.yaml (+23/-8)
files/crypt_mount.sh (+19/-0)
hooks/common_util.py (+72/-20)
hooks/crypt_fs.py (+164/-0)
hooks/storage-provider.d/partition/data-relation-changed (+31/-0)
hooks/test_common_util.py (+23/-12)
hooks/test_crypt_fs.py (+248/-0)
tests/10-crypt-keep-keyfile (+79/-0)
tests/20-crypt-lose-keyfile (+73/-0)
To merge this branch: bzr merge lp:~chris-gondolin/charms/trusty/storage/trunk
Reviewer Review Type Date Requested Status
Matt Bruzek (community) Approve
Cory Johns (community) Needs Fixing
Review via email: mp+234452@code.launchpad.net

Description of the change

Added encrypted filesystem support for block devices.
Introduces two new config variables:

encryption_key_map = "{postgres/0: password, postgres/2: password2}"
store_encryption_keys = yes

If store_encryption_keys is "yes" the passwords will be stored in /root/keyfile--dev-vdc (or vdd, vde, etc. whatever the device name is) and an entry written to /etc/crypttab to allow auto-mounting on reboot.

If store_encryption_keys is anything else, the keyfile will be written temporarily to allow cryptsetup to do its work, then removed, any entries for the device in /etc/crypttab will be removed if present and "defaults,noauto" set in fstab to ensure prevent auto-mounting on reboot (which would fail, waiting for a passphrase).

To post a comment you must log in.
Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

Hey Chris, nice addition to the storage charm, thanks!

My only quibble with this proposal is that there are no new tests to cover the new functionality. Before giving this a +1 I would like to see some tests to cover the new encryption stuff - maybe some unit test coverage of the crypt_fs module, and a new amulet test to test a deployment scenario.

41. By Chris Stratford

[chriss] Added a partition provider to cope with physical disk partitions (on MaaS units for example). Added some amulet tests that should cover both the new provider and encryption.

42. By Chris Stratford

[chriss] A bit more logging

43. By Chris Stratford

[chriss] Attempt to get juju test to log useful stuff

44. By Chris Stratford

[chriss] Removed spin_up_delay, as it did not do what I had hoped. Tests *should* work once bug #1378309 is fixed

Revision history for this message
Cory Johns (johnsca) wrote :

Chris,

Thanks for adding the tests for this functionality. The new tests look good, but unfortunately I had some issues running them.

First, I'd just like to point out that Amulet has been updated to fix the issues you are working around and mention in your tests, specifically the issues with the series and local charm not being used properly, and adding relations after deployment not working correctly. So, you can change your d.add('postgresql', 'cs:trusty/postgresql-5') and d.add('storage', '/home/chris/work/charms/trusty/storage') to just d.add('postgresql') and d.add('storage'). Regarding the post-deployment relations, the only thing that you should need to change is to add d.sentry.wait() after adding the relation, to ensure that the system has time to process the change.

That said, with those changes I am still getting failures (http://pastebin.ubuntu.com/8838368/), as well as hook failures (http://pastebin.ubuntu.com/8838352/). This happened on both lxc and aws, on newly bootstrapped environments.

Additionally, `make lint` had some complaints about the new code, just some small quibbles about line length and spacing: http://pastebin.ubuntu.com/8837062/

Finally, some of the existing tests in `make test` are having issues with the new code. It mostly seems like they need some additional mocks (http://pastebin.ubuntu.com/8837040/) or new data values (http://pastebin.ubuntu.com/8837008/), but one in particular seems like it might be an issue in the new code: http://pastebin.ubuntu.com/8837016/

review: Needs Fixing
45. By Chris Stratford

[chriss] PEP8 fixes. Amulet test fixes now amulet itself is fixed

Revision history for this message
Chris Stratford (chris-gondolin) wrote :

The Amulet tests should now work (d.sentry.wait() after adding the relation doesn't appear to be enough, so there's a sleep in there too now, which should fix the errors you saw)

make lint should also be happy now.

I've got to do some reading up on mocker before fixing the make test problems.

46. By Chris Stratford

[chriss] Unit test fixes

Revision history for this message
Chris Stratford (chris-gondolin) wrote :

Ok, the unit tests now pass (even the ones that weren't a result of my changes :-)

There still aren't any for the crypt_fs functions or partition storage type additions (yet).

47. By Chris Stratford

[chriss] Add unit tests for crypt_fs. Minor fixes.

Revision history for this message
Matt Bruzek (mbruzek) wrote :

+1 LGTM

review: Approve
Revision history for this message
Adam Collard (adam-collard) wrote :

This broke the charm :(

http://paste.ubuntu.com/9458517/

There is nothing in the charm that attempts to install cryptsetup-bin package (which installs the cryptsetup script that this MP relies on).

Revision history for this message
Chris Stratford (chris-gondolin) wrote :

Adam, I'm curious to know what system you installed it on to get that error, as it looks like cryptsetup-bin is standard on all recent Ubuntu installs.

I'll fix the bug, but it would be good to know for future reference.

Revision history for this message
David Britton (dpb) wrote :

Hi @Chris --

Adam tested on openstack with official cloud images. Matt tested on lxc. Could be a difference between the images there. :(

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'config.yaml'
--- config.yaml 2014-02-10 21:51:04 +0000
+++ config.yaml 2014-11-07 12:56:29 +0000
@@ -8,21 +8,36 @@
8 default: local8 default: local
9 description: |9 description: |
10 The backend storage provider. Will be mounted at root, and10 The backend storage provider. Will be mounted at root, and
11 can be one of, local, block-storage-broker or nfs. If you set to11 can be one of, local, block-storage-broker, nfs or partition.
12 block-storage-broker, you should provide a suitable value for12 If you set to block-storage-broker, you should provide a suitable
13 volume_size.13 value for volume_size.
14 volume_map:14 volume_map:
15 type: string15 type: string
16 default: ""16 default: ""
17 description: |17 description: |
18 YAML map as e.g. "{postgres/0: vol-0000010, postgres/1: vol-0000016}".18 YAML map as e.g. "{postgres/0: vol-0000010, postgres/1: vol-0000016}".
19 A service unit to specific block storage volume-id that should be19 A service unit to specific volume-id that should be attached to each
20 attached to each unit. This requires that provider is set to20 unit. If the provider is set to block-storage-broker, volume-ids
21 block-storage-broker and that volume-ids specified match a listing21 specified should match a listing from nova volume-list.
22 from nova volume-list. If a related unit does not have a volume-id22 If a related unit does not have a volume-id specified, a new volume
23 specified, a new volume of volume_size will be created for that unit.23 of volume_size will be created for that unit.
24 If the provider is set to partition, volume-ids should be a disk
25 partition name (e.g. /dev/sdb1)
24 volume_size:26 volume_size:
25 type: int27 type: int
26 description: |28 description: |
27 The volume size in GB to request from the block-storage-broker.29 The volume size in GB to request from the block-storage-broker.
28 default: 530 default: 5
31 encryption_key_map:
32 type: string
33 default: ""
34 description: |
35 YAML map as e.g. "{postgres/0: password, postgres/1: password2}"
36 If set, the unit's volume will be encrypted with the matching key
37 (unless the volume is already formatted)
38 store_encryption_keys:
39 type: string
40 default: "yes"
41 description: |
42 If the volume is encrypted, this will write the details to
43 disk to allow auto-mounting.
2944
=== added directory 'files'
=== added file 'files/crypt_mount.sh'
--- files/crypt_mount.sh 1970-01-01 00:00:00 +0000
+++ files/crypt_mount.sh 2014-11-07 12:56:29 +0000
@@ -0,0 +1,19 @@
1#!/bin/bash
2#
3# Simple scrip to assist with mounting a crypted filesystem
4# Assumes an fstab entry already exists
5
6scriptname=$(basename $0)
7dev=$1
8
9set -eu
10
11if [ "${dev}" = "" ]; then
12 echo "Usage: ${scriptname} <device>"
13 echo "e.g. ${scriptname} vdc"
14 exit 1
15fi
16
17cryptsetup open --type luks /dev/${dev} ${dev}
18mount /dev/mapper/${dev}
19exit 0
020
=== modified file 'hooks/common_util.py'
--- hooks/common_util.py 2014-08-27 11:01:14 +0000
+++ hooks/common_util.py 2014-11-07 12:56:29 +0000
@@ -6,6 +6,8 @@
6from time import sleep6from time import sleep
7from stat import S_ISBLK, ST_MODE7from stat import S_ISBLK, ST_MODE
8import json8import json
9import crypt_fs
10
911
10FSTAB_FILE = "/etc/fstab"12FSTAB_FILE = "/etc/fstab"
11EC2_METADATA_URL = "http://169.254.169.254/latest/meta-data"13EC2_METADATA_URL = "http://169.254.169.254/latest/meta-data"
@@ -167,38 +169,81 @@
167169
168def _read_partition_table(device_path):170def _read_partition_table(device_path):
169 """Call blockdev to read a valid partition table and return exit code"""171 """Call blockdev to read a valid partition table and return exit code"""
172 # blockdev doesn't like loopback, so we lie
173 if device_path.startswith("/dev/loop"):
174 return 0
170 return subprocess.call(175 return subprocess.call(
171 "blockdev --rereadpt %s" % device_path, shell=True)176 "blockdev --rereadpt %s" % device_path, shell=True)
172177
173178
174def _assert_fstype(device_path, fstype):179def _assert_fstype(device_path, fstype):
175 """Return C{True} if a filesystem is of type C{fstype}"""180 """Return C{True} if a filesystem is of type C{fstype}"""
176 command = "file -s %s | egrep -q %s" % (device_path, fstype)181 # /dev/mapper/blah is usually a symlink
182 real_device_path = os.path.realpath(device_path)
183 command = "file -s %s | egrep -q %s" % (real_device_path, fstype)
177 return subprocess.call(command, shell=True) == 0184 return subprocess.call(command, shell=True) == 0
178185
179186
187def _get_mkfs_command(device_path, encryption):
188 mkfs_command = None
189 encrypted_dev_path = None
190 if encryption:
191 if crypt_fs.is_encrypted(device_path):
192 encrypted_dev_path = crypt_fs.get_encrypted_dev_path(device_path)
193 log("Found an encrypted device {}".format(encrypted_dev_path))
194 else:
195 encrypted_dev_path = crypt_fs.encrypt_device(device_path)
196 mkfs_command = ["mkfs.ext4", encrypted_dev_path]
197 crypt_fs.write_encryption_details(device_path)
198
199 if _assert_fstype(encrypted_dev_path, "ext4"):
200 log("{} already encrypted and formatted - "
201 "skipping mkfs.ext4.".format(device_path))
202 mkfs_command = None
203 else:
204 mkfs_command = ["mkfs.ext4", device_path]
205 return mkfs_command, encrypted_dev_path
206
207
180def _format_device(device_path):208def _format_device(device_path):
181 """209 """
182 Create an ext4 partition, if needed, for C{device_path} and fsck that210 Create an ext4 partition, if needed, for C{device_path} and fsck that
183 partition.211 partition.
184 """212 """
213 encryption = True if crypt_fs.get_encryption_key() else False
214 command = []
215
185 result = _read_partition_table(device_path)216 result = _read_partition_table(device_path)
186 if result == 1:217 if result == 1:
187 log("INFO: %s is busy, no fsck performed. Assuming formatted." %218 log("INFO: %s is busy, no fsck performed. Assuming formatted." %
188 device_path)219 device_path)
220 encryption = False
189 elif result == 0:221 elif result == 0:
190 # Create an ext4 filesystem if NOT already present222 # Create an ext4 filesystem if NOT already present
191 # use e.g. LABEl=vol-000012345223 # use e.g. LABEl=vol-000012345
192 if _assert_fstype(device_path, "ext4"):224 if _assert_fstype(device_path, "ext4"):
193 log("%s already formatted - skipping mkfs.ext4." % device_path)225 log("%s already formatted - skipping mkfs.ext4." % device_path)
194 else:226 encryption = False
195 command = "mkfs.ext4 %s" % device_path227 else:
196 log("NOTICE: Running: %s" % command)228 mkfs_command, encrypted_dev_path = _get_mkfs_command(device_path,
197 subprocess.check_call(command, shell=True)229 encryption)
198 if subprocess.call("fsck -p %s" % device_path, shell=True) != 0:230 if mkfs_command:
231 log("NOTICE: Running: %s" % " ".join(mkfs_command))
232 subprocess.check_call(mkfs_command, shell=False)
233
234 if encryption:
235 command = ["fsck", "-p", encrypted_dev_path]
236 else:
237 command = ["fsck", "-p", device_path]
238 if subprocess.call(command, shell=False) != 0:
199 log("ERROR: fsck -p %s failed" % device_path)239 log("ERROR: fsck -p %s failed" % device_path)
200 sys.exit(1)240 sys.exit(1)
201241
242 if encryption:
243 return encrypted_dev_path
244 else:
245 return device_path
246
202247
203def _get_from_relation(relation, key, default=None):248def _get_from_relation(relation, key, default=None):
204 relids = hookenv.relation_ids(relation)249 relids = hookenv.relation_ids(relation)
@@ -213,8 +258,8 @@
213258
214def mount_volume(device_path=None):259def mount_volume(device_path=None):
215 """260 """
216 Mount the attached volume, nfs or block-storage-broker type, to the261 Mount the attached volume, partition, nfs or block-storage-broker
217 principal requested C{mountpoint}.262 type, to the principal requested C{mountpoint}.
218263
219 If the C{mountpoint} required relation data does not exist, log the264 If the C{mountpoint} required relation data does not exist, log the
220 issue and exit. Otherwise attempt to initialize and mount the blockstorage265 issue and exit. Otherwise attempt to initialize and mount the blockstorage
@@ -255,13 +300,16 @@
255 "nfs-relation-changed hook needs to run first.")300 "nfs-relation-changed hook needs to run first.")
256 return301 return
257302
258 device_path = "%s:%s" % (nfs_server, nfs_path)303 new_device_path = "%s:%s" % (nfs_server, nfs_path)
259 elif provider == "block-storage-broker":304 elif provider in ("block-storage-broker", "partition"):
260 if device_path is None:305 # Specific to block-storage
261 log("Waiting for persistent nova device provided by "306 if provider == "block-storage-broker":
262 "block-storage-broker.")307 if device_path is None:
263 sys.exit(0)308 log("Waiting for persistent nova device provided by "
309 "block-storage-broker.")
310 sys.exit(0)
264311
312 # Common to any partition-like device
265 _assert_block_device(device_path)313 _assert_block_device(device_path)
266 if os.path.exists("%s1" % device_path):314 if os.path.exists("%s1" % device_path):
267 # Then we are supporting upgrade-charm path for deprecated315 # Then we are supporting upgrade-charm path for deprecated
@@ -271,10 +319,14 @@
271 device_path = "%s1" % device_path319 device_path = "%s1" % device_path
272 log("Mounting %s as the device path as the root device is already "320 log("Mounting %s as the device path as the root device is already "
273 "partitioned." % device_path)321 "partitioned." % device_path)
274 _format_device(device_path)322 # _format_device may create a new device_path if it's encrypted
275 _set_label(device_path, mountpoint)323 new_device_path = _format_device(device_path)
324 _set_label(new_device_path, mountpoint)
276 fstype = subprocess.check_output(325 fstype = subprocess.check_output(
277 "blkid -o value -s TYPE %s" % device_path, shell=True).strip()326 "blkid -o value -s TYPE %s" % new_device_path, shell=True).strip()
327 if crypt_fs.is_encrypted(device_path):
328 if hookenv.config("store_encryption_keys") != "yes":
329 options = "defaults,noauto"
278 else:330 else:
279 log("ERROR: Unknown provider %s. Cannot mount volume." % hookenv.ERROR)331 log("ERROR: Unknown provider %s. Cannot mount volume." % hookenv.ERROR)
280 sys.exit(1)332 sys.exit(1)
@@ -284,12 +336,12 @@
284336
285 options_string = "" if options == "defaults" else " -o %s" % options337 options_string = "" if options == "defaults" else " -o %s" % options
286 command = "mount -t %s%s %s %s" % (338 command = "mount -t %s%s %s %s" % (
287 fstype, options_string, device_path, mountpoint)339 fstype, options_string, new_device_path, mountpoint)
288 subprocess.check_call(command, shell=True)340 subprocess.check_call(command, shell=True)
289 log("Mount (%s) successful" % mountpoint)341 log("Mount (%s) successful" % mountpoint)
290 with open(FSTAB_FILE, "a") as output_file:342 with open(FSTAB_FILE, "a") as output_file:
291 output_file.write(343 output_file.write("{} {} {} {} 0 0\n".format(
292 "%s %s %s %s 0 0\n" % (device_path, mountpoint, fstype, options))344 new_device_path, mountpoint, fstype, options))
293 # publish changes to data relation and exit345 # publish changes to data relation and exit
294 for relid in hookenv.relation_ids("data"):346 for relid in hookenv.relation_ids("data"):
295 hookenv.relation_set(347 hookenv.relation_set(
296348
=== added file 'hooks/crypt_fs.py'
--- hooks/crypt_fs.py 1970-01-01 00:00:00 +0000
+++ hooks/crypt_fs.py 2014-11-07 12:56:29 +0000
@@ -0,0 +1,164 @@
1"""Common python utilities for storage providers"""
2from charmhelpers.core import hookenv
3import os
4import subprocess
5import sys
6import yaml
7from yaml.constructor import ConstructorError
8import shutil
9
10
11CRYPTTAB_FILE = "/etc/crypttab"
12KEYFILE_DIR = "/root"
13CRYPT_MOUNT_SCRIPT = "/usr/local/sbin/crypt_mount.sh"
14
15
16def log(message, level=None):
17 """Quaint little log wrapper for juju logging"""
18 hookenv.log(message, level)
19
20
21# Format a Luks device
22def encrypt_device(device_path):
23 keyfile = _write_encryption_keyfile(device_path)
24 # Create the encrypted volume
25 command = ["cryptsetup",
26 "--key-file", keyfile,
27 "--batch-mode",
28 "luksFormat", device_path]
29 log("NOTICE: Running: {}".format(command))
30 if subprocess.call(command, shell=False) != 0:
31 log("ERROR: {} failed".format(command))
32 sys.exit(1)
33 return get_encrypted_dev_path(device_path)
34
35
36# Return the Luks device path if it exists
37# Perform a LuksOpen if it doesn't
38def get_encrypted_dev_path(device_path):
39 keyfile = _write_encryption_keyfile(device_path)
40 newname = "/dev/mapper/{}".format(os.path.basename(device_path))
41 if os.path.exists(newname):
42 return newname
43 else:
44 basedev = os.path.basename(device_path)
45 command = ["cryptsetup",
46 "--key-file", keyfile,
47 "open",
48 "--type", "luks",
49 device_path,
50 basedev]
51 log("NOTICE: Running: {}".format(command))
52 if subprocess.call(command, shell=False) != 0:
53 log("ERROR: {} failed".format(command))
54 sys.exit(1)
55 return newname
56
57
58def is_encrypted(device_path):
59 command = ["cryptsetup", "isLuks", device_path]
60 if subprocess.call(command, shell=False) == 0:
61 return True
62 else:
63 return False
64
65
66def _get_encryption_uuid(device_path):
67 command = ["cryptsetup", "luksUUID", device_path]
68 try:
69 uuid = subprocess.check_output(command, shell=False).rstrip()
70 return uuid
71 except subprocess.CalledProcessError:
72 log("ERROR: {} failed".format(command))
73 return None
74
75
76def get_encryption_key():
77 # Get first unit of first data relation
78 # (hopefully only unit of only data relation)
79 relid = hookenv.relation_ids("data")[0]
80 unit = hookenv.related_units(relid)[0]
81 log("get_encryption_key() unit: {}".format(unit))
82 encryption_key_map = hookenv.config("encryption_key_map")
83 encryption_key = None
84 if encryption_key_map and unit:
85 log("get_encryption_key() Have map and unit")
86 try:
87 encryption_key_map = yaml.load(encryption_key_map)
88 if encryption_key_map:
89 encryption_key = encryption_key_map.get(unit, None)
90 except ConstructorError as e:
91 log("Invalid YAML in 'encryption_key_map': {}".format(e),
92 hookenv.WARNING)
93 return None
94 if not encryption_key:
95 log("get_encryption_key() Failed to find key")
96 return encryption_key
97
98
99def _get_encryption_keyfile_name(device_path):
100 return "{}/keyfile-{}".format(KEYFILE_DIR, device_path.replace("/", "-"))
101
102
103# Store the encryption key, as it's needed by the Luks commands
104# We will delete it later if the store_encryption_keys config variable
105# is not "yes"
106def _write_encryption_keyfile(device_path):
107 encryption_key = get_encryption_key()
108 keyfile = _get_encryption_keyfile_name(device_path)
109 # Write out the keyfile
110 log("Writing keyfile: {}".format(keyfile))
111 with open(keyfile, "w") as f:
112 os.chmod(keyfile, 0600)
113 f.write(encryption_key)
114 return keyfile
115
116
117def _delete_encryption_keyfile(device_path):
118 keyfile = _get_encryption_keyfile_name(device_path)
119 if os.path.isfile(keyfile):
120 log("Deleting keyfile: {}".format(keyfile))
121 os.unlink(keyfile)
122
123
124# Write the encryption details to the FS to allow auto-mounting
125# If store_encryption_keys config option is not "yes", this will
126# remove the current device_path from the file
127def write_encryption_details(device_path):
128 keyfile = _write_encryption_keyfile(device_path)
129 basedev = os.path.basename(device_path)
130 uuid = _get_encryption_uuid(device_path)
131 newCrypttabFile = CRYPTTAB_FILE + ".new"
132 if uuid is not None:
133 log("Writing encryption details to {}".format(CRYPTTAB_FILE))
134 # Read original
135 lines = []
136 if os.path.exists(CRYPTTAB_FILE):
137 with open(CRYPTTAB_FILE, "r") as fr:
138 lines = fr.readlines()
139
140 # Write new (removing any existing lines for our device)
141 with open(newCrypttabFile, "w") as fw:
142 for line in lines:
143 if not line.startswith((basedev + " ", basedev + "\t")):
144 fw.write(line)
145 if hookenv.config("store_encryption_keys") == "yes":
146 fw.write("{} UUID={} {} luks\n".format(basedev, uuid, keyfile))
147
148 # Move into place
149 if os.path.exists(CRYPTTAB_FILE):
150 os.rename(CRYPTTAB_FILE, CRYPTTAB_FILE + ".orig")
151 os.rename(newCrypttabFile, CRYPTTAB_FILE)
152
153 # Remove the keyfile if we don't want it saved
154 if hookenv.config("store_encryption_keys") != "yes":
155 _delete_encryption_keyfile(device_path)
156 else:
157 log("Unable to write encryption details to".format(CRYPTTAB_FILE))
158
159 # Add a simple script to help mount the crypted filesystem
160 # (only needed is not auto-mounting)
161 src = os.path.join(hookenv.charm_dir(), "files", "crypt_mount.sh")
162 dest = "/usr/local/sbin/crypt_mount.sh"
163 shutil.copyfile(src, dest)
164 os.chmod(dest, 0755)
0165
=== added directory 'hooks/storage-provider.d/partition'
=== added file 'hooks/storage-provider.d/partition/data-relation-changed'
--- hooks/storage-provider.d/partition/data-relation-changed 1970-01-01 00:00:00 +0000
+++ hooks/storage-provider.d/partition/data-relation-changed 2014-11-07 12:56:29 +0000
@@ -0,0 +1,31 @@
1#!/usr/bin/python
2
3import sys
4import yaml
5from yaml.constructor import ConstructorError
6
7from charmhelpers.core import hookenv
8import common_util
9
10log = hookenv.log
11
12partition = None
13volume_map = hookenv.config().get("volume_map", None)
14relids = hookenv.relation_ids("data")
15for relid in relids:
16 for unit in hookenv.related_units(relid):
17 if volume_map is not None:
18 try:
19 volume_map = yaml.load(volume_map)
20 if volume_map:
21 partition = volume_map.get(unit, None)
22 except ConstructorError as e:
23 hookenv.log(
24 "invalid YAML in 'volume-map': {}".format(e),
25 hookenv.WARNING)
26
27if partition is None:
28 log("No data-relation-changed hook fired. Awaiting data-relation")
29 sys.exit(0)
30
31common_util.mount_volume(partition)
032
=== modified file 'hooks/test_common_util.py'
--- hooks/test_common_util.py 2014-03-21 22:53:22 +0000
+++ hooks/test_common_util.py 2014-11-07 12:56:29 +0000
@@ -1,4 +1,5 @@
1import common_util as util1import common_util as util
2import crypt_fs
2import json3import json
3import mocker4import mocker
4import os5import os
@@ -13,6 +14,7 @@
13 self.maxDiff = None14 self.maxDiff = None
14 util.FSTAB_FILE = self.makeFile()15 util.FSTAB_FILE = self.makeFile()
15 util.hookenv = TestHookenv({"provider": "nfs"})16 util.hookenv = TestHookenv({"provider": "nfs"})
17 crypt_fs.hookenv = TestHookenv()
16 self.addCleanup(setattr, os, "environ", os.environ)18 self.addCleanup(setattr, os, "environ", os.environ)
17 self.charm_dir = self.makeDir()19 self.charm_dir = self.makeDir()
18 os.environ = {"CHARM_DIR": self.charm_dir}20 os.environ = {"CHARM_DIR": self.charm_dir}
@@ -537,7 +539,7 @@
537539
538 # Assert fsck is called540 # Assert fsck is called
539 fsck = self.mocker.replace(subprocess.call)541 fsck = self.mocker.replace(subprocess.call)
540 fsck("fsck -p /dev/vdz", shell=True)542 fsck(["fsck", "-p", "/dev/vdz"], shell=False)
541 self.mocker.result(0) # successful fsck command exit543 self.mocker.result(0) # successful fsck command exit
542 self.mocker.replay()544 self.mocker.replay()
543545
@@ -561,9 +563,9 @@
561 ext4_check("/dev/vdz", "ext4")563 ext4_check("/dev/vdz", "ext4")
562 self.mocker.result(False)564 self.mocker.result(False)
563 fsck = self.mocker.replace(subprocess.check_call)565 fsck = self.mocker.replace(subprocess.check_call)
564 fsck("mkfs.ext4 /dev/vdz", shell=True)566 fsck(["mkfs.ext4", "/dev/vdz"], shell=False)
565 fsck = self.mocker.replace(subprocess.call)567 fsck = self.mocker.replace(subprocess.call)
566 fsck("fsck -p /dev/vdz", shell=True)568 fsck(["fsck", "-p", "/dev/vdz"], shell=False)
567 self.mocker.result(0) # Sucessful command exit 0569 self.mocker.result(0) # Sucessful command exit 0
568 self.mocker.replay()570 self.mocker.replay()
569571
@@ -586,9 +588,9 @@
586 ext4_check("/dev/vdz", "ext4")588 ext4_check("/dev/vdz", "ext4")
587 self.mocker.result(False)589 self.mocker.result(False)
588 fsck = self.mocker.replace(subprocess.check_call)590 fsck = self.mocker.replace(subprocess.check_call)
589 fsck("mkfs.ext4 /dev/vdz", shell=True)591 fsck(["mkfs.ext4", "/dev/vdz"], shell=False)
590 fsck = self.mocker.replace(subprocess.call)592 fsck = self.mocker.replace(subprocess.call)
591 fsck("fsck -p /dev/vdz", shell=True)593 fsck(["fsck", "-p", "/dev/vdz"], shell=False)
592 self.mocker.result(1)594 self.mocker.result(1)
593 self.mocker.replay()595 self.mocker.replay()
594596
@@ -733,7 +735,7 @@
733 util.hookenv.is_logged(message), "Not logged- %s" % message)735 util.hookenv.is_logged(message), "Not logged- %s" % message)
734736
735 # Wrote proper fstab mount info to mount on reboot737 # Wrote proper fstab mount info to mount on reboot
736 fstab_content = "me.com:/nfs/server/path /mnt/this nfs someopts 0 0"738 fstab_content = "me.com:/nfs/server/path /mnt/this nfs someopts 0 0\n"
737 with open(util.FSTAB_FILE) as input_file:739 with open(util.FSTAB_FILE) as input_file:
738 self.assertEqual(input_file.read(), fstab_content)740 self.assertEqual(input_file.read(), fstab_content)
739741
@@ -789,9 +791,9 @@
789 exists = self.mocker.replace(os.path.exists)791 exists = self.mocker.replace(os.path.exists)
790 exists("/dev/vdx1")792 exists("/dev/vdx1")
791 self.mocker.result(False)793 self.mocker.result(False)
792 create_partition = self.mocker.replace(794 create_partition = self.mocker.replace(util._format_device)
793 util._format_device)
794 create_partition("/dev/vdx")795 create_partition("/dev/vdx")
796 self.mocker.result("/dev/vdx")
795797
796 _set_label = self.mocker.replace(util._set_label)798 _set_label = self.mocker.replace(util._set_label)
797 _set_label("/dev/vdx", "/mnt/this")799 _set_label("/dev/vdx", "/mnt/this")
@@ -806,6 +808,11 @@
806808
807 mount = self.mocker.replace(subprocess.check_call)809 mount = self.mocker.replace(subprocess.check_call)
808 mount("mount -t ext4 /dev/vdx /mnt/this", shell=True)810 mount("mount -t ext4 /dev/vdx /mnt/this", shell=True)
811
812 is_encrypted = self.mocker.replace(crypt_fs.is_encrypted)
813 is_encrypted("/dev/vdx")
814 self.mocker.result(False)
815
809 self.mocker.replay()816 self.mocker.replay()
810817
811 self.assertEqual(util.get_provider(), "block-storage-broker")818 self.assertEqual(util.get_provider(), "block-storage-broker")
@@ -815,7 +822,7 @@
815 util.hookenv.is_logged(message), "Not logged- %s" % message)822 util.hookenv.is_logged(message), "Not logged- %s" % message)
816823
817 # Wrote proper fstab mount info to mount on reboot824 # Wrote proper fstab mount info to mount on reboot
818 fstab_content = "/dev/vdx /mnt/this ext4 default 0 0"825 fstab_content = "/dev/vdx /mnt/this ext4 defaults 0 0\n"
819 with open(util.FSTAB_FILE) as input_file:826 with open(util.FSTAB_FILE) as input_file:
820 self.assertEqual(input_file.read(), fstab_content)827 self.assertEqual(input_file.read(), fstab_content)
821828
@@ -849,9 +856,9 @@
849 exists = self.mocker.replace(os.path.exists)856 exists = self.mocker.replace(os.path.exists)
850 exists("/dev/vdx1")857 exists("/dev/vdx1")
851 self.mocker.result(True)858 self.mocker.result(True)
852 create_partition = self.mocker.replace(859 create_partition = self.mocker.replace(util._format_device)
853 util._format_device)
854 create_partition("/dev/vdx1")860 create_partition("/dev/vdx1")
861 self.mocker.result("/dev/vdx1")
855862
856 _set_label = self.mocker.replace(util._set_label)863 _set_label = self.mocker.replace(util._set_label)
857 _set_label("/dev/vdx1", "/mnt/this")864 _set_label("/dev/vdx1", "/mnt/this")
@@ -861,6 +868,10 @@
861 get_fstype("blkid -o value -s TYPE /dev/vdx1", shell=True)868 get_fstype("blkid -o value -s TYPE /dev/vdx1", shell=True)
862 self.mocker.result("ext4\n")869 self.mocker.result("ext4\n")
863870
871 is_encrypted = self.mocker.replace(crypt_fs.is_encrypted)
872 is_encrypted("/dev/vdx1")
873 self.mocker.result(False)
874
864 exists("/mnt/this")875 exists("/mnt/this")
865 self.mocker.result(True)876 self.mocker.result(True)
866877
@@ -878,7 +889,7 @@
878 util.hookenv.is_logged(message), "Not logged- %s" % message)889 util.hookenv.is_logged(message), "Not logged- %s" % message)
879890
880 # Wrote proper fstab mount info to mount on reboot891 # Wrote proper fstab mount info to mount on reboot
881 fstab_content = "/dev/vdx1 /mnt/this ext4 default 0 0"892 fstab_content = "/dev/vdx1 /mnt/this ext4 defaults 0 0\n"
882 with open(util.FSTAB_FILE) as input_file:893 with open(util.FSTAB_FILE) as input_file:
883 self.assertEqual(input_file.read(), fstab_content)894 self.assertEqual(input_file.read(), fstab_content)
884895
885896
=== added file 'hooks/test_crypt_fs.py'
--- hooks/test_crypt_fs.py 1970-01-01 00:00:00 +0000
+++ hooks/test_crypt_fs.py 2014-11-07 12:56:29 +0000
@@ -0,0 +1,248 @@
1import crypt_fs
2import os
3import shutil
4import mocker
5from testing import TestHookenv
6
7
8class TestCryptFs(mocker.MockerTestCase):
9
10 def setUp(self):
11 self.maxDiff = None
12 crypt_fs.hookenv = TestHookenv()
13 crypt_fs.KEYFILE_DIR = "/tmp"
14 crypt_fs.CRYPTTAB_FILE = "/tmp/crypttab"
15 self.addCleanup(setattr, os, "environ", os.environ)
16 self.charm_dir = self.makeDir()
17 os.environ = {"CHARM_DIR": self.charm_dir}
18
19 def test_get_encryption_key_if_exists(self):
20 """
21 L{get_encryption_key} returns the encryption key for a
22 specific unit
23 """
24 crypt_fs.hookenv._config = (
25 ("encryption_key_map", "{data/0: password1}"),
26 )
27 self.assertEqual(crypt_fs.get_encryption_key(), "password1")
28
29 def test_get_encryption_key_if_not_exists(self):
30 """
31 L{get_encryption_key} returns None when no key found
32 """
33 crypt_fs.hookenv._config = (
34 ("encryption_key_map", "{data/1: password1}"),
35 )
36 self.assertEqual(crypt_fs.get_encryption_key(), None)
37
38 def test_write_encryption_keyfile(self):
39 """
40 L{_write_encryption_keyfile} writes the key to disk in
41 crypt_fs.KEYFILE_DIR
42 """
43 crypt_fs.hookenv._config = (
44 ("encryption_key_map", "{data/0: password1}"),
45 )
46 keyfile_path = os.path.join(crypt_fs.KEYFILE_DIR,
47 "keyfile--dev-something")
48 if os.path.exists(keyfile_path):
49 os.unlink(keyfile_path)
50 crypt_fs._write_encryption_keyfile("/dev/something")
51 self.assertTrue(os.path.exists(keyfile_path))
52 with open(keyfile_path) as infile:
53 key = infile.read()
54 self.assertEqual(key, "password1")
55
56 def test_delete_encryption_keyfile(self):
57 """
58 L{_delete_encryption_keyfile} should remove the key from
59 crypt_fs.KEYFILE_DIR
60 """
61 keyfile_path = os.path.join(crypt_fs.KEYFILE_DIR,
62 "keyfile--dev-something")
63 with open(keyfile_path, "w") as outfile:
64 outfile.write("test")
65 self.assertTrue(os.path.exists(keyfile_path))
66 crypt_fs._delete_encryption_keyfile("/dev/something")
67 self.assertFalse(os.path.exists(keyfile_path))
68
69 def test_write_encryption_details_no_uuid_no_crypttab(self):
70 """
71 L{write_encryption_details} Adds a crypttab entry for
72 the newly created filesystem.
73 It should leave it well alone for this test
74 """
75 crypt_fs.hookenv._config = (
76 ("encryption_key_map", "{data/0: password1}"),
77 )
78
79 encryption_uuid = self.mocker.replace(crypt_fs._get_encryption_uuid)
80 encryption_uuid("/dev/absent")
81 self.mocker.result(None)
82
83 copyfile = self.mocker.replace(shutil.copyfile)
84 src = os.path.join(crypt_fs.hookenv.charm_dir(),
85 "files",
86 "crypt_mount.sh")
87 copyfile(src, "/usr/local/sbin/crypt_mount.sh")
88
89 chmod = self.mocker.replace(os.chmod)
90 chmod("/usr/local/sbin/crypt_mount.sh", 0755)
91
92 self.mocker.replay()
93
94 if os.path.exists(crypt_fs.CRYPTTAB_FILE):
95 os.unlink(crypt_fs.CRYPTTAB_FILE)
96 self.assertFalse(os.path.exists(crypt_fs.CRYPTTAB_FILE))
97 crypt_fs.write_encryption_details("/dev/absent")
98 self.assertFalse(os.path.exists(crypt_fs.CRYPTTAB_FILE))
99
100 def test_write_encryption_details_no_uuid_have_crypttab(self):
101 """
102 L{write_encryption_details} Adds a crypttab entry for
103 the newly created filesystem
104 It should leave the existing file untouched in this test
105 """
106 crypt_fs.hookenv._config = (
107 ("encryption_key_map", "{data/0: password1}"),
108 )
109
110 encryption_uuid = self.mocker.replace(crypt_fs._get_encryption_uuid)
111 encryption_uuid("/dev/absent")
112 self.mocker.result(None)
113
114 copyfile = self.mocker.replace(shutil.copyfile)
115 src = os.path.join(crypt_fs.hookenv.charm_dir(),
116 "files",
117 "crypt_mount.sh")
118 copyfile(src, "/usr/local/sbin/crypt_mount.sh")
119
120 chmod = self.mocker.replace(os.chmod)
121 chmod("/usr/local/sbin/crypt_mount.sh", 0755)
122
123 self.mocker.replay()
124
125 crypttab_content = "sda_crypt UUID=SOME-EXISTING-UUID none luks\n"
126 with open(crypt_fs.CRYPTTAB_FILE, "w") as outfile:
127 outfile.write(crypttab_content)
128 self.assertTrue(os.path.exists(crypt_fs.CRYPTTAB_FILE))
129 crypt_fs.write_encryption_details("/dev/absent")
130 with open(crypt_fs.CRYPTTAB_FILE) as infile:
131 self.assertEqual(infile.read(), crypttab_content)
132
133 def test_write_encryption_details_have_uuid_no_crypttab(self):
134 """
135 L{write_encryption_details} Adds a crypttab entry for
136 the newly created filesystem
137 It should leave the existing file untouched in this test
138 """
139 crypt_fs.hookenv._config = (
140 ("encryption_key_map", "{data/0: password1}"),
141 ("store_encryption_keys", "yes"),
142 )
143
144 encryption_uuid = self.mocker.replace(crypt_fs._get_encryption_uuid)
145 encryption_uuid("/dev/something")
146 self.mocker.result("SOME-NEW-UUID")
147
148 copyfile = self.mocker.replace(shutil.copyfile)
149 src = os.path.join(crypt_fs.hookenv.charm_dir(),
150 "files",
151 "crypt_mount.sh")
152 copyfile(src, "/usr/local/sbin/crypt_mount.sh")
153
154 chmod = self.mocker.replace(os.chmod)
155 chmod("/usr/local/sbin/crypt_mount.sh", 0755)
156
157 self.mocker.replay()
158
159 if os.path.exists(crypt_fs.CRYPTTAB_FILE):
160 os.unlink(crypt_fs.CRYPTTAB_FILE)
161 self.assertFalse(os.path.exists(crypt_fs.CRYPTTAB_FILE))
162 crypt_fs.write_encryption_details("/dev/something")
163 keyfile_path = os.path.join(crypt_fs.KEYFILE_DIR,
164 "keyfile--dev-something")
165 crypttab_content = (
166 "something UUID=SOME-NEW-UUID {} luks\n".format(keyfile_path))
167 with open(crypt_fs.CRYPTTAB_FILE) as infile:
168 self.assertEqual(infile.read(), crypttab_content)
169
170 def test_write_encryption_details_have_uuid_have_crypttab(self):
171 """
172 L{write_encryption_details} Adds a crypttab entry for
173 the newly created filesystem
174 It should add to the file for this test
175 """
176 crypt_fs.hookenv._config = (
177 ("encryption_key_map", "{data/0: password1}"),
178 ("store_encryption_keys", "yes"),
179 )
180
181 encryption_uuid = self.mocker.replace(crypt_fs._get_encryption_uuid)
182 encryption_uuid("/dev/something")
183 self.mocker.result("SOME-NEW-UUID")
184
185 copyfile = self.mocker.replace(shutil.copyfile)
186 src = os.path.join(crypt_fs.hookenv.charm_dir(),
187 "files",
188 "crypt_mount.sh")
189 copyfile(src, "/usr/local/sbin/crypt_mount.sh")
190
191 chmod = self.mocker.replace(os.chmod)
192 chmod("/usr/local/sbin/crypt_mount.sh", 0755)
193
194 self.mocker.replay()
195
196 crypttab_content = "sda_crypt UUID=SOME-EXISTING-UUID none luks\n"
197 with open(crypt_fs.CRYPTTAB_FILE, "w") as outfile:
198 outfile.write(crypttab_content)
199 self.assertTrue(os.path.exists(crypt_fs.CRYPTTAB_FILE))
200 crypt_fs.write_encryption_details("/dev/something")
201 keyfile_path = os.path.join(crypt_fs.KEYFILE_DIR,
202 "keyfile--dev-something")
203 crypttab_content = (
204 "sda_crypt UUID=SOME-EXISTING-UUID none luks\n"
205 "something UUID=SOME-NEW-UUID {} luks\n".format(keyfile_path))
206 with open(crypt_fs.CRYPTTAB_FILE) as infile:
207 self.assertEqual(infile.read(), crypttab_content)
208
209 def test_write_encryption_details_have_uuid_have_duplicate_crypttab(self):
210 """
211 L{write_encryption_details} Adds a crypttab entry for
212 the newly created filesystem
213 It should replace an existing entry in this test
214 """
215 crypt_fs.hookenv._config = (
216 ("encryption_key_map", "{data/0: password1}"),
217 ("store_encryption_keys", "yes"),
218 )
219
220 encryption_uuid = self.mocker.replace(crypt_fs._get_encryption_uuid)
221 encryption_uuid("/dev/something")
222 self.mocker.result("SOME-NEW-UUID")
223
224 copyfile = self.mocker.replace(shutil.copyfile)
225 src = os.path.join(crypt_fs.hookenv.charm_dir(),
226 "files",
227 "crypt_mount.sh")
228 copyfile(src, "/usr/local/sbin/crypt_mount.sh")
229
230 chmod = self.mocker.replace(os.chmod)
231 chmod("/usr/local/sbin/crypt_mount.sh", 0755)
232
233 self.mocker.replay()
234
235 crypttab_content = (
236 "sda_crypt UUID=SOME-EXISTING-UUID none luks\n"
237 "something UUID=AN-OLD-UUID none luks\n")
238 with open(crypt_fs.CRYPTTAB_FILE, "w") as outfile:
239 outfile.write(crypttab_content)
240 self.assertTrue(os.path.exists(crypt_fs.CRYPTTAB_FILE))
241 crypt_fs.write_encryption_details("/dev/something")
242 keyfile_path = os.path.join(crypt_fs.KEYFILE_DIR,
243 "keyfile--dev-something")
244 crypttab_content = (
245 "sda_crypt UUID=SOME-EXISTING-UUID none luks\n"
246 "something UUID=SOME-NEW-UUID {} luks\n".format(keyfile_path))
247 with open(crypt_fs.CRYPTTAB_FILE) as infile:
248 self.assertEqual(infile.read(), crypttab_content)
0249
=== added file 'tests/10-crypt-keep-keyfile'
--- tests/10-crypt-keep-keyfile 1970-01-01 00:00:00 +0000
+++ tests/10-crypt-keep-keyfile 2014-11-07 12:56:29 +0000
@@ -0,0 +1,79 @@
1#!/usr/bin/python3
2#
3# Tests the partition provider with disk encryption
4# Almost identical to 20-crypt-lose-keyfile only we want to
5# make sure everything works AND the keyfile still exists
6
7import amulet
8import time
9import logging
10
11d = amulet.Deployment(series="trusty")
12
13# Add units
14d.add('postgresql', 'postgresql')
15d.add('storage', 'storage')
16
17# Set the unit configs
18d.configure("storage", {"provider": "partition", "volume_map": "{postgresql/0: /dev/loop0}", "encryption_key_map": "{postgresql/0: password1}", "store_encryption_keys": "yes"})
19
20try:
21 d.setup(timeout=900)
22 d.sentry.wait()
23except amulet.helpers.TimeoutError:
24 amulet.raise_status(amulet.FAIL, msg="Environment wasn't stood up in time")
25except:
26 raise
27
28unit = d.sentry['postgresql/0']
29
30# Create a test device
31if unit.run("dd if=/dev/zero of=/testfile bs=1024 count=10240 2>&1")[1] != 0:
32 amulet.raise_status(amulet.FAIL, msg="dd failed creating /testfile")
33if unit.run("losetup /dev/loop0 /testfile")[1] != 0:
34 amulet.raise_status(amulet.FAIL, msg="losetup failed creating /dev/loop0")
35time.sleep(10)
36
37# Add relations
38d.relate('postgresql:data', 'storage:data')
39d.sentry.wait()
40time.sleep(30)
41
42
43# Check mounts
44mounts = unit.run("cat /proc/mounts")[0].split("\n")
45mountsOk = False
46for line in mounts:
47 if "/dev/mapper/loop0" in line and "/srv/data" in line:
48 mountsOk = True
49if not mountsOk:
50 amulet.raise_status(amulet.FAIL, msg="/dev/mapper/loop0 mount missing")
51
52# Check fstab
53fstab = unit.run("cat /etc/fstab")[0].split("\n")
54fstabOk = False
55for line in fstab:
56 if "/dev/mapper/loop0" in line and "/srv/data" in line:
57 fstabOk = True
58if not fstabOk:
59 amulet.raise_status(amulet.FAIL, msg="/dev/mapper/loop0 fstab entry missing")
60
61# Check crypt status
62cryptstatus = unit.run("cryptsetup status /dev/mapper/loop0")[0].split("\n")
63isActive = False
64isLuks = False
65for line in cryptstatus:
66 if "/dev/mapper/loop0 is active and is in use" in line:
67 isActive = True
68 if "type:" in line and "LUKS1" in line:
69 isLuks = True
70if not (isActive and isLuks):
71 amulet.raise_status(amulet.FAIL, msg="Crypt status incorrect")
72
73# Make sure keyfile exists
74if not "keyfile--dev-loop0" in unit.directory_contents("/root")["files"]:
75 amulet.raise_status(amulet.FAIL, msg="Keyfile not found")
76
77# And has the correct data
78if unit.file_contents("/root/keyfile--dev-loop0") != "password1":
79 amulet.raise_status(amulet.FAIL, msg="Keyfile incorrect")
080
=== added file 'tests/20-crypt-lose-keyfile'
--- tests/20-crypt-lose-keyfile 1970-01-01 00:00:00 +0000
+++ tests/20-crypt-lose-keyfile 2014-11-07 12:56:29 +0000
@@ -0,0 +1,73 @@
1#!/usr/bin/python3
2#
3# Tests the partition provider with disk encryption
4# Almost identical to 10-crypt-keep-keyfile only we want to
5# make sure everything works AND the keyfile is deleted
6
7import amulet
8import time
9
10d = amulet.Deployment(series="trusty")
11
12# Add units
13d.add('postgresql', 'postgresql')
14d.add('storage', 'storage')
15
16# Set the unit configs
17d.configure("storage", {"provider": "partition", "volume_map": "{postgresql/0: /dev/loop0}", "encryption_key_map": "{postgresql/0: password1}", "store_encryption_keys": "no"})
18
19try:
20 d.setup(timeout=900)
21 d.sentry.wait()
22except amulet.helpers.TimeoutError:
23 amulet.raise_status(amulet.FAIL, msg="Environment wasn't stood up in time")
24except:
25 raise
26
27unit = d.sentry.unit['postgresql/0']
28
29# Create a test device
30if unit.run("dd if=/dev/zero of=/testfile bs=1024 count=10240 2>&1")[1] != 0:
31 amulet.raise_status(amulet.FAIL, msg="dd failed creating /testfile")
32if unit.run("losetup /dev/loop0 /testfile")[1] != 0:
33 amulet.raise_status(amulet.FAIL, msg="losetup failed creating /dev/loop0")
34time.sleep(10)
35
36# Add relations
37d.relate('postgresql:data', 'storage:data')
38d.sentry.wait()
39time.sleep(30)
40
41# Check mounts
42mounts = unit.run("cat /proc/mounts")[0].split("\n")
43mountsOk = False
44for line in mounts:
45 if "/dev/mapper/loop0" in line and "/srv/data" in line:
46 mountsOk = True
47if not mountsOk:
48 amulet.raise_status(amulet.FAIL, msg="/dev/mapper/loop0 mount missing")
49
50# Check fstab
51fstab = unit.run("cat /etc/fstab")[0].split("\n")
52fstabOk = False
53for line in fstab:
54 if "/dev/mapper/loop0" in line and "/srv/data" in line:
55 fstabOk = True
56if not fstabOk:
57 amulet.raise_status(amulet.FAIL, msg="/dev/mapper/loop0 fstab entry missing")
58
59# Check crypt status
60cryptstatus = unit.run("cryptsetup status /dev/mapper/loop0")[0].split("\n")
61isActive = False
62isLuks = False
63for line in cryptstatus:
64 if "/dev/mapper/loop0 is active and is in use" in line:
65 isActive = True
66 if "type:" in line and "LUKS1" in line:
67 isLuks = True
68if not (isActive and isLuks):
69 amulet.raise_status(amulet.FAIL, msg="Crypt status incorrect")
70
71# Make sure keyfile does not exist
72if "keyfile--dev-loop0" in unit.directory_contents("/root")["files"]:
73 amulet.raise_status(amulet.FAIL, msg="Keyfile found when not wanted")

Subscribers

People subscribed via source and target branches

to all changes: