osbuild: error when {Device,Mount} is modified after creation

This is a drive-by change after spending some quality time with the
mount code. The `id` field of `Mount` is calculated only once and
only when creating a `Mount`. This seems slightly dangerous as
any change to an attribute after creation will not update the
id. This means two options:
1. dynamically update the `id` on changes
2. forbid changes after the `id` is calculcated

I went with (2) but happy to discuss of course but it seems more
the spirit of the class.

It also does the same change for "devices.Device"
This commit is contained in:
Michael Vogt 2023-12-21 19:04:52 +01:00 committed by Simon de Vlieger
parent 4977501cc6
commit f5d6d11f1d
5 changed files with 51 additions and 3 deletions

View file

@ -21,10 +21,11 @@ import stat
from typing import Any, Dict, Optional
from osbuild import host
from osbuild.mixins import MixinImmutableID
from osbuild.util import ctx
class Device:
class Device(MixinImmutableID):
"""
A single device with its corresponding options
"""

View file

@ -48,7 +48,6 @@ class Input:
self.id = self.calc_id()
def calc_id(self):
# NB: The input `name` is not included here on purpose since it
# is either prescribed by the stage itself and thus not actual
# parameter or arbitrary and chosen by the manifest generator

15
osbuild/mixins.py Normal file
View file

@ -0,0 +1,15 @@
"""
Mixin helper classes
"""
class MixinImmutableID:
"""
Mixin to ensure that "self.id" attributes are immutable after id is set
"""
def __setattr__(self, name, val):
if hasattr(self, "id"):
class_name = self.__class__.__name__
raise ValueError(f"cannot set '{name}': {class_name} cannot be changed after creation")
super().__setattr__(name, val)

View file

@ -16,9 +16,10 @@ from typing import Dict, List
from osbuild import host
from osbuild.devices import DeviceManager
from osbuild.mixins import MixinImmutableID
class Mount:
class Mount(MixinImmutableID):
"""
A single mount with its corresponding options
"""

32
test/mod/test_mixins.py Normal file
View file

@ -0,0 +1,32 @@
from unittest.mock import Mock
import pytest
from osbuild.devices import Device
from osbuild.meta import ModuleInfo
from osbuild.mounts import Mount
def test_mount_immutable_mixin():
info = Mock(spec=ModuleInfo)
info.name = "some-name"
device = Mock(spec=ModuleInfo)
device.id = "some-id"
partition = 1
target = "/"
opts = {"opt1": 1}
mnt = Mount("name", info, device, partition, target, opts)
with pytest.raises(ValueError) as e:
mnt.name = "new-name"
assert str(e.value) == "cannot set 'name': Mount cannot be changed after creation"
def test_device_immutable_mixins():
info = Mock(spec=ModuleInfo)
info.name = "some-name"
parent = None
opts = {"opt1": 1}
dev = Device("name", info, parent, opts)
with pytest.raises(ValueError) as e:
dev.name = "new-name"
assert str(e.value) == "cannot set 'name': Device cannot be changed after creation"