Giuseppe Lavagetto has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/405302 )

Change subject: Add preemptive validation.
......................................................................

Add preemptive validation.

When writing to the backend, let's check that all the new values are
valid before writing.

Bug: T185080
Change-Id: I17c8e5cebd71369e27f78f05db1aaac31165467c
---
M conftool/action.py
M conftool/cli/syncer.py
M conftool/kvobject.py
M conftool/tests/integration/__init__.py
M conftool/tests/unit/test_kvobject.py
5 files changed, 33 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/operations/software/conftool 
refs/changes/02/405302/1

diff --git a/conftool/action.py b/conftool/action.py
index 92313ae..1870435 100644
--- a/conftool/action.py
+++ b/conftool/action.py
@@ -8,6 +8,10 @@
     pass
 
 
+class ActionValidationError(ActionError):
+    pass
+
+
 class Action(object):
 
     def __init__(self, obj, act):
@@ -80,6 +84,12 @@
         elif self.action == 'set':
             if not self.entity.exists:
                 raise ActionError("Entity %s doesn't exist" % self.entity.name)
+            # Validate the new data *before* updating the object
+            try:
+                self.entity.validate(self.args)
+            except Exception as e:
+                raise ActionValidationError("The provided data is not valid: 
%s" % e)
+
             desc = []
             for (k, v) in self.args.items():
                 curval = getattr(self.entity, k)
diff --git a/conftool/cli/syncer.py b/conftool/cli/syncer.py
index 2debe94..fc3b430 100644
--- a/conftool/cli/syncer.py
+++ b/conftool/cli/syncer.py
@@ -135,6 +135,7 @@
             obj = self.cls(*tags)
             if obj.static_values:
                 _log.info("Syncing static object %s:%s", self.entity, key)
+                obj.validate(self.data[key])
                 obj.from_net(self.data[key])
             else:
                 if obj.exists:
diff --git a/conftool/kvobject.py b/conftool/kvobject.py
index 45ab060..e45ec5f 100644
--- a/conftool/kvobject.py
+++ b/conftool/kvobject.py
@@ -121,6 +121,20 @@
             self._set_value(k, self._schema[k], {k: v}, set_defaults=False)
         self.write()
 
+    def validate(self, values):
+        """
+        Validate a set of proposed values against the schema.
+        Returns True on success, raises an exception otherwise
+        """
+        for k, v in values.items():
+            if k not in self._schema:
+                if self.strict_schema:
+                    raise TypeError("Key %s not in the schema" % k)
+                continue
+            validator = self._schema[k]
+            validator(v)
+        return True
+
     @classmethod
     def from_yaml(cls, data):
         if cls.static_values:
@@ -232,6 +246,7 @@
     General-purpose entity with a strict schema
     """
     depends = []
+    strict_schema = True
 
     def __init__(self, *tags):
         if len(tags) != (len(self._tags) + 1):
@@ -266,6 +281,7 @@
 
 
 class FreeSchemaEntity(Entity):
+    strict_schema = False
 
     def __init__(self, *tags, **kwargs):
         self._schemaless = kwargs
diff --git a/conftool/tests/integration/__init__.py 
b/conftool/tests/integration/__init__.py
index a207875..4dc3a8b 100644
--- a/conftool/tests/integration/__init__.py
+++ b/conftool/tests/integration/__init__.py
@@ -60,6 +60,7 @@
     def stop(self):
         self.proc.kill()
         self.proc = None
+        time.sleep(2)
 
 
 class IntegrationTestBase(unittest.TestCase):
diff --git a/conftool/tests/unit/test_kvobject.py 
b/conftool/tests/unit/test_kvobject.py
index 494c810..d503ab6 100644
--- a/conftool/tests/unit/test_kvobject.py
+++ b/conftool/tests/unit/test_kvobject.py
@@ -133,6 +133,11 @@
                                                   set_defaults=False)
         self.entity.write.assert_called_with()
 
+    def test_validate(self):
+        self.assertTrue(self.entity.validate({'a': 1, 'b': 'testtest'}))
+        self.assertRaises(TypeError, self.entity.validate, {'a': 1, 'b': 
'testtest', 'c': True})
+        self.assertRaises(ValueError, self.entity.validate, {'a': 'test'})
+
     def test_to_net(self):
         self.entity.a = 100
         self.entity.b = 'meoow'

-- 
To view, visit https://gerrit.wikimedia.org/r/405302
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I17c8e5cebd71369e27f78f05db1aaac31165467c
Gerrit-PatchSet: 1
Gerrit-Project: operations/software/conftool
Gerrit-Branch: master
Gerrit-Owner: Giuseppe Lavagetto <glavage...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to