Skip to content

Commit f264330

Browse files
authored
Merge pull request #451 from SimonRichardson/handle-strict-mode
#451 Strict mode is a new feature in Juju 2.9. The mode allows more things to fail early if the mode is switched to strict. If no mode is detected then it will fallback to a more passive mode, where it will fallback to other solutions and try to carry on. The purpose of strict mode is to enable people developing with Juju/pylibjuju is highlight errors more early on and to prevent less magic in Juju. The PR for implementing strict mode is simple, EXCEPT when I turned it one, it found the first error! ModelInfo delta was wrongly implemented and wasn't gaining all the history correctly from the deltas over time. This requires[1] juju/juju#12183 to land before this PR is really useful. To enable it at bootstrap time: ```sh juju bootstrap lxd test --config mode=strict ``` 1. juju/juju#12183
2 parents 7dde9a3 + b8cf773 commit f264330

2 files changed

Lines changed: 46 additions & 10 deletions

File tree

juju/delta.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ def get_entity_class(self):
4646

4747

4848
class ModelDelta(EntityDelta):
49+
def get_id(self):
50+
return self.data['model-uuid']
51+
4952
@classmethod
5053
def get_entity_class(self):
5154
from .model import ModelInfo

juju/model.py

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ def entity_type(self):
301301
'application' or 'unit', etc.
302302
303303
"""
304+
# Allow the overriding of entity names from the type instead of from
305+
# the class name. Useful because Model and ModelInfo clash and we really
306+
# want ModelInfo to be called Model.
307+
if hasattr(self.__class__, "type_name_override") and callable(self.__class__.type_name_override):
308+
return self.__class__.type_name_override()
309+
304310
def first_lower(s):
305311
if len(s) == 0:
306312
return s
@@ -449,6 +455,7 @@ def __init__(
449455
self._observers = weakref.WeakValueDictionary()
450456
self.state = ModelState(self)
451457
self._info = None
458+
self._mode = None
452459
self._watch_stopping = asyncio.Event(loop=self._connector.loop)
453460
self._watch_stopped = asyncio.Event(loop=self._connector.loop)
454461
self._watch_received = asyncio.Event(loop=self._connector.loop)
@@ -797,6 +804,10 @@ def info(self):
797804
"""
798805
return self._info
799806

807+
@property
808+
def strict_mode(self):
809+
return self._mode is not None and "strict" in self._mode
810+
800811
def add_observer(
801812
self, callable_, entity_type=None, action=None, entity_id=None,
802813
predicate=None):
@@ -843,7 +854,21 @@ def _watch(self):
843854
See :meth:`add_observer` to register an onchange callback.
844855
845856
"""
857+
def _post_step(obj):
858+
# Once we get the model, ensure we're running in the correct state
859+
# as a post step.
860+
if isinstance(obj, ModelInfo) and obj.safe_data is not None:
861+
model_config = obj.safe_data["config"]
862+
if "mode" in model_config:
863+
self._mode = model_config["mode"]
864+
846865
async def _all_watcher():
866+
# First attempt to get the model config so we know what mode the
867+
# library should be running in.
868+
model_config = await self.get_config()
869+
if "mode" in model_config:
870+
self._mode = model_config["mode"]["value"]
871+
847872
try:
848873
allwatcher = client.AllWatcherFacade.from_connection(
849874
self.connection())
@@ -892,17 +917,20 @@ async def _all_watcher():
892917
pass # can't stop on a closed conn
893918
break
894919
for delta in results.deltas:
920+
entity = None
895921
try:
896-
delta = get_entity_delta(delta)
897-
old_obj, new_obj = self.state.apply_delta(delta)
898-
await self._notify_observers(delta, old_obj, new_obj)
899-
except KeyError as e:
900-
# TODO (stickupkid): we should raise the unknown delta
901-
# type, so we handle correctly all the types comming from
902-
# the all watcher. Currently they're ignored, causing
903-
# issue.
904-
# raise JujuError("unknown delta type {}".format(e.args))
905-
log.warning("unknown delta type: %s", e.args[0])
922+
entity = get_entity_delta(delta)
923+
except KeyError:
924+
if self.strict_mode:
925+
raise JujuError("unknown delta type '{}'".format(delta.entity))
926+
927+
if not self.strict_mode and entity is None:
928+
continue
929+
old_obj, new_obj = self.state.apply_delta(entity)
930+
await self._notify_observers(entity, old_obj, new_obj)
931+
# Post step ensure that we can handle any settings
932+
# that need to be correctly set as a post step.
933+
_post_step(new_obj)
906934
self._watch_received.set()
907935
except CancelledError:
908936
pass
@@ -2270,6 +2298,11 @@ def _ignore(self, path):
22702298

22712299

22722300
class ModelInfo(ModelEntity):
2301+
22732302
@property
22742303
def tag(self):
22752304
return tag.model(self.uuid)
2305+
2306+
@staticmethod
2307+
def type_name_override():
2308+
return "model"

0 commit comments

Comments
 (0)