Merge ~ballot/content-cache-charm/+git/content-cache-charm:site_unique into content-cache-charm:master

Proposed by Benjamin Allot
Status: Work in progress
Proposed branch: ~ballot/content-cache-charm/+git/content-cache-charm:site_unique
Merge into: content-cache-charm:master
Diff against target: 84 lines (+48/-3)
2 files modified
lib/utils.py (+38/-0)
reactive/content_cache.py (+10/-3)
Reviewer Review Type Date Requested Status
Content Cache Charmers Pending
Review via email: mp+383828@code.launchpad.net
To post a comment you must log in.
dcdc954... by Benjamin Allot

WIP

Unmerged commits

dcdc954... by Benjamin Allot

WIP

7971dd9... by Benjamin Allot

Duplicate keys of the same node level will now return an error.

This will block the charm.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/utils.py b/lib/utils.py
index f06effc..c8ef458 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -6,6 +6,14 @@ import re
6import shutil6import shutil
7import subprocess7import subprocess
88
9from yaml.constructor import ConstructorError
10
11try:
12 from yaml import CLoader as Loader
13except ImportError:
14 from yaml import Loader
15
16
9BASE_CACHE_PORT = 608017BASE_CACHE_PORT = 6080
10BASE_BACKEND_PORT = 808018BASE_BACKEND_PORT = 8080
11BACKEND_PORT_LIMIT = 61000 # sysctl net.ipv4.ip_local_port_range19BACKEND_PORT_LIMIT = 61000 # sysctl net.ipv4.ip_local_port_range
@@ -218,3 +226,33 @@ def package_version(package):
218 if not version:226 if not version:
219 return None227 return None
220 return version228 return version
229
230
231class SafeDuplicateKeysLoader(yaml.SafeLoader):
232 """A YAML Loader refusing to load duplicate keys."""
233
234 def construct_mapping(self, node: yaml.nodes.MappingNode, deep=False: bool) -> dict:
235 """Construct the dict without duplicate keys.
236
237 This is a redefinition of :py:function:`yaml.constructor.BaseConstructor.construct_mapping`.
238 It throws a :py:class:`yaml.constructor.ConstructorError` exception if duplicate keys are present at any level.
239
240 :param node: The yaml representation to load.
241 :param deep: Do a deep copy of the yaml node.
242 :returns: The dictionary representing the yaml
243
244
245
246def no_duplicates_constructor(loader, node, deep=False):
247 """Check for duplicate keys."""
248
249 mapping = {}
250 for key_node, value_node in node.value:
251 key = loader.construct_object(key_node, deep=deep)
252 value = loader.construct_object(value_node, deep=deep)
253 if key in mapping:
254 raise ConstructorError("while constructing a mapping", node.start_mark,
255 "found duplicate key (%s)" % key, key_node.start_mark)
256 mapping[key] = value
257
258 return loader.construct_mapping(node, deep)
diff --git a/reactive/content_cache.py b/reactive/content_cache.py
index d396779..b80fb60 100644
--- a/reactive/content_cache.py
+++ b/reactive/content_cache.py
@@ -22,6 +22,8 @@ from lib import haproxy as HAProxy
2222
23SYSCTL_CONF_PATH = '/etc/sysctl.d/90-content-cache.conf'23SYSCTL_CONF_PATH = '/etc/sysctl.d/90-content-cache.conf'
2424
25yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, utils.no_duplicates_constructor)
26
2527
26@reactive.hook('upgrade-charm')28@reactive.hook('upgrade-charm')
27def upgrade_charm():29def upgrade_charm():
@@ -580,9 +582,14 @@ def ports_map_lookup(ports_map, site, base_port, blacklist_ports=None, key=None)
580582
581583
582def sites_from_config(sites_yaml, sites_secrets=None, blacklist_ports=None):584def sites_from_config(sites_yaml, sites_secrets=None, blacklist_ports=None):
583 conf = yaml.safe_load(sites_yaml)585 try:
584 # 'configs' is special and used to host YAML anchors so let's remove it586 conf = yaml.safe_load(sites_yaml)
585 conf.pop('configs', '')587 # 'configs' is special and used to host YAML anchors so let's remove it
588 conf.pop('configs', '')
589 except yaml.constructor.ConstructorError as exc:
590 hookenv.log(str(exc))
591 return False
592
586 sites = interpolate_secrets(conf, sites_secrets)593 sites = interpolate_secrets(conf, sites_secrets)
587 cache_port = 0594 cache_port = 0
588 backend_port = 0595 backend_port = 0

Subscribers

People subscribed via source and target branches