@@ -27,9 +27,9 @@ class CheckStatus:
2727 units : set [str ]
2828 """All units visible at this point."""
2929 ready_units : set [str ]
30- """Units in good status ( workload, agent, machine?) ."""
30+ """Units with the expected workload status ."""
3131 idle_units : set [str ]
32- """Units with stable agent status (FIXME details) ."""
32+ """Units with stable (idle) agent status."""
3333
3434
3535class Loop :
@@ -56,10 +56,6 @@ def next(self, status: CheckStatus | None) -> bool:
5656
5757 expected_idle_since = now - self .idle_period
5858
59- # FIXME there's some confusion about what a "busy" unit is
60- # are we ready when over the last idle_period, every time sampled:
61- # a. >=N units were ready (possibly different each time), or
62- # b. >=N units were ready each time
6359 for name in status .units :
6460 if name in status .idle_units :
6561 self .idle_since [name ] = min (
@@ -114,40 +110,66 @@ def check(
114110 logger .info ("Waiting for app %r" , app_name )
115111 return None
116112
117- # Order of errors:
118- #
119- # Machine error (any unit of any app from apps)
120- # Agent error (-"-)
121- # Workload error (-"-)
122- # App error (any app from apps)
123- #
124- # Workload blocked (any unit of any app from apps)
125- # App blocked (any app from apps)
126113 units : dict [str , UnitStatus ] = {}
114+ rv = CheckStatus (set (), set (), set ())
115+
116+ for app_name in apps :
117+ units .update (app_units (full_status , app_name ))
118+
119+ if raise_on_error :
120+ check_errors (full_status , apps , units )
121+
122+ if raise_on_blocked :
123+ check_blocked (full_status , apps , units )
127124
128125 for app_name in apps :
129- units .update (_app_units (full_status , app_name ))
126+ app = full_status .applications [app_name ]
127+ assert isinstance (app , ApplicationStatus )
128+
129+ for unit_name , unit in app_units (full_status , app_name ).items ():
130+ rv .units .add (unit_name )
131+ assert unit .agent_status
132+ assert unit .workload_status
133+
134+ if unit .agent_status .status == "idle" :
135+ rv .idle_units .add (unit_name )
136+
137+ if not status or unit .workload_status .status == status :
138+ rv .ready_units .add (unit_name )
139+
140+ return rv
141+
130142
143+ def check_errors (
144+ full_status : FullStatus , apps : AbstractSet [str ], units : dict [str , UnitStatus ]
145+ ) -> None :
146+ """Check the full status for error conditions, in this order:
147+
148+ - Machine error (any unit of any app from apps)
149+ - Agent error (-"-)
150+ - Workload error (-"-)
151+ - App error (any app from apps)
152+ """
131153 for unit_name , unit in units .items ():
132154 if unit .machine :
133155 machine = full_status .machines [unit .machine ]
134156 assert isinstance (machine , MachineStatus )
135157 assert machine .instance_status
136- if machine .instance_status .status == "error" and raise_on_error :
158+ if machine .instance_status .status == "error" :
137159 raise JujuMachineError (
138160 f"{ unit_name !r} machine { unit .machine !r} has errored: { machine .instance_status .info !r} "
139161 )
140162
141163 for unit_name , unit in units .items ():
142164 assert unit .agent_status
143- if unit .agent_status .status == "error" and raise_on_error :
165+ if unit .agent_status .status == "error" :
144166 raise JujuAgentError (
145167 f"{ unit_name !r} agent has errored: { unit .agent_status .info !r} "
146168 )
147169
148170 for unit_name , unit in units .items ():
149171 assert unit .workload_status
150- if unit .workload_status .status == "error" and raise_on_error :
172+ if unit .workload_status .status == "error" :
151173 raise JujuUnitError (
152174 f"{ unit_name !r} workload has errored: { unit .workload_status .info !r} "
153175 )
@@ -156,12 +178,21 @@ def check(
156178 app = full_status .applications [app_name ]
157179 assert isinstance (app , ApplicationStatus )
158180 assert app .status
159- if app .status .status == "error" and raise_on_error :
181+ if app .status .status == "error" :
160182 raise JujuAppError (f"{ app_name !r} has errored: { app .status .info !r} " )
161183
184+
185+ def check_blocked (
186+ full_status : FullStatus , apps : AbstractSet [str ], units : dict [str , UnitStatus ]
187+ ) -> None :
188+ """Check the full status for blocked conditions, in this order:
189+
190+ - Workload blocked (any unit of any app from apps)
191+ - App blocked (any app from apps)
192+ """
162193 for unit_name , unit in units .items ():
163194 assert unit .workload_status
164- if unit .workload_status .status == "blocked" and raise_on_blocked :
195+ if unit .workload_status .status == "blocked" :
165196 raise JujuUnitError (
166197 f"{ unit_name !r} workload is blocked: { unit .workload_status .info !r} "
167198 )
@@ -170,29 +201,11 @@ def check(
170201 app = full_status .applications [app_name ]
171202 assert isinstance (app , ApplicationStatus )
172203 assert app .status
173- if app .status .status == "blocked" and raise_on_blocked :
204+ if app .status .status == "blocked" :
174205 raise JujuAppError (f"{ app_name !r} is blocked: { app .status .info !r} " )
175206
176- rv = CheckStatus (set (), set (), set ())
177-
178- for app_name in apps :
179- app = full_status .applications [app_name ]
180- assert isinstance (app , ApplicationStatus )
181- for unit_name , unit in _app_units (full_status , app_name ).items ():
182- rv .units .add (unit_name )
183- assert unit .agent_status
184- assert unit .workload_status
185-
186- if unit .agent_status .status == "idle" :
187- rv .idle_units .add (unit_name )
188-
189- if not status or unit .workload_status .status == status :
190- rv .ready_units .add (unit_name )
191-
192- return rv
193-
194207
195- def _app_units (full_status : FullStatus , app_name : str ) -> dict [str , UnitStatus ]:
208+ def app_units (full_status : FullStatus , app_name : str ) -> dict [str , UnitStatus ]:
196209 """Fish out the app's units' status from a FullStatus response."""
197210 rv : dict [str , UnitStatus ] = {}
198211 app = full_status .applications [app_name ]
0 commit comments