Skip to content

Commit df421e9

Browse files
committed
Adding support for new snapper helpers
This commit makes use of snapper helpers to install a new system where frist root is already a snapshot. Requires snapper higher or equal to v0.12.1. Signed-off-by: David Cassany <[email protected]>
1 parent 1a2bfaf commit df421e9

File tree

2 files changed

+195
-5
lines changed

2 files changed

+195
-5
lines changed

kiwi/volume_manager/btrfs.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,18 @@ def _set_snapper_sysconfig_file(root_path):
542542
)
543543

544544
def _create_first_snapper_snapshot_as_default(self):
545-
if not CommandCapabilities.check_version(
546-
'snapper', (0,12,1), root=self.root_dir
545+
if CommandCapabilities.check_version(
546+
'snapper', (0, 12, 1), root=self.root_dir
547547
):
548+
with ChrootManager(
549+
self.root_dir, binds=[self.mountpoint]
550+
) as chroot:
551+
chroot.run([
552+
'/usr/lib/snapper/installation-helper', '--root-prefix',
553+
os.sep.join([self.mountpoint, self.root_volume_name]),
554+
'--step', 'filesystem'
555+
])
556+
else:
548557
snapshot_volume = self.mountpoint + \
549558
f'/{self.root_volume_name}/.snapshots'
550559
Command.run(
@@ -562,9 +571,19 @@ def _create_first_snapper_snapshot_as_default(self):
562571
)
563572

564573
def _create_snapshot_info(self, path):
565-
if not CommandCapabilities.check_version(
566-
'snapper', (0,12,1), root=self.mountpoint
574+
if CommandCapabilities.check_version(
575+
'snapper', (0, 12, 1), root=self.mountpoint
567576
):
577+
snapshots_prefix = os.sep.join([path, '.snapshots'])
578+
with ChrootManager(
579+
self.root_dir, binds=[path, snapshots_prefix]
580+
) as chroot:
581+
chroot.run([
582+
'/usr/lib/snapper/installation-helper', '--root-prefix',
583+
path, '--step', 'config', '--description',
584+
'first root filesystem'
585+
])
586+
else:
568587
date_info = datetime.datetime.now()
569588
snapshot = ElementTree.Element('snapshot')
570589

test/unit/volume_manager/btrfs_test.py

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,73 @@ def return_snapper_version(cmd, *args):
179179
]
180180
mock_os_chmod.assert_called_once_with('tmpdir/@/.snapshots', 0o700)
181181

182+
@patch('os.chmod')
183+
@patch('os.path.exists')
184+
@patch('kiwi.volume_manager.btrfs.Command.run')
185+
@patch('kiwi.volume_manager.btrfs.FileSystem.new')
186+
@patch('kiwi.volume_manager.btrfs.MappedDevice')
187+
@patch('kiwi.volume_manager.btrfs.MountManager')
188+
@patch('kiwi.volume_manager.base.Temporary')
189+
def test_setup_with_snapshot_helpers(
190+
self, mock_Temporary, mock_mount, mock_mapped_device, mock_fs,
191+
mock_command, mock_os_exists, mock_os_chmod
192+
):
193+
def return_snapper_version(command=None, raise_on_error=None, *args):
194+
mock = Mock()
195+
snapperCmd = ['chroot', 'snapper', '--version']
196+
subCmd = [element for element in command if element in snapperCmd]
197+
if snapperCmd == subCmd:
198+
mock = Mock()
199+
mock.output = 'snapper 0.12.1'
200+
else:
201+
mock.output = \
202+
'ID 258 gen 26 top level 257 path @/.snapshots/1/snapshot'
203+
mock.return_code = 0
204+
return mock
205+
206+
mock_command.side_effect = return_snapper_version
207+
208+
mock_Temporary.return_value.new_dir.return_value.name = 'tmpdir'
209+
toplevel_mount = Mock()
210+
mock_mount.return_value = toplevel_mount
211+
mock_mapped_device.return_value = 'mapped_device'
212+
mock_os_exists.return_value = False
213+
self.volume_manager.custom_args['root_is_snapper_snapshot'] = True
214+
self.volume_manager.custom_args['quota_groups'] = True
215+
216+
self.volume_manager.setup()
217+
218+
assert mock_mount.call_args_list == [
219+
call(device='/dev/storage', mountpoint='tmpdir'),
220+
call(
221+
device='/dev/storage',
222+
attributes={
223+
'subvol_path': '@/.snapshots',
224+
'subvol_name': '@/.snapshots'
225+
},
226+
mountpoint='tmpdir/@/.snapshots/1/snapshot/.snapshots'
227+
)
228+
]
229+
toplevel_mount.mount.assert_called_once_with([])
230+
assert mock_command.call_args_list == [
231+
call(['btrfs', 'quota', 'enable', 'tmpdir']),
232+
call(['btrfs', 'subvolume', 'create', 'tmpdir/@']),
233+
call(['chroot', 'root_dir', 'snapper', '--version']),
234+
call(
235+
command=['mountpoint', '-q', 'root_dir/tmpdir'],
236+
raise_on_error=False
237+
),
238+
call(['mount', '-n', '--bind', 'tmpdir', 'root_dir/tmpdir']),
239+
call([
240+
'chroot', 'root_dir', '/usr/lib/snapper/installation-helper',
241+
'--root-prefix', 'tmpdir/@', '--step', 'filesystem'
242+
], None, True, False, True),
243+
call(
244+
command=['mountpoint', '-q', 'root_dir/tmpdir'],
245+
raise_on_error=False
246+
),
247+
]
248+
182249
@patch('os.path.exists')
183250
@patch('kiwi.volume_manager.btrfs.Command.run')
184251
@patch('kiwi.volume_manager.btrfs.FileSystem.new')
@@ -508,6 +575,110 @@ def return_snapper_version(cmd, *args):
508575
], None, True, False, True)
509576
]
510577

