diff --git a/supervisor/addons/model.py b/supervisor/addons/model.py index a2c6bd7a0..d0c6601fa 100644 --- a/supervisor/addons/model.py +++ b/supervisor/addons/model.py @@ -535,7 +535,9 @@ class AddonModel(CoreSysAttributes, ABC): # Machine / Hardware machine = config.get(ATTR_MACHINE) - if machine and self.sys_machine not in machine: + if machine and f"!{self.sys_machine}" in machine: + return False + elif machine and self.sys_machine not in machine: return False # Home Assistant diff --git a/supervisor/addons/validate.py b/supervisor/addons/validate.py index 68b232601..03f2747bd 100644 --- a/supervisor/addons/validate.py +++ b/supervisor/addons/validate.py @@ -120,7 +120,10 @@ V_LIST = "list" RE_SCHEMA_ELEMENT = re.compile( r"^(?:" - r"|bool|email|url|port" + r"|bool" + r"|email" + r"|url" + r"|port" r"|str(?:\((?P\d+)?,(?P\d+)?\))?" r"|password(?:\((?P\d+)?,(?P\d+)?\))?" r"|int(?:\((?P\d+)?,(?P\d+)?\))?" @@ -148,24 +151,25 @@ RE_DOCKER_IMAGE_BUILD = re.compile( SCHEMA_ELEMENT = vol.Match(RE_SCHEMA_ELEMENT) - -MACHINE_ALL = [ - "intel-nuc", - "odroid-c2", - "odroid-n2", - "odroid-xu", - "qemuarm-64", - "qemuarm", - "qemux86-64", - "qemux86", - "raspberrypi", - "raspberrypi2", - "raspberrypi3-64", - "raspberrypi3", - "raspberrypi4-64", - "raspberrypi4", - "tinker", -] +RE_MACHINE = re.compile( + r"^!?(?:" + r"|intel-nuc" + r"|odroid-c2" + r"|odroid-n2" + r"|odroid-xu" + r"|qemuarm-64" + r"|qemuarm" + r"|qemux86-64" + r"|qemux86" + r"|raspberrypi" + r"|raspberrypi2" + r"|raspberrypi3-64" + r"|raspberrypi3" + r"|raspberrypi4-64" + r"|raspberrypi4" + r"|tinker" + r")$" +) def _simple_startup(value) -> str: @@ -185,7 +189,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema( vol.Required(ATTR_SLUG): vol.Coerce(str), vol.Required(ATTR_DESCRIPTON): vol.Coerce(str), vol.Required(ATTR_ARCH): [vol.In(ARCH_ALL)], - vol.Optional(ATTR_MACHINE): [vol.In(MACHINE_ALL)], + vol.Optional(ATTR_MACHINE): vol.All([vol.Match(RE_MACHINE)], vol.Unique()), vol.Optional(ATTR_URL): vol.Url(), vol.Required(ATTR_STARTUP): vol.All(_simple_startup, vol.Coerce(AddonStartup)), vol.Required(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]), diff --git a/tests/addons/test_config.py b/tests/addons/test_config.py index 8cea64350..a8488f5e4 100644 --- a/tests/addons/test_config.py +++ b/tests/addons/test_config.py @@ -70,3 +70,87 @@ def test_valid_basic_build(): config = load_json_fixture("basic-build-config.json") vd.SCHEMA_BUILD_CONFIG(config) + + +def test_valid_machine(): + """Validate valid machine config.""" + config = load_json_fixture("basic-addon-config.json") + + config["machine"] = [ + "intel-nuc", + "odroid-c2", + "odroid-n2", + "odroid-xu", + "qemuarm-64", + "qemuarm", + "qemux86-64", + "qemux86", + "raspberrypi", + "raspberrypi2", + "raspberrypi3-64", + "raspberrypi3", + "raspberrypi4-64", + "raspberrypi4", + "tinker", + ] + + assert vd.SCHEMA_ADDON_CONFIG(config) + + config["machine"] = [ + "!intel-nuc", + "!odroid-c2", + "!odroid-n2", + "!odroid-xu", + "!qemuarm-64", + "!qemuarm", + "!qemux86-64", + "!qemux86", + "!raspberrypi", + "!raspberrypi2", + "!raspberrypi3-64", + "!raspberrypi3", + "!raspberrypi4-64", + "!raspberrypi4", + "!tinker", + ] + + assert vd.SCHEMA_ADDON_CONFIG(config) + + config["machine"] = [ + "odroid-n2", + "!odroid-xu", + "qemuarm-64", + "!qemuarm", + "qemux86-64", + "qemux86", + "raspberrypi", + "raspberrypi4-64", + "raspberrypi4", + "!tinker", + ] + + assert vd.SCHEMA_ADDON_CONFIG(config) + + +def test_invalid_machine(): + """Validate invalid machine config.""" + config = load_json_fixture("basic-addon-config.json") + + config["machine"] = [ + "intel-nuc", + "raspberrypi3", + "raspberrypi4-64", + "raspberrypi4", + "tinkerxy", + ] + + with pytest.raises(vol.Invalid): + assert vd.SCHEMA_ADDON_CONFIG(config) + + config["machine"] = [ + "intel-nuc", + "intel-nuc", + ] + + with pytest.raises(vol.Invalid): + assert vd.SCHEMA_ADDON_CONFIG(config)