diff --git a/landscape/client/monitor/snapmonitor.py b/landscape/client/monitor/snapmonitor.py index da7a66dff..162bc52e7 100644 --- a/landscape/client/monitor/snapmonitor.py +++ b/landscape/client/monitor/snapmonitor.py @@ -1,3 +1,4 @@ +import json import logging from landscape.client import snap_http @@ -33,7 +34,7 @@ def get_data(self): except SnapdHttpException: config = {} - snaps[i]["config"] = config + snaps[i]["config"] = json.dumps(config) # We get a lot of extra info from snapd. To avoid caching it all # or invalidating the cache on timestamp changes, we use Message diff --git a/landscape/client/monitor/tests/test_snapmonitor.py b/landscape/client/monitor/tests/test_snapmonitor.py index daec7bcbf..47883d5e2 100644 --- a/landscape/client/monitor/tests/test_snapmonitor.py +++ b/landscape/client/monitor/tests/test_snapmonitor.py @@ -50,46 +50,8 @@ def test_get_data_snapd_http_exception(self): ) @patch("landscape.client.monitor.snapmonitor.snap_http") - def test_get_simple_snap_config(self, snap_http_mock): - """Tests that we can get and coerce simple snap config.""" - plugin = SnapMonitor() - self.monitor.add(plugin) - - snap_http_mock.list.return_value = SnapdResponse( - "sync", - 200, - "OK", - [ - { - "name": "test-snap", - "revision": "1", - "confinement": "strict", - "version": "v1.0", - "id": "123", - } - ], - ) - snap_http_mock.get_conf.return_value = {"bar": "default", "baz": False} - plugin.exchange() - - messages = self.mstore.get_pending_messages() - - self.assertTrue(len(messages) > 0) - self.assertDictEqual( - messages[0]["snaps"]["installed"][0], - { - "name": "test-snap", - "revision": "1", - "confinement": "strict", - "version": "v1.0", - "id": "123", - "config": {"bar": "default", "baz": False}, - }, - ) - - @patch("landscape.client.monitor.snapmonitor.snap_http") - def test_get_complex_snap_config(self, snap_http_mock): - """Tests that we can get and coerce complex snap config.""" + def test_get_snap_config(self, snap_http_mock): + """Tests that we can get and coerce snap config.""" plugin = SnapMonitor() self.monitor.add(plugin) @@ -124,9 +86,9 @@ def test_get_complex_snap_config(self, snap_http_mock): "confinement": "strict", "version": "v1.0", "id": "123", - "config": { - "foo": {"baz": "default", "qux": [1, True, 2.0]}, - "bar": "enabled", - }, + "config": ( + '{"foo": {"baz": "default", "qux": [1, true, 2.0]}, ' + '"bar": "enabled"}' + ), }, ) diff --git a/landscape/lib/schema.py b/landscape/lib/schema.py index bf4a17750..80f4df164 100644 --- a/landscape/lib/schema.py +++ b/landscape/lib/schema.py @@ -1,6 +1,4 @@ """A schema system. Yes. Another one!""" -from functools import singledispatchmethod - from twisted.python.compat import iteritems from twisted.python.compat import long from twisted.python.compat import unicode @@ -235,56 +233,3 @@ def coerce(self, value): for k, v in value.items(): new_dict[self.key_schema.coerce(k)] = self.value_schema.coerce(v) return new_dict - - -class Nested: - """A type that allows nesting of C{list} and C{dict}. - - @param schema: A `List`, `Dict`, or `KeyDict` schema. - """ - - def __init__(self, schema): - if not isinstance(schema, (List, Dict, KeyDict)): - raise InvalidError(f"{schema} does not support nesting") - - self.schema = schema - - @singledispatchmethod - def coerce(self, value): - raise InvalidError( - f"{value!r} could not coerce with {self.schema}: " - "type does not support nesting" - ) - - @coerce.register(list) - def _(self, value): - if not isinstance(self.schema, List): - raise InvalidError( - f"{value!r} has type list which doesn't match {self.schema}" - ) - - new_sequence = [] - for subvalue in value: - if isinstance(subvalue, list): - coerced = Nested(self.schema).coerce(subvalue) - else: - coerced = self.schema.schema.coerce(subvalue) - new_sequence.append(coerced) - return new_sequence - - @coerce.register(dict) - def _(self, value): - if not isinstance(self.schema, Dict): - raise InvalidError( - f"{value!r} has type dict which doesn't match {self.schema}" - ) - - new_dict = {} - for k, v in value.items(): - coerced_key = self.schema.key_schema.coerce(k) - if isinstance(v, dict): - coerced_value = Nested(self.schema).coerce(v) - else: - coerced_value = self.schema.value_schema.coerce(v) - new_dict[coerced_key] = coerced_value - return new_dict diff --git a/landscape/lib/tests/test_schema.py b/landscape/lib/tests/test_schema.py index c7fd76862..ea19a2ff9 100644 --- a/landscape/lib/tests/test_schema.py +++ b/landscape/lib/tests/test_schema.py @@ -12,7 +12,6 @@ from landscape.lib.schema import InvalidError from landscape.lib.schema import KeyDict from landscape.lib.schema import List -from landscape.lib.schema import Nested from landscape.lib.schema import Tuple from landscape.lib.schema import Unicode @@ -226,54 +225,3 @@ def test_dict_inner_bad(self): def test_dict_wrong_type(self): self.assertRaises(InvalidError, Dict(Int(), Int()).coerce, 32) - - def test_nested_list_correct_types_only(self): - schema = Nested(List(Any(Int(), Unicode()))) - data = [[1, "foo"], [[5]]] - self.assertEqual(schema.coerce(data), [[1, "foo"], [[5]]]) - - def test_nested_list_bad_types_only(self): - schema = Nested(List(Any(Int(), Unicode()))) - data = [[5.0]] - self.assertRaises(InvalidError, schema.coerce, data) - - def test_nested_list_mixed_correct_and_bad_types_only(self): - schema = Nested(List(Any(Int(), Unicode()))) - data = [[1.0, "foo"], [[5]]] - self.assertRaises(InvalidError, schema.coerce, data) - - def test_nested_list_with_dictionary(self): - schema = Nested(List(Any(Int(), Unicode()))) - data = {"foo": "bar"} - self.assertRaises(InvalidError, schema.coerce, data) - - def test_nested_dict_correct_types_only(self): - schema = Nested(Dict(Unicode(), Any(Int(), Unicode()))) - data = {"foo": 5, "bar": {"baz": "default"}} - self.assertEqual( - schema.coerce(data), {"foo": 5, "bar": {"baz": "default"}} - ) - - def test_nested_dict_bad_types_only(self): - schema = Nested(Dict(Unicode(), Any(Int(), Unicode()))) - data = {2: 5.0} - self.assertRaises(InvalidError, schema.coerce, data) - - def test_nested_dict_mixed_correct_and_bad_types_only(self): - schema = Nested(Dict(Unicode(), Any(Int(), Unicode()))) - data = {"bar": {"baz": "default"}, 2: 5.0} - self.assertRaises(InvalidError, schema.coerce, data) - - def test_nested_dict_with_list(self): - schema = Nested(Dict(Unicode(), Any(Int(), Unicode()))) - data = [] - self.assertRaises(InvalidError, schema.coerce, data) - - def test_nested_with_type_that_doesnt_support_nesting(self): - with self.assertRaises(InvalidError): - Nested(Int()) - - def test_nested_coerce_unsupported_type(self): - schema = Nested(List(Any(Int(), Unicode()))) - data = 4 - self.assertRaises(InvalidError, schema.coerce, data) diff --git a/landscape/message_schemas/server_bound.py b/landscape/message_schemas/server_bound.py index 314702300..89169945c 100644 --- a/landscape/message_schemas/server_bound.py +++ b/landscape/message_schemas/server_bound.py @@ -9,7 +9,6 @@ from landscape.lib.schema import Int from landscape.lib.schema import KeyDict from landscape.lib.schema import List -from landscape.lib.schema import Nested from landscape.lib.schema import Tuple from landscape.lib.schema import Unicode @@ -759,28 +758,6 @@ {"ubuntu-pro-reboot-required": Unicode()}, ) -snap_config_vals = Nested( - Dict( - Unicode(), - Any( - Unicode(), - Int(), - Bool(), - Float(), - Nested( - List( - Any( - Unicode(), - Int(), - Bool(), - Float(), - ), - ) - ), - ), - ), -) - SNAPS = Message( "snaps", { @@ -804,7 +781,7 @@ ), "confinement": Unicode(), "summary": Unicode(), - "config": snap_config_vals, + "config": Unicode(), }, strict=False, optional=[