diff --git a/ironic_inspector/plugins/rules.py b/ironic_inspector/plugins/rules.py index adc19429a..b9aea72fb 100644 --- a/ironic_inspector/plugins/rules.py +++ b/ironic_inspector/plugins/rules.py @@ -115,7 +115,10 @@ class FailAction(base.RuleActionPlugin): class SetAttributeAction(base.RuleActionPlugin): - REQUIRED_PARAMS = {'path', 'value'} + # NOTE(iurygregory): set as optional to accept None as value, check + # that the key 'value' is present, otherwise will raise ValueError. + OPTIONAL_PARAMS = {'value'} + REQUIRED_PARAMS = {'path'} # TODO(dtantsur): proper validation of path FORMATTED_PARAMS = ['value'] @@ -124,6 +127,13 @@ class SetAttributeAction(base.RuleActionPlugin): node_info.patch([{'op': 'add', 'path': params['path'], 'value': params['value']}]) + def validate(self, params, **kwargs): + if 'value' in params: + super(base.RuleActionPlugin, self).validate(params, **kwargs) + else: + msg = _('missing required parameter(s): value') + raise ValueError(msg) + class SetCapabilityAction(base.RuleActionPlugin): REQUIRED_PARAMS = {'name'} diff --git a/ironic_inspector/test/unit/test_plugins_rules.py b/ironic_inspector/test/unit/test_plugins_rules.py index cfdbbb540..9c9aa4a16 100644 --- a/ironic_inspector/test/unit/test_plugins_rules.py +++ b/ironic_inspector/test/unit/test_plugins_rules.py @@ -157,6 +157,8 @@ class TestSetAttributeAction(test_base.NodeTest): self.assertRaises(ValueError, self.act.validate, {'value': 42}) self.assertRaises(ValueError, self.act.validate, {'path': '/extra/value'}) + self.params['value'] = None + self.act.validate(self.params) @mock.patch.object(node_cache.NodeInfo, 'patch') def test_apply(self, mock_patch): diff --git a/ironic_inspector/test/unit/test_rules.py b/ironic_inspector/test/unit/test_rules.py index 8d53b0b6d..5ba854737 100644 --- a/ironic_inspector/test/unit/test_rules.py +++ b/ironic_inspector/test/unit/test_rules.py @@ -59,6 +59,18 @@ class TestCreateRule(BaseTest): 'actions': self.actions_json}, rule_json) + def test_create_action_none_value(self): + self.actions_json = [{'action': 'set-attribute', + 'path': '/properties/cpus', 'value': None}] + rule = rules.create([], self.actions_json) + rule_json = rule.as_dict() + + self.assertTrue(rule_json.pop('uuid')) + self.assertEqual({'description': None, + 'conditions': [], + 'actions': self.actions_json}, + rule_json) + def test_duplicate_uuid(self): rules.create([], self.actions_json, uuid=self.uuid) self.assertRaisesRegex(utils.Error, 'already exists', diff --git a/releasenotes/notes/unset_property_instrospectionrules-78d64b8b7643e40d.yaml b/releasenotes/notes/unset_property_instrospectionrules-78d64b8b7643e40d.yaml new file mode 100644 index 000000000..9340701df --- /dev/null +++ b/releasenotes/notes/unset_property_instrospectionrules-78d64b8b7643e40d.yaml @@ -0,0 +1,4 @@ +fixes: + - | + Allows the ``set-attribute`` introspection rule action to accept ``None`` + as value for a property.