578+
@patch('kiwi.volume_manager.btrfs.SysConfig')
579+
@patch('kiwi.volume_manager.btrfs.DataSync')
580+
@patch('kiwi.volume_manager.btrfs.Command.run')
581+
@patch('os.path.exists')
582+
@patch('shutil.copyfile')
583+
def test_sync_data_with_snapper_helpers(
584+
self, mock_copy, mock_exists, mock_command,
585+
mock_sync, mock_sysconf
586+
):
587+
item = {'SNAPPER_CONFIGS': '""'}
588+
589+
def getitem(key):
590+
return item[key]
591+
592+
def setitem(key, value):
593+
item[key] = value
594+
595+
def contains(key):
596+
return key in item
597+
598+
def exists(name):
599+
if 'snapper/configs/root' in name:
600+
return False
601+
return True
602+
603+
def return_snapper_version(command=None, raise_on_error=None, *args):
604+
snapperCmd = ['chroot', 'snapper', '--version']
605+
subCmd = [element for element in command if element in snapperCmd]
606+
mock = Mock()
607+
if snapperCmd == subCmd:
608+
mock.output = 'snapper 0.12.1'
609+
mock.return_code = 0
610+
return mock
611+
612+
mock_command.side_effect = return_snapper_version
613+
614+
self.volume_manager.custom_args['quota_groups'] = True
615+
mock_exists.side_effect = exists
616+
617+
sysconf = Mock()
618+
sysconf.__contains__ = Mock(side_effect=contains)
619+
sysconf.__getitem__ = Mock(side_effect=getitem)
620+
sysconf.__setitem__ = Mock(side_effect=setitem)
621+
mock_sysconf.return_value = sysconf
622+
623+
self.volume_manager.toplevel_mount = Mock()
624+
self.volume_manager.mountpoint = 'tmpdir'
625+
self.volume_manager.custom_args['root_is_snapper_snapshot'] = True
626+
sync = Mock()
627+
mock_sync.return_value = sync
628+
629+
self.volume_manager.sync_data(['exclude_me'])
630+
631+
root_path = 'tmpdir/@/.snapshots/1/snapshot'
632+
mock_sync.assert_called_once_with('root_dir', root_path)
633+
mock_copy.assert_called_once_with(
634+
root_path + '/etc/snapper/config-templates/default',
635+
root_path + '/etc/snapper/configs/root'
636+
)
637+
sync.sync_data.assert_called_once_with(
638+
exclude=['exclude_me'],
639+
options=[
640+
'--archive', '--hard-links', '--xattrs',
641+
'--acls', '--one-file-system', '--inplace'
642+
]
643+
)
644+
assert mock_command.call_args_list == [
645+
call(['chroot', 'tmpdir', 'snapper', '--version']),
646+
call(
647+
command=['mountpoint', '-q', 'root_dir/tmpdir/@/.snapshots/1'],
648+
raise_on_error=False
649+
),
650+
call([
651+
'mount', '-n', '--bind', 'tmpdir/@/.snapshots/1',
652+
'root_dir/tmpdir/@/.snapshots/1'
653+
]),
654+
call(
655+
command=['mountpoint', '-q', 'root_dir/tmpdir/@/.snapshots/1/.snapshots'],
656+
raise_on_error=False
657+
),
658+
call([
659+
'mount', '-n', '--bind', 'tmpdir/@/.snapshots/1/.snapshots',
660+
'root_dir/tmpdir/@/.snapshots/1/.snapshots'
661+
]),
662+
call([
663+
'chroot', 'root_dir', '/usr/lib/snapper/installation-helper',
664+
'--root-prefix', 'tmpdir/@/.snapshots/1', '--step', 'config',
665+
'--description', 'first root filesystem'
666+
], None, True, False, True),
667+
call(
668+
command=['mountpoint', '-q', 'root_dir/tmpdir/@/.snapshots/1/.snapshots'],
669+
raise_on_error=False
670+
),
671+
call(
672+
command=['mountpoint', '-q', 'root_dir/tmpdir/@/.snapshots/1'],
673+
raise_on_error=False
674+
),
675+
call(['btrfs', 'qgroup', 'create', '1/0', 'tmpdir']),
676+
call([
677+
'chroot', 'tmpdir/@/.snapshots/1/snapshot', 'snapper', '--no-dbus',
678+
'set-config', 'QGROUP=1/0'
679+
], None, True, False, True)
680+
]
681+
511682
@patch('kiwi.volume_manager.btrfs.SysConfig')
512683
@patch('kiwi.volume_manager.btrfs.DataSync')
513684
@patch('kiwi.volume_manager.btrfs.Command.run')
@@ -526,7 +697,7 @@ def contains(key):
526697

527698
def return_snapper_version(cmd, *args):
528699
snapperCmd = ['chroot', 'snapper', '--version']
529-
subCmd = [element for element in cmd if element in snapperCmd]
700+
subCmd = [element for element in cmd if element in snapperCmd]
530701
if snapperCmd == subCmd:
531702
mock = Mock()
532703
mock.output = 'snapper 0.12.0'

0 commit comments

Comments
 (0